Hex/Binary String Conversion in Swift

How to convert hex number to bin in Swift?

You can use NSScanner() from the Foundation framework:

let scanner = NSScanner(string: str)
var result : UInt32 = 0
if scanner.scanHexInt(&result) {
println(result) // 37331519
}

Or the BSD library function strtoul()

let num = strtoul(str, nil, 16)
println(num) // 37331519

As of Swift 2 (Xcode 7), all integer types have an

public init?(_ text: String, radix: Int = default)

initializer, so that a pure Swift solution is available:

let str = "239A23F"
let num = Int(str, radix: 16)

Binary to hexadecimal in Swift

A possible solution:

func binToHex(bin : String) -> String {
// binary to integer:
let num = bin.withCString { strtoul($0, nil, 2) }
// integer to hex:
let hex = String(num, radix: 16, uppercase: true) // (or false)
return hex
}

This works as long as the numbers fit into the range of UInt (32-bit or 64-bit,
depending on the platform). It uses the BSD library function strtoul() which converts a string to an integer according to a given base.

For larger numbers you have to process the input
in chunks. You might also add a validation of the input string.

Update for Swift 3/4: The strtoul function is no longer needed.
Return nil for invalid input:

func binToHex(_ bin : String) -> String? {
// binary to integer:
guard let num = UInt64(bin, radix: 2) else { return nil }
// integer to hex:
let hex = String(num, radix: 16, uppercase: true) // (or false)
return hex
}

Convert between Decimal, Binary and Hexadecimal in Swift

Both String and Int have initializers which take a radix (base). Combining those, you can achieve all of the conversions:

// Decimal to binary
let d1 = 21
let b1 = String(d1, radix: 2)
print(b1) // "10101"

// Binary to decimal
let b2 = "10110"
let d2 = Int(b2, radix: 2)!
print(d2) // 22

// Decimal to hexadecimal
let d3 = 61
let h1 = String(d3, radix: 16)
print(h1) // "3d"

// Hexadecimal to decimal
let h2 = "a3"
let d4 = Int(h2, radix: 16)!
print(d4) // 163

// Binary to hexadecimal
let b3 = "10101011"
let h3 = String(Int(b3, radix: 2)!, radix: 16)
print(h3) // "ab"

// Hexadecimal to binary
let h4 = "face"
let b4 = String(Int(h4, radix: 16)!, radix: 2)
print(b4) // "1111101011001110"

How to convert Data to hex string in swift

A simple implementation (taken from How to hash NSString with SHA1 in Swift?, with an additional option for uppercase output) would be

extension Data {
struct HexEncodingOptions: OptionSet {
let rawValue: Int
static let upperCase = HexEncodingOptions(rawValue: 1 << 0)
}

func hexEncodedString(options: HexEncodingOptions = []) -> String {
let format = options.contains(.upperCase) ? "%02hhX" : "%02hhx"
return self.map { String(format: format, $0) }.joined()
}
}

I chose a hexEncodedString(options:) method in the style of the existing method base64EncodedString(options:).

Data conforms to the Collection protocol, therefore one can use
map() to map each byte to the corresponding hex string.
The %02x format prints the argument in base 16, filled up to two digits
with a leading zero if necessary. The hh modifier causes the argument
(which is passed as an integer on the stack) to be treated as a one byte
quantity. One could omit the modifier here because $0 is an unsigned
number (UInt8) and no sign-extension will occur, but it does no harm leaving
it in.

The result is then joined to a single string.

Example:

let data = Data([0, 1, 127, 128, 255])
// For Swift < 4.2 use:
// let data = Data(bytes: [0, 1, 127, 128, 255])
print(data.hexEncodedString()) // 00017f80ff
print(data.hexEncodedString(options: .upperCase)) // 00017F80FF

The following implementation is faster by a factor about 50
(tested with 1000 random bytes). It is inspired to
RenniePet's solution
and Nick Moore's solution, but takes advantage of
String(unsafeUninitializedCapacity:initializingUTF8With:)
which was introduced with Swift 5.3/Xcode 12 and is available on macOS 11 and iOS 14 or newer.

This method allows to create a Swift string from UTF-8 units efficiently, without unnecessary copying or reallocations.

An alternative implementation for older macOS/iOS versions is also provided.

extension Data {
struct HexEncodingOptions: OptionSet {
let rawValue: Int
static let upperCase = HexEncodingOptions(rawValue: 1 << 0)
}

func hexEncodedString(options: HexEncodingOptions = []) -> String {
let hexDigits = options.contains(.upperCase) ? "0123456789ABCDEF" : "0123456789abcdef"
if #available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *) {
let utf8Digits = Array(hexDigits.utf8)
return String(unsafeUninitializedCapacity: 2 * self.count) { (ptr) -> Int in
var p = ptr.baseAddress!
for byte in self {
p[0] = utf8Digits[Int(byte / 16)]
p[1] = utf8Digits[Int(byte % 16)]
p += 2
}
return 2 * self.count
}
} else {
let utf16Digits = Array(hexDigits.utf16)
var chars: [unichar] = []
chars.reserveCapacity(2 * self.count)
for byte in self {
chars.append(utf16Digits[Int(byte / 16)])
chars.append(utf16Digits[Int(byte % 16)])
}
return String(utf16CodeUnits: chars, count: chars.count)
}
}
}

Hexadecimal (Long string)l to Binary

You can do this by converting each character to a 4-digit binary value and then concatenating the binary string values:

let hexString = "3c1878900216d211aa9e0924"

let binary = hexString.compactMap { Int(String($0), radix: 16) }
.map { ("000" + String($0, radix: 2)).suffix(4) }
.joined()
print(binary)

Output:

001111000001100001111000100100000000001000010110110100100001000110101010100111100000100100100100

Hex string to text conversion - swift 3

You probably can use something like this:

func hexToStr(text: String) -> String {

let regex = try! NSRegularExpression(pattern: "(0x)?([0-9A-Fa-f]{2})", options: .caseInsensitive)
let textNS = text as NSString
let matchesArray = regex.matches(in: textNS as String, options: [], range: NSMakeRange(0, textNS.length))
let characters = matchesArray.map {
Character(UnicodeScalar(UInt32(textNS.substring(with: $0.rangeAt(2)), radix: 16)!)!)
}

return String(characters)
}

How to Save A String With Hexadecimal Encoding In Swift

What I was originally doing was:
Binary->Decimal->Hex string->Data->File

What seemed to solve my issue was to do:
Binary->UInt8 Array->Data->File

Creating Data directly from [UInt8] took care of converting it to hex, spacing, and retained the hexadecimal encoding I needed.

Hopefully that helps someone else having similar issues.

How to convert hexadecimal string to an array of UInt8 bytes in Swift?

You can convert your hexa String back to array of [UInt8] iterating every two hexa characters and initialize an UInt8 using its string radix initializer. The following implementation assumes the hexa string is well formed:


Edit/update: Xcode 11 • Swift 5.1

extension StringProtocol {
var hexaData: Data { .init(hexa) }
var hexaBytes: [UInt8] { .init(hexa) }
private var hexa: UnfoldSequence<UInt8, Index> {
sequence(state: startIndex) { startIndex in
guard startIndex < self.endIndex else { return nil }
let endIndex = self.index(startIndex, offsetBy: 2, limitedBy: self.endIndex) ?? self.endIndex
defer { startIndex = endIndex }
return UInt8(self[startIndex..<endIndex], radix: 16)
}
}
}


let string = "e0696349774606f1b5602ffa6c2d953f"
let data = string.hexaData // 16 bytes
let bytes = string.hexaBytes // [224, 105, 99, 73, 119, 70, 6, 241, 181, 96, 47, 250, 108, 45, 149, 63]

If you would like to handle malformed hexa strings as well you can make it a throwing method:

extension String {
enum DecodingError: Error {
case invalidHexaCharacter(Character), oddNumberOfCharacters
}
}


extension Collection {
func unfoldSubSequences(limitedTo maxLength: Int) -> UnfoldSequence<SubSequence,Index> {
sequence(state: startIndex) { lowerBound in
guard lowerBound < endIndex else { return nil }
let upperBound = index(lowerBound,
offsetBy: maxLength,
limitedBy: endIndex
) ?? endIndex
defer { lowerBound = upperBound }
return self[lowerBound..<upperBound]
}
}
}


extension StringProtocol {
func hexa<D>() throws -> D where D: DataProtocol & RangeReplaceableCollection {
try .init(self)
}
}


extension DataProtocol where Self: RangeReplaceableCollection {
init<S: StringProtocol>(_ hexa: S) throws {
guard hexa.count.isMultiple(of: 2) else {
throw String.DecodingError.oddNumberOfCharacters
}
self = .init()
reserveCapacity(hexa.utf8.count/2)
for pair in hexa.unfoldSubSequences(limitedTo: 2) {
guard let byte = UInt8(pair, radix: 16) else {
for character in pair where !character.isHexDigit {
throw String.DecodingError.invalidHexaCharacter(character)
}
continue
}
append(byte)
}
}
}

Usage:

let hexaString = "e0696349774606f1b5602ffa6c2d953f"
do {
let bytes: [UInt8] = try hexaString.hexa()
print(bytes)
let data: Data = try hexaString.hexa()
print(data)
} catch {
print(error)
}

This will print

[224, 105, 99, 73, 119, 70, 6, 241, 181, 96, 47, 250, 108, 45, 149, 63]

16 bytes



Related Topics



Leave a reply



Submit