What's the best way to cut Swift string into 2-letter-strings? - swift

I need to split a string into 2-letter pieces. Like “friend" -> "fr" "ie" "nd". (Okay, its a step for me to change HEX string to Uint8 Array)
My code is
for i=0; i<chars.count/2; i++ {
let str = input[input.startIndex.advancedBy(i*2)..<input.startIndex.advancedBy(i*2+1)]
bytes.append(UInt8(str,radix: 16)!)
}
But I don't know why I cannot use Range to do this split. And I have no idea what will happen when i*2+1 is bigger than string's length. So what's the best way to cut Swift string into 2-letter-strings?

Your range wasn't working because you need to use ... instead of ..<.
let input = "ff103"
var bytes = [UInt8]()
let strlen = input.characters.count
for i in 0 ..< (strlen + 1)/2 {
let str = input[input.startIndex.advancedBy(i*2)...input.startIndex.advancedBy(min(strlen - 1, i*2+1))]
bytes.append(UInt8(str,radix: 16) ?? 0)
}
print(bytes) // [255, 16, 3]
Here is another take on splitting the string into 2-letter strings. advancedBy() is an expensive O(n) operation, so this version keeps track of start and just marches it ahead by 2 each loop, and end is based on start:
let input = "friends"
var strings = [String]()
let strlen = input.characters.count
var start = input.startIndex
let lastIndex = strlen > 0 ? input.endIndex.predecessor() : input.startIndex
for i in 0 ..< (strlen + 1)/2 {
start = i > 0 ? start.advancedBy(2) : start
let end = start < lastIndex ? start.successor() : start
let str = input[start...end]
strings.append(str)
}
print(strings) // ["fr", "ie", "nd", "s"]
Alternate Answer:
Using ranges is probably overkill. It is easy just to add the characters to an array and make Strings from those:
let input = "friends"
var strings = [String]()
var newchars = [Character]()
for c in input.characters {
newchars.append(c)
if newchars.count == 2 {
strings.append(String(newchars))
newchars = []
}
}
if newchars.count > 0 {
strings.append(String(newchars))
}
print(strings) // ["fr", "ie", "nd", "s"]
And here is the new version for making [UInt8]:
let input = "ff103"
var bytes = [UInt8]()
var newchars = [Character]()
for c in input.characters {
newchars.append(c)
if newchars.count == 2 {
bytes.append(UInt8(String(newchars), radix: 16) ?? 0)
newchars = []
}
}
if newchars.count > 0 {
bytes.append(UInt8(String(newchars), radix: 16) ?? 0)
}
print(bytes) // [255, 16, 3]
Based on #LeoDabus' answer, we can make an extension with a method that will return substrings of any length, and a computed property that returns [UInt8]:
extension String {
func substringsOfLength(length: Int) -> [String] {
if length < 1 { return [] }
var result:[String] = []
let chars = Array(characters)
for index in 0.stride(to: chars.count, by: length) {
result.append(String(chars[index ..< min(index+length, chars.count)]))
}
return result
}
var toUInt8: [UInt8] {
var result:[UInt8] = []
let chars = Array(characters)
for index in 0.stride(to: chars.count, by: 2) {
let str = String(chars[index ..< min(index+2, chars.count)])
result.append(UInt8(str, radix: 16) ?? 0)
}
return result
}
}
let input = "friends"
let str2 = input.substringsOfLength(2) // ["fr", "ie", "nd", "s"]
let str0 = input.substringsOfLength(0) // []
let str3 = input.substringsOfLength(3) // ["fri", "end", "s"]
let bytes = "ff107".toUInt8 // [255, 16, 7]

Another option just for fun:
extension String {
var pairs:[String] {
var result:[String] = []
let chars = Array(characters)
for index in 0.stride(to: chars.count, by: 2) {
result.append(String(chars[index..<min(index+2, chars.count)]))
}
return result
}
}
let input = "friends"
let pairs = input.pairs
print(pairs) // ["fr", "ie", "nd", "s"]

Related

string replace substring without NSString API

I would like to be able to find and replace occurrences of a substring in a native Swift string without bridging to the NS class. How can I accomplish this?
This is not a duplicate of this question, as that question is about replacing a single character. This question is about finding and replacing a substring, which may contain many characters.
Method without Foundation:
extension String {
func replacing(_ oldString: String, with newString: String) -> String {
guard !oldString.isEmpty, !newString.isEmpty else { return self }
let charArray = Array(self.characters)
let oldCharArray = Array(oldString.characters)
let newCharArray = Array(newString.characters)
var matchedChars = 0
var resultCharArray = [Character]()
for char in charArray {
if char == oldCharArray[matchedChars] {
matchedChars += 1
if matchedChars == oldCharArray.count {
resultCharArray.append(contentsOf: newCharArray)
matchedChars = 0
}
} else {
for i in 0 ..< matchedChars {
resultCharArray.append(oldCharArray[i])
}
if char == oldCharArray[0] {
matchedChars = 1
} else {
matchedChars = 0
resultCharArray.append(char)
}
}
}
return String(resultCharArray)
}
}
Example usage:
let myString = "Hello World HelHelloello Hello HellHellooo"
print(myString.replacing("Hello", with: "Hi"))
Output:
Hi World HelHiello Hi HellHioo
Method using Foundation:
You can use the replacingOccurrences method on the String struct.
let myString = "Hello World"
let newString = myString.replacingOccurrences(of: "World", with: "Everyone")
print(newString) // prints "Hello Everyone"
generic and pure Swift approach
func splitBy<T: RangeReplaceableCollection>(_ s:T, by:T)->[T] where T.Iterator.Element:Equatable {
var tmp = T()
var res = [T]()
var i:T.IndexDistance = 0
let count = by.count
var pc:T.Iterator.Element {
get {
i %= count
let idx = by.index(by.startIndex, offsetBy: i)
return by[idx]
}
}
for sc in s {
if sc != pc {
i = 0
if sc != pc {
} else {
i = i.advanced(by: 1)
}
} else {
i = i.advanced(by: 1)
}
tmp.append(sc)
if i == count {
tmp.removeSubrange(tmp.index(tmp.endIndex, offsetBy: -i)..<tmp.endIndex)
res.append(tmp)
tmp.removeAll()
}
}
res.append(tmp)
return res
}
func split(_ s:String, by:String)->[String] {
return splitBy(s.characters, by: by.characters).map(String.init)
}
extension RangeReplaceableCollection where Self.Iterator.Element: Equatable {
func split(by : Self)->[Self] {
return splitBy(self, by: by)
}
}
how to use it?
let str = "simple text where i would like to replace something with anything"
let pat = "something"
let rep = "anything"
let s0 = str.characters.split(by: pat.characters).map(String.init)
let res = s0.joined(separator: rep)
print(res) // simple text where i would like to replace anything with anything
let res2 = split(str, by: pat).joined(separator: rep)
print(res2) // simple text where i would like to replace anything with anything
let arr = [1,2,3,4,1,2,3,4,1,2,3]
let p = [4,1]
print(arr.split(by: p)) // [[1, 2, 3], [2, 3], [2, 3]]

How do i convert HexString To ByteArray in Swift 3

I'm was trying to convert hexString to Array of Bytes([UInt8]) I searched everywhere but couldn't find a solution. Below is my swift 2 code
func stringToBytes(_ string: String) -> [UInt8]? {
let chars = Array(string)
let length = chars.count
if length & 1 != 0 {
return nil
}
var bytes = [UInt8]()
bytes.reserveCapacity(length/2)
for var i = 0; i < length; i += 2 {
if let a = find(hexChars, chars[i]),
let b = find(hexChars, chars[i+1]) {
bytes.append(UInt8(a << 4) + UInt8(b))
} else {
return nil
}
}
return bytes
}
Example Hex
Hex : "7661706f72"
expectedOutput : "vapor"
This code can generate the same output as your swift 2 code.
func stringToBytes(_ string: String) -> [UInt8]? {
let length = string.characters.count
if length & 1 != 0 {
return nil
}
var bytes = [UInt8]()
bytes.reserveCapacity(length/2)
var index = string.startIndex
for _ in 0..<length/2 {
let nextIndex = string.index(index, offsetBy: 2)
if let b = UInt8(string[index..<nextIndex], radix: 16) {
bytes.append(b)
} else {
return nil
}
index = nextIndex
}
return bytes
}
let bytes = stringToBytes("7661706f72")
print(String(bytes: bytes!, encoding: .utf8)) //->Optional("vapor")
Here is a sketch of how I'd do it in a more idiomatic Swift style (this might be Swift 4 only):
func toPairsOfChars(pairs: [String], string: String) -> [String] {
if string.count == 0 {
return pairs
}
var pairsMod = pairs
pairsMod.append(String(string.prefix(2)))
return toPairsOfChars(pairs: pairsMod, string: String(string.dropFirst(2)))
}
func stringToBytes(_ string: String) -> [UInt8]? {
// omit error checking: remove '0x', make sure even, valid chars
let pairs = toPairsOfChars(pairs: [], string: string)
return pairs.map { UInt8($0, radix: 16)! }
}
Following code may be help for you
extension String {
/// Create `Data` from hexadecimal string representation
///
/// This takes a hexadecimal representation and creates a `Data` object. Note, if the string has any spaces or non-hex characters (e.g. starts with '<' and with a '>'), those are ignored and only hex characters are processed.
///
/// - returns: Data represented by this hexadecimal string.
func hexadecimal() -> Data? {
var data = Data(capacity: characters.count / 2)
let regex = try! NSRegularExpression(pattern: "[0-9a-f]{1,2}", options: .caseInsensitive)
regex.enumerateMatches(in: self, options: [], range: NSMakeRange(0, characters.count)) { match, flags, stop in
let byteString = (self as NSString).substring(with: match!.range)
var num = UInt8(byteString, radix: 16)!
data.append(&num, count: 1)
}
guard data.count > 0 else {
return nil
}
return data
}
}
extension String {
/// Create `String` representation of `Data` created from hexadecimal string representation
///
/// This takes a hexadecimal representation and creates a String object from that. Note, if the string has any spaces, those are removed. Also if the string started with a `<` or ended with a `>`, those are removed, too.
init?(hexadecimal string: String) {
guard let data = string.hexadecimal() else {
return nil
}
self.init(data: data, encoding: .utf8)
}
/// - parameter encoding: The `NSStringCoding` that indicates how the string should be converted to `NSData` before performing the hexadecimal conversion.
/// - returns: `String` representation of this String object.
func hexadecimalString() -> String? {
return data(using: .utf8)?
.hexadecimal()
}
}
extension Data {
/// Create hexadecimal string representation of `Data` object.
/// - returns: `String` representation of this `Data` object.
func hexadecimal() -> String {
return map { String(format: "%02x", $0) }
.joined(separator: "")
}
}
Use like this :
let hexString = "68656c6c 6f2c2077 6f726c64"
print(String(hexadecimalString: hexString))
Or
let originalString = "hello, world"
print(originalString.hexadecimalString())
After lot searching and thinking here is how you do it
func toByteArray( _ hex:String ) -> [UInt8] {
// remove "-" from Hexadecimal
var hexString = hex.removeWord( "-" )
let size = hexString.characters.count / 2
var result:[UInt8] = [UInt8]( repeating: 0, count: size ) // array with length = size
// for ( int i = 0; i < hexString.length; i += 2 )
for i in stride( from: 0, to: hexString.characters.count, by: 2 ) {
let subHexStr = hexString.subString( i, length: 2 )
result[ i / 2 ] = UInt8( subHexStr, radix: 16 )! // ! - because could be null
}
return result
}
extension String {
func subString( _ from: Int, length: Int ) -> String {
let size = self.characters.count
let to = length + from
if from < 0 || to > size {
return ""
}
var result = ""
for ( idx, char ) in self.characters.enumerated() {
if idx >= from && idx < to {
result.append( char )
}
}
return result
}
func removeWord( _ word:String ) -> String {
var result = ""
let textCharArr = Array( self.characters )
let wordCharArr = Array( word.characters )
var possibleMatch = ""
var i = 0, j = 0
while i < textCharArr.count {
if textCharArr[ i ] == wordCharArr[ j ] {
if j == wordCharArr.count - 1 {
possibleMatch = ""
j = 0
}
else {
possibleMatch.append( textCharArr[ i ] )
j += 1
}
}
else {
result.append( possibleMatch )
possibleMatch = ""
if j == 0 {
result.append( textCharArr[ i ] )
}
else {
j = 0
i -= 1
}
}
i += 1
}
return result
}
}
Refer this video to know how I did it.
Credit : AllTech
Conversion of String to Data with nicer syntax.
static func hexStringToData(string: String) -> Data {
let stringArray = Array(string)
var data: Data = Data()
for i in stride(from: 0, to: string.count, by: 2) {
let pair: String = String(stringArray[i]) + String(stringArray[i+1])
if let byteNum = UInt8(pair, radix: 16) {
let byte = Data([byteNum])
data.append(byte)
}
else{
fatalError()
}
}
return data
}

Reverse Strings without using predefined functions

Pardon me as I am a newbie on this language.
Edit: Is there a way to reverse the position of a array element?
I am trying to create a function that test the given input if its a palindrome or not. I'm trying to avoid using functions using reversed()
let word = ["T","E","S","T"]
var temp = [String]()
let index_count = 3
for words in word{
var text:String = words
print(text)
temp.insert(text, atIndex:index_count)
index_count = index_count - 1
}
Your approach can be used to reverse an array. But you have to
insert each element of the original array at the start position
of the destination array (moving the other elements to the end):
// Swift 2.2:
let word = ["T", "E", "S", "T"]
var reversed = [String]()
for char in word {
reversed.insert(char, atIndex: 0)
}
print(reversed) // ["T", "S", "E", "T"]
// Swift 3:
let word = ["T", "E", "S", "T"]
var reversed = [String]()
for char in word {
reversed.insert(char, at: 0)
}
print(reversed) // ["T", "S", "E", "T"]
The same can be done on the characters of a string directly:
// Swift 2.2:
let word = "TEST"
var reversed = ""
for char in word.characters {
reversed.insert(char, atIndex: reversed.startIndex)
}
print(reversed) // "TSET"
// Swift 3:
let word = "TEST"
var reversed = ""
for char in word.characters {
reversed.insert(char, at: reversed.startIndex)
}
print(reversed)
Swift 5
extension String {
func invert() -> String {
var word = [Character]()
for char in self {
word.insert(char, at: 0)
}
return String(word)
}
}
var anadrome = "god"
anadrome.invert()
// "dog"
Here's my solution:
extension String {
func customReverse() -> String {
var chars = Array(self)
let count = chars.count
for i in 0 ..< count/2 {
chars.swapAt(i, count - 1 - i)
}
return String(chars)
}
}
let input = "abcdef"
let output = input.customReverse()
print(output)
You can try it here.
func reverse(_ str: String) -> String {
let arr = Array(str) // turn the string into an array of all of the letters
let reversed = ""
for char in arr {
reversed.insert(char, at: reversed.startIndex)
}
return reversed
}
To use it:
let word = "hola"
let wordReversed = reverse(word)
print(wordReversed) // prints aloh
Another solution for reversing:
var original : String = "Test"
var reversed : String = ""
var c = original.characters
for _ in 0..<c.count{
reversed.append(c.popLast()!)
}
It simply appends each element of the old string that is popped, starting at the last element and working towards the first
Solution 1
let word = "aabbaa"
let chars = word.characters
let half = chars.count / 2
let leftSide = Array(chars)[0..<half]
let rightSide = Array(chars.reverse())[0..<half]
let palindrome = leftSide == rightSide
Solution 2
var palindrome = true
let chars = Array(word.characters)
for i in 0 ..< (chars.count / 2) {
if chars[i] != chars[chars.count - 1 - i] {
palindrome = false
break
}
}
print(palindrome)
static func reverseString(str : String) {
var data = Array(str)
var i = 0// initial
var j = data.count // final
//either j or i for while , data.count/2 buz only half we need check
while j != data.count/2 {
print("befor i:\(i) j:\(j)" )
j = j-1
data.swapAt(i, j) // swapAt API avalible only for array in swift
i = i+1
}
print(String(data))
}
//Reverse String
let str = "Hello World"
var reverseStr = ""
for char in Array(str) {
print(char)
reverseStr.insert(char, at: str.startIndex)
}
print(reverseStr)

Split string by two symbols in Swift

I would like to split the string by two symbols in Swift. So after string "df57g5df7g" I would like to obtain an Array ["df","57","g5","df","7g"].
Is it possible to force iterator
for i in word.characters {
print(i)
}
to jump by two symbols, and get acsess to the next symbol inside the loop?
A simple while loop:
let str = "df57g5df7g"
var startIndex = str.startIndex
var result = [String]()
repeat {
let endIndex = startIndex.advancedBy(2, limit: str.endIndex)
result.append(str[startIndex..<endIndex])
startIndex = endIndex
} while startIndex < str.endIndex
print(result)
Or something more Swifty:
let result = 0.stride(to: str.characters.count, by: 2).map { i -> String in
let startIndex = str.startIndex.advancedBy(i)
let endIndex = startIndex.advancedBy(2, limit: str.endIndex)
return str[startIndex..<endIndex]
}
This might not be the slickest solution, but it works:
var word = "df57g5df7g"
var pairsArray = [String]()
while word.characters.count > 1 {
let firstCharacter = word.removeAtIndex(word.startIndex)
let secondCharacter = word.removeAtIndex(word.startIndex)
pairsArray.append("\(firstCharacter)\(secondCharacter)")
}
print(pairsArray)
The result is:
["df", "57", "g5", "df", "7g"]
This is the best solution I've seen, taken from the SwiftSequence library.
extension CollectionType {
public func chunk(n: Index.Distance) -> [SubSequence] {
var res: [SubSequence] = []
var i = startIndex
var j: Index
while i != endIndex {
j = i.advancedBy(n, limit: endIndex)
res.append(self[i..<j])
i = j
}
return res
}
}
let word = "df57g5df7g"
let pairs = word.characters.chunk(2).map(String.init)
print(pairs) //["df", "57", "g5", "df", "7g"]
You can see it in action here.

Given a hexadecimal string in Swift, convert to hex value

Suppose I am given a string like this:
D7C17A4F
How do I convert each individual character to a hex value?
So D should be 0xD, 7 should be 0x7…
Right now, I have each individual character represented as it's ASCII value. D is 68, 7 is 55. I'm trying to pack those two values into one byte. For example: D7 becomes 0xD7 and C1 becomes 0xC1. I can't do that using the ASCII decimal values though.
A possible solution:
let string = "D7C17A4F"
let chars = Array(string)
let numbers = map (stride(from: 0, to: chars.count, by: 2)) {
strtoul(String(chars[$0 ..< $0+2]), nil, 16)
}
Using the approach from https://stackoverflow.com/a/29306523/1187415,
the string is split into substrings of two characters.
Each substring is interpreted as a sequence of digits
in base 16, and converted to a number with strtoul().
Verify the result:
println(numbers)
// [215, 193, 122, 79]
println(map(numbers, { String(format: "%02X", $0) } ))
// [D7, C1, 7A, 4F]
Update for Swift 2 (Xcode 7):
let string = "D7C17A4F"
let chars = Array(string.characters)
let numbers = 0.stride(to: chars.count, by: 2).map {
UInt8(String(chars[$0 ..< $0+2]), radix: 16) ?? 0
}
print(numbers)
or
let string = "D7C17A4F"
var numbers = [UInt8]()
var from = string.startIndex
while from != string.endIndex {
let to = from.advancedBy(2, limit: string.endIndex)
numbers.append(UInt8(string[from ..< to], radix: 16) ?? 0)
from = to
}
print(numbers)
The second solution looks a bit more complicated but has the small
advantage that no additional chars array is needed.
Swift 3 version, modified from #Martin R's answer. This variant also accepts incoming string with odd length.
let string = "D7C17A4F"
let chars = Array(string.characters)
let numbers = stride(from: 0, to: chars.count, by: 2).map() {
strtoul(String(chars[$0 ..< min($0 + 2, chars.count)]), nil, 16)
}
Use chunks!
"D7C17A4F"
.chunks(ofCount: 2)
.map { UInt8($0, radix: 0x10)! }
My variation of #martin-r answer:
extension String {
func hexToByteArray() -> [UInt8] {
let byteCount = self.utf8.count / 2
var array = [UInt8](count: byteCount, repeatedValue: 0)
var from = self.startIndex
for i in 0..<byteCount {
let to = from.successor()
let sub = self.substringWithRange(from...to)
array[i] = UInt8(sub, radix: 16) ?? 0
from = to.successor()
}
return array
}
}
here is the more generic, "pure swift" approach (no Foundation required :-))
extension UnsignedInteger {
var hex: String {
var str = String(self, radix: 16, uppercase: true)
while str.characters.count < 2 * MemoryLayout<Self>.size {
str.insert("0", at: str.startIndex)
}
return str
}
}
extension Array where Element: UnsignedInteger {
var hex: String {
var str = ""
self.forEach { (u) in
str.append(u.hex)
}
return str
}
}
let str = [UInt8(1),22,63,41].hex // "01163F29"
let str2 = [UInt(1),22,63,41].hex // "00000000000000010000000000000016000000000000003F0000000000000029"
extension String {
func toUnsignedInteger<T:UnsignedInteger>()->[T]? {
var ret = [T]()
let nibles = MemoryLayout<T>.size * 2
for i in stride(from: 0, to: characters.count, by: nibles) {
let start = self.index(startIndex, offsetBy: i)
guard let end = self.index(start, offsetBy: nibles, limitedBy: endIndex),
let ui = UIntMax(self[start..<end], radix: 16) else { return nil }
ret.append(T(ui))
}
return ret
}
}
let u0:[UInt8]? = str.toUnsignedInteger() // [1, 22, 63, 41]
let u1 = "F2345f".toUnsignedInteger() as [UInt8]? // [18, 52, 95]
let u2 = "12345f".toUnsignedInteger() as [UInt16]? // nil
let u3 = "12345g".toUnsignedInteger() as [UInt8]? // nil
let u4 = "12345f".toUnsignedInteger() as [UInt]? // nil
let u5 = "12345678".toUnsignedInteger() as [UInt8]? // [18, 52, 86, 120]
let u6 = "12345678".toUnsignedInteger() as [UInt16]? // [4660, 22136]
let u7 = "1234567812345678".toUnsignedInteger() as [UInt]? // [1311768465173141112]
It is very easily to do the same for SignedInteger as well, but better approach will be to map results to signed type
let u8 = u1?.map { Int8(bitPattern: $0) } // [-14, 52, 95]