How to parse ascii characters in Swift?

I have a string encoded as ascii characters that contains letters and numbers. When I print out the string it looks like the numbers are not showing up properly. The string is being read from a data object where I convert everything to an Int8 type and construct a Character from that:

for i in 0..<dataLength {
    let byte: Int8 = readByte()
    let char = Character(unicodeScalarLiteral: .init(.init(byte)))
     print("Character: ", char, char.asciiValue)
}

Example.) "40" appears as "@D". I was able to print out the ascii codes of these and found it to be [64, 68], but I'm not sure how to now convert it to the number 40.

It’s hard to say what’s going on here without more info about your input data. You wrote:

The string is being read from a data object

Do you mean a value of type Data? If so, can you post a hex dump of the bytes you’re trying to interpret?

To get a hex dump, use this code:

let d: Data = … some value …
let hexDumpForDebuggingPurposesOnly = (d as NSData).debugDescription

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

I have a string encoded as ascii characters that contains letters and numbers.

Your hex dump, taken as a whole, isn’t an ASCII string. And if it’s supposed to be an Adobe Photoshop brush file then it definitely won’t be a simple string. I don’t know this format but it looks like a binary structure that includes short ASCII tags. Does your question pertain to parsing just the individual tag-like parts from within the larger structure?

Example.) "40" appears as "@D". I was able to print out the ascii codes of these and found it to be [64, 68]

Well 64 decimal is equal to 0x40 (hex), but this isn’t too helpful without knowing details of the file format. How are you sure beforehand that those two bytes are to be interpreted as that value? Do you have the documentation for this file format?

For those reading along at home, I’ll be helping yyjhuj in a different context.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

I’ll be helping yyjhuj in a different context.

I wanted to post a summary here, just for folks reading along at home. I won’t go into too much detail, but I think the approach described below has a number of benefits:

  • It’s super easy (-:

  • The resulting parser is relatively straightforward.

  • The final code is reasonably efficient, especially in Release builds.

  • It uses no ‘unsafe’ constructs.

  • It can be easily extended for other types.


To start, I added an extension to Data to parse integers:

extension Data {

    mutating func popFirstBigEndian<T>(_ x: T.Type) -> T? where T: FixedWidthInteger {
        guard self.count >= MemoryLayout<T>.size else { return nil }
        defer {
            self = self.dropFirst(MemoryLayout<T>.size)
        }
        return self.prefix(MemoryLayout<T>.size).reduce(0, { T($0) << 8 | T($1) })
    }
}

With that, parsing a file in the format described by the above-mentioned Adobe Community Forums post is pretty straightforward:

var remaining = data
guard
    let version = remaining.popFirstBigEndian(UInt16.self),
    let subVersion = remaining.popFirstBigEndian(UInt16.self)
else {
    throw ParseError.expectedVersionAndSubVersion
}
var sections: [Section] = []
while !remaining.isEmpty {
    guard
        let sig32 = remaining.popFirstBigEndian(UInt32.self),
        let key32 = remaining.popFirstBigEndian(UInt32.self),
        let length32 = remaining.popFirstBigEndian(UInt32.self),
        let length = Int(exactly: length32)
    else {
        throw ParseError.expectedSectionHeader
    }
    guard OSType(rawValue: sig32) == .photoshop else {
        throw ParseError.expectedPhotoshopSignature
    }
    guard let contents = remaining.popFirst(length) else {
        throw ParseError.expectedSectionContents
    }
    guard length.isMultiple(of: 2) || remaining.popFirst() != nil else {
        throw ParseError.expectedPad
    }
    let section = Section(key: OSType(rawValue: key32), contents: contents)
    sections.append(section)
}

The final sticking point encountered by yyjhuj was that some of the sections of this file include binary floating point numbers. Parsing those is relatively straightforward — see the snippet below — but you have to recognise them for what they are.

extension Data {

    mutating func popFirstBigEndianFloat64() -> Float64? {
        guard let u64 = self.popFirstBigEndian(UInt64.self) else { return nil }
        return Double(bitPattern: u64)
    }
}

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

How to parse ascii characters in Swift?
 
 
Q