Is there a swift native function to convert a number to a hex string?
Asked Answered
H

5

57

Is there any native Swift way for any (at least integer) number to get its hexadecimal representation in a string? And the inverse. It must not use Foundation. For example the String class has a function

func toInt() -> Int?

which converts a string representing an integer to its Int value. I am looking for something similar, using the hex strings. I know this is easily implementable, but if Swift has it there already it would be better. Otherwise if you made already an extension of String and Int to achieve the following:

let anHex = "0xA0"
if let anInt = anHex.toInt() {
   println(anInt)               // prints 128
   println(anInt.toHexString()) // prints "0xA0"
}

I know it isn't rocket science but in case please share it.

PS: This is similar to this question, the difference is that it was very related to the Foundation framework, while I am not using it in my code (nor I am importing anything else) and for now I'd like to keep it in this way, also for learning purposes.

Hemimorphite answered 28/11, 2014 at 13:6 Comment(3)
See #26791160 for one direction and #26181721 for the other directions (just replace base 2 by base 16). The first requires import Darwin, the latter is "Swift only".Delfeena
Well, if you're not importing anything you're going to have trouble, but have you tried sprintf?Melbamelborn
I see it now. So "String(num, radix: base)" is what I was looking for, since I am not importing anything for now. Ok, thanks. I must force myself to look at the initializers when checking the source code. This is the second one I missed.Mealymouthed
D
107

As of Swift 2, all integer types have a constructor

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

so that both integer to hex string and hex string to integer conversions can be done with built-in methods. Example:

let num = 1000
let str = String(num, radix: 16)
print(str) // "3e8"

if let num2 = Int(str, radix: 16) {
    print(num2) // 1000
}

(Old answer for Swift 1:) The conversion from an integer to a hex string can be done with

let hex = String(num, radix: 16)

(see for example How to convert a decimal number to binary in Swift?). This does not require the import of any Framework and works with any base between 2 and 36.

The conversion from a hex string to an integer can be done with the BSD library function strtoul() (compare How to convert a binary to decimal in Swift?) if you are willing to import Darwin.

Otherwise there is (as far as I know) no built-in Swift method. Here is an extension that converts a string to a number according to a given base:

extension UInt {
    init?(_ string: String, radix: UInt) {
        let digits = "0123456789abcdefghijklmnopqrstuvwxyz"
        var result = UInt(0)
        for digit in string.lowercaseString {
            if let range = digits.rangeOfString(String(digit)) {
                let val = UInt(distance(digits.startIndex, range.startIndex))
                if val >= radix {
                    return nil
                }
                result = result * radix + val
            } else {
                return nil
            }
        }
        self = result
    }
}

Example:

let hexString = "A0"
if let num = UInt(hexString, radix: 16) {
    println(num)
} else {
    println("invalid input")
}
Delfeena answered 28/11, 2014 at 14:7 Comment(8)
Minor correction String(num, radix: 16) works for bases 2 through 36.Hundredpercenter
@vacawama: Thanks, corrected. The same is true for strtoul() and the above extension method.Delfeena
Is there a way to avoid stripping any leading 0 characters? i.e. with radix 16, I'd like "0F", instead I get "F".Biographer
@charl: No, not with the built-in methods. You have to strip a leading "0" after the conversion.Delfeena
Swift 3: extension UInt { init?(_ string: String, radix: UInt) { let digits = "0123456789abcdefghijklmnopqrstuvwxyz" var result = UInt(0) for digit in string.lowercased().characters { guard let index = digits.characters.index(where: { $0 == digit }) else { return nil } let val = UInt(digits.distance(from: digits.startIndex, to: index)) if val >= radix { return nil } result = result * radix + val } self = result } }Prehistory
@Oscar: Why so complicated? The "builtin" constructor UInt("DEADBEAF", radix: 16) works in Swift 3 as well.Delfeena
Ah. Well that sure makes things much easier indeed :) Thanks!Prehistory
@Martin R Can we convert four digit decimal Integer to UInt8 Hexa Decimal?Harbour
G
28

update: Xcode 12.5 • Swift 5.4

extension StringProtocol {
    func dropping<S: StringProtocol>(prefix: S) -> SubSequence { hasPrefix(prefix) ? dropFirst(prefix.count) : self[...] }
    var hexaToDecimal: Int { Int(dropping(prefix: "0x"), radix: 16) ?? 0 }
    var hexaToBinary: String { .init(hexaToDecimal, radix: 2) }
    var decimalToHexa: String { .init(Int(self) ?? 0, radix: 16) }
    var decimalToBinary: String { .init(Int(self) ?? 0, radix: 2) }
    var binaryToDecimal: Int { Int(dropping(prefix: "0b"), radix: 2) ?? 0 }
    var binaryToHexa: String { .init(binaryToDecimal, radix: 16) }
}

extension BinaryInteger {
    var binary: String { .init(self, radix: 2) }
    var hexa: String { .init(self, radix: 16) }
}

Testing:

print("7fffffffffffffff".hexaToDecimal)      // "9223372036854775807" decimal integer
print("0x7fffffffffffffff".hexaToDecimal)    // "9223372036854775807" decimal integer
print("7fffffffffffffff".hexaToBinary) // "111111111111111111111111111111111111111111111111111111111111111" binary (String)
print("0x7fffffffffffffff".hexaToBinary) // "111111111111111111111111111111111111111111111111111111111111111"

print("255".decimalToHexa)   // "ff"       hexa (String)
print("255".decimalToBinary) // "11111111" binary (String)
0b11111111

print("11111111".binaryToHexa)      // "ff"  hexa (String)
print("0b11111111".binaryToHexa)    // "ff"  hexa (String)
print("11111111".binaryToDecimal)   // 255 decimal (Int)
print("0b11111111".binaryToDecimal) // 255 decimal (Int)

print(255.binary) // "11111111" binary (String)
print(255.hexa)   // "ff"       hexa (String)
Goar answered 28/11, 2014 at 15:28 Comment(2)
Incredibly thorough and helpful answer! As a side note, when converting from hex to decimal string, which is what I'm doing, this code doesn't left pad the 0's on to make a whole byte. This likely won't be a problem for anyone, but it's worth pointing out just in case. Thanks again!Arte
#33547467Goar
L
19

Swift 3:

String to UInt:

let str = "fcd7d7"
let number = UInt(str, radix: 16)!
print(number)

result: 16570327

UInt to hex String:

let number = UInt(exactly: 16570327)!
let str = String(number, radix: 16, uppercase: false)
print(str)

result: fcd7d7

Lamentable answered 7/9, 2017 at 10:56 Comment(0)
A
3

For Float, If you might want IEEE754 floating point to HEX

extension Float {
 func floatToHex()->String {
    return String(self.bitPattern, radix: 16, uppercase: true)
 }
}
let f:Float = 3.685746e+19
let hex = f.floatToHex()

print("\(hex)")//5FFFC000

Or Visa-versa

extension String {

 func hexToFloat() -> Float {
    var toInt = Int32(truncatingBitPattern: strtol(self, nil, 16))
    var toInt = Int32(_truncatingBits: strtoul(self, nil, 16)) //For Swift 5
    var float:Float32!
    memcpy(&float, &toInt, MemoryLayout.size(ofValue: float))
    return float
 }
}
Ashtray answered 12/7, 2017 at 8:27 Comment(0)
J
0

If you are trying to convert from a string into the following format "0x12ab", you also have to take into account the 0x bit at the start. Here is how to do it using modern Swift syntax:

let hex_str = "0x12ab"
let index = hex_str.index(hex_str.startIndex, offsetBy: 2)
let hex = Int(hex_str[index...], radix: 16)! // may throw an error
print(hex) // prints 4779
Jiggered answered 15/5 at 12:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.