Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to parse a string of hex into ascii equivalent in Swift 2

In swift 2 what is the best way to go about turning strings of hex characters into their ascii equivalent.

Given

let str1 = "0x4d 0x4c 0x4e 0x63"
let str2 = "4d 4c 4e 63"
let str3 = "4d4c4e63"
let str4 = "4d4d 4e63"
let str5 = "4d,4c,4e,63"

we would like to run a function (or string extension) that spits out: 'MLNc' which is the ascii equivalent of the hex strings

Pseudo Code:

  • Strip out all "junk", commas spaces etc
  • Get "2 character chunks" and then convert these characters into the int equivalent with strtoul
  • build an array of characters and merge them into a string

Partial Implementation

func hexStringtoAscii(hexString : String) -> String {
    
    let hexArray = split(hexString.characters) { $0 == " "}.map(String.init)
    let numArray = hexArray.map{  strtoul($0, nil, 16)  }.map{Character(UnicodeScalar(UInt32($0)))}
    return String(numArray)
}

Is this partial implementation on the correct path? And if so, how is the best way to handle the chunking

like image 396
Jeef Avatar asked Aug 04 '15 18:08

Jeef


1 Answers

Using regular expression matching is one possible method to extract the "hex numbers" from the string. What you are looking for is an optional "0x", followed by exactly 2 hex digits. The corresponding regex pattern is "(0x)?([0-9a-f]{2})".

Then you can convert each match to a Character and finally concatenate the characters to a String, quite similar to your "partial implementation". Instead of strtoul() you can use the UInt32 initializer

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

which is new in Swift 2.

The pattern has two "capture groups" (encloses in parentheses), the first one matches the optional "0x", and the second one matches the two hex digits, the corresponding range can be retrieved with rangeAtIndex(2).

This leads to the following implementation which can handle all your sample strings:

func hexStringtoAscii(hexString : String) -> String {

    let pattern = "(0x)?([0-9a-f]{2})"
    let regex = try! NSRegularExpression(pattern: pattern, options: .CaseInsensitive)
    let nsString = hexString as NSString
    let matches = regex.matchesInString(hexString, options: [], range: NSMakeRange(0, nsString.length))
    let characters = matches.map {
        Character(UnicodeScalar(UInt32(nsString.substringWithRange($0.rangeAtIndex(2)), radix: 16)!))
    }
    return String(characters)
}

(See Swift extract regex matches for an explanation for the conversion to NSString.)

Note that this function is quite lenient, it just searches for 2-digit hex strings and ignores all other characters, so this would be accepted as well:

let str6 = "4d+-4c*/4e😈🇩🇪0x63"

Update for Swift 5.1:

func hexStringtoAscii(_ hexString : String) -> String {

    let pattern = "(0x)?([0-9a-f]{2})"
    let regex = try! NSRegularExpression(pattern: pattern, options: .caseInsensitive)
    let nsString = hexString as NSString
    let matches = regex.matches(in: hexString, options: [], range: NSMakeRange(0, nsString.length))
    let characters = matches.map {
        Character(UnicodeScalar(UInt32(nsString.substring(with: $0.range(at: 2)), radix: 16)!)!)
    }
    return String(characters)
}
like image 79
Martin R Avatar answered Sep 29 '22 21:09

Martin R