Converting Hex String to NSData in Swift

后端 未结 11 1104
一生所求
一生所求 2020-11-27 05:22

I got the code to convert String to HEX-String in objective-C.

- (NSString *) CreateDataWithHexString:(NSString*)inputString
{
NSUInteger inLength = [inputSt         


        
相关标签:
11条回答
  • 2020-11-27 05:40

    convert hex string to data and string:

    Swift1

    func dataWithHexString(hex: String) -> NSData {
        var hex = hex
        let data = NSMutableData()
        while(countElements(hex) > 0) {
            var c: String = hex.substringToIndex(advance(hex.startIndex, 2))
            hex = hex.substringFromIndex(advance(hex.startIndex, 2))
            var ch: UInt32 = 0
            NSScanner(string: c).scanHexInt(&ch)
            data.appendBytes(&ch, length: 1)
        }
        return data
    }
    

    use:

    let data = dataWithHexString("68656c6c6f2c20776f726c64") // <68656c6c 6f2c2077 6f726c64>
    if let string = NSString(data: data, encoding: 1) {
        print(string) // hello, world
    }
    

    Swift2

    func dataWithHexString(hex: String) -> NSData {
        var hex = hex
        let data = NSMutableData()
        while(hex.characters.count > 0) {
            let c: String = hex.substringToIndex(hex.startIndex.advancedBy(2))
            hex = hex.substringFromIndex(hex.startIndex.advancedBy(2))
            var ch: UInt32 = 0
            NSScanner(string: c).scanHexInt(&ch)
            data.appendBytes(&ch, length: 1)
        }
        return data
    }
    

    use:

    let data = dataWithHexString("68656c6c6f2c20776f726c64") // <68656c6c 6f2c2077 6f726c64>
    if let string = String(data: data, encoding: NSUTF8StringEncoding) {
        print(string) //"hello, world"
    }
    

    Swift3

    func dataWithHexString(hex: String) -> Data {
        var hex = hex
        var data = Data()
        while(hex.characters.count > 0) {
            let c: String = hex.substring(to: hex.index(hex.startIndex, offsetBy: 2))
            hex = hex.substring(from: hex.index(hex.startIndex, offsetBy: 2))
            var ch: UInt32 = 0
            Scanner(string: c).scanHexInt32(&ch)
            var char = UInt8(ch)
            data.append(&char, count: 1)
        }
        return data
    }
    

    use:

    let data = dataWithHexString(hex: "68656c6c6f2c20776f726c64") // <68656c6c 6f2c2077 6f726c64>
    let string = String(data: data, encoding: .utf8) // "hello, world"
    

    Swift4

    func dataWithHexString(hex: String) -> Data {
        var hex = hex
        var data = Data()
        while(hex.count > 0) {
            let subIndex = hex.index(hex.startIndex, offsetBy: 2)
            let c = String(hex[..<subIndex])
            hex = String(hex[subIndex...])
            var ch: UInt32 = 0
            Scanner(string: c).scanHexInt32(&ch)
            var char = UInt8(ch)
            data.append(&char, count: 1)
        }
        return data
    }
    

    use:

    let data = dataWithHexString(hex: "68656c6c6f2c20776f726c64") // <68656c6c 6f2c2077 6f726c64>
    let string = String(data: data, encoding: .utf8) // "hello, world"
    
    0 讨论(0)
  • 2020-11-27 05:42

    This is my hex string to Data routine:

    extension String {
    
        /// Create `Data` from hexadecimal string representation
        ///
        /// This creates a `Data` object from hex string. 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.
    
        var 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, range: NSRange(startIndex..., in: self)) { match, _, _ in
                let byteString = (self as NSString).substring(with: match!.range)
                let num = UInt8(byteString, radix: 16)!
                data.append(num)
            }
    
            guard data.count > 0 else { return nil }
    
            return data
        }
    
    }
    

    And for the sake of completeness, this is my Data to hex string routine:

    extension Data {
    
        /// Hexadecimal string representation of `Data` object.
    
        var hexadecimal: String {
            return map { String(format: "%02x", $0) }
                .joined()
        }
    }
    

    Note, as shown in the above, I generally only convert between hexadecimal representations and NSData instances (because if the information could have been represented as a string you probably wouldn't have created a hexadecimal representation in the first place). But your original question wanted to convert between hexadecimal representations and String objects, and that might look like so:

    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.
        ///
        /// For example,
        ///
        ///     String(hexadecimal: "<666f6f>")
        ///
        /// is
        ///
        ///     Optional("foo")
        ///
        /// - returns: `String` represented by this hexadecimal string.
    
        init?(hexadecimal string: String, encoding: String.Encoding = .utf8) {
            guard let data = string.hexadecimal() else {
                return nil
            }
    
            self.init(data: data, encoding: encoding)
        }
    
        /// Create hexadecimal string representation of `String` object.
        ///
        /// For example,
        ///
        ///     "foo".hexadecimalString()
        ///
        /// is
        ///
        ///     Optional("666f6f")
        ///
        /// - parameter encoding: The `String.Encoding` that indicates how the string should be converted to `Data` before performing the hexadecimal conversion.
        ///
        /// - returns: `String` representation of this String object.
    
        func hexadecimalString(encoding: String.Encoding = .utf8) -> String? {
            return data(using: encoding)?
                .hexadecimal
        }
    
    }
    

    You could then use the above like so:

    let hexString = "68656c6c 6f2c2077 6f726c64"
    print(String(hexadecimal: hexString))
    

    Or,

    let originalString = "hello, world"
    print(originalString.hexadecimalString())
    

    For permutations of the above for earlier Swift versions, see the revision history of this question.

    0 讨论(0)
  • 2020-11-27 05:44

    Supposing your string is even size, you can use this to convert to hexadecimal and save it to Data:

    Swift 5.2

    func hex(from string: String) -> Data {
        .init(stride(from: 0, to: string.count, by: 2).map {
            string[string.index(string.startIndex, offsetBy: $0) ... string.index(string.startIndex, offsetBy: $0 + 1)]
        }.map {
            UInt8($0, radix: 16)!
        })
    }
    
    0 讨论(0)
  • 2020-11-27 05:49

    Here is my Swifty 5 way to do it:

    • does take care of "0x" prefixes
    • use subscript instead of allocated Array(), no C style [i+1] too
    • add .hexadecimal to String.data(using encoding:) -> Data?

    .

    String Extension:

        extension String {
            enum ExtendedEncoding {
                case hexadecimal
            }
    
            func data(using encoding:ExtendedEncoding) -> Data? {
                let hexStr = self.dropFirst(self.hasPrefix("0x") ? 2 : 0)
    
                guard hexStr.count % 2 == 0 else { return nil }
    
                var newData = Data(capacity: hexStr.count/2)
    
                var indexIsEven = true
                for i in hexStr.indices {
                    if indexIsEven {
                        let byteRange = i...hexStr.index(after: i)
                        guard let byte = UInt8(hexStr[byteRange], radix: 16) else { return nil }
                        newData.append(byte)
                    }
                    indexIsEven.toggle()
                }
                return newData
            }
        }
    

    Usage:

        "5413".data(using: .hexadecimal)
        "0x1234FF".data(using: .hexadecimal)
    

    Tests:

        extension Data {
            var bytes:[UInt8] { // fancy pretty call: myData.bytes -> [UInt8]
                return [UInt8](self)
            }
    
            // Could make a more optimized one~
            func hexa(prefixed isPrefixed:Bool = true) -> String {
                return self.bytes.reduce(isPrefixed ? "0x" : "") { $0 + String(format: "%02X", $1) }
            }
        }
    
        print("000204ff5400".data(using: .hexadecimal)?.hexa() ?? "failed") // OK
        print("0x000204ff5400".data(using: .hexadecimal)?.hexa() ?? "failed") // OK
        print("541".data(using: .hexadecimal)?.hexa() ?? "failed") // fails
        print("5413".data(using: .hexadecimal)?.hexa() ?? "failed") // OK
    
    0 讨论(0)
  • 2020-11-27 05:50

    One more solution that is simple to follow and leverages swifts built-in hex parsing

    func convertHexToBytes(_ str: String) -> Data? {
        let values = str.compactMap { $0.hexDigitValue } // map char to value of 0-15 or nil
        if values.count == str.count && values.count % 2 == 0 {
            var data = Data()
            for x in stride(from: 0, to: values.count, by: 2) {
              let byte = (values[x] << 4) + values[x+1] // concat high and low bits
              data.append(UInt8(byte))
            }
            return data
        }
        return nil
    }
    
    let good = "e01AFd"
    let bad = "e0671"
    let ugly = "GT40"
    print("\(convertHexToBytes(good))") // Optional(3 bytes)
    print("\(convertHexToBytes(bad))") // nil
    print("\(convertHexToBytes(ugly))") // nil
    
    0 讨论(0)
  • 2020-11-27 05:53

    The code worked for me in Swift 3.0.2.

    extension String {
        /// Expanded encoding
        ///
        /// - bytesHexLiteral: Hex string of bytes
        /// - base64: Base64 string
        enum ExpandedEncoding {
            /// Hex string of bytes
            case bytesHexLiteral
            /// Base64 string
            case base64
        }
    
        /// Convert to `Data` with expanded encoding
        ///
        /// - Parameter encoding: Expanded encoding
        /// - Returns: data
        func data(using encoding: ExpandedEncoding) -> Data? {
            switch encoding {
            case .bytesHexLiteral:
                guard self.characters.count % 2 == 0 else { return nil }
                var data = Data()
                var byteLiteral = ""
                for (index, character) in self.characters.enumerated() {
                    if index % 2 == 0 {
                        byteLiteral = String(character)
                    } else {
                        byteLiteral.append(character)
                        guard let byte = UInt8(byteLiteral, radix: 16) else { return nil }
                        data.append(byte)
                    }
                }
                return data
            case .base64:
                return Data(base64Encoded: self)
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题