How do i convert HexString To ByteArray in Swift 3

10,719

Solution 1

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")

Solution 2

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())

Solution 3

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)! }
}

Solution 4

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

Solution 5

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
}
Share:
10,719
O-mkar
Author by

O-mkar

Updated on June 09, 2022

Comments

  • O-mkar
    O-mkar about 2 years

    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"

  • OOPer
    OOPer over 7 years
    Your code does not return the expectedOutput in your question. What actually is the expected output for the input "2dafdb7cd8b898a1affde546b2a1b440"?
  • OOPer
    OOPer over 7 years
    I have added some sample output to my answer.
  • Sam King
    Sam King about 6 years
    Although this may work, it's pretty verbose. In general, when I have a while i < something.count loop I try to find a better way to do it when using Swift.