swift - 如何用字符串、 float 解析十六进制

标签 swift

我有包含地点名称和坐标(纬度、经度)的二进制文件,每当我使用 .ascii 编码将其解析为 String 时,它都不会解析它出色地。我假设从 Float 值(坐标)解析失败。

读取InputStream

extension Data {
    init(reading input: InputStream) {
        self.init()
        input.open()

        let bufferSize = 1024
        let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bufferSize)
        while input.hasBytesAvailable {
            let read = input.read(buffer, maxLength: bufferSize)
            self.append(buffer, count: read)
        }
        buffer.deallocate()

        input.close()
    }
}

File to parse

let filepath = Bundle.main.path(forResource: "MN", ofType: "dat")
let data = Data.init(reading: InputStream(fileAtPath: filepath)!)
let parsedData = String.init(data: data, encoding: .ascii)

Parsing result

关于如何以正确的方式解析它有什么想法吗?

例如 Java ObjectInputStream 有调用的方法:

inputStreamObj.readUTF()
inputStreamObj.readFloat()

Java

enter image description here

最佳答案

正如我在评论中所写,您需要阅读规范 Object Serialization Stream Protocol .

因此,前 4 个字节表示 STREAM_MAGIC、STREAM_VERSION,应始终为相同的值。而5字节序列0x7A 0xhh 0xhh 0xhh 0xhh表示TC_BLOCKDATALONG(0xhhhhhhhh)。

并且在解析字符串和 float 之前需要连接所有 block 。

因此,准备DataReader:

(与 Sulthan 的几乎相同,但它正确处理了修改后的 UTF-8。)

struct DataReader {
    enum DataReaderError: Error {
        case invalidFirstByte(byte: UInt16, offset: Int)
        case invalidFollowingByte
        case missingFollowingByte
        case insufficientData
    }
    var data: Data
    var currentPosition: Int

    init(data: Data) {
        self.data = data
        self.currentPosition = 0
    }

    mutating func skipBytes(_ n: Int) {
        currentPosition += n
    }

    private mutating func readBigEndian<T: FixedWidthInteger>() throws -> T {
        guard currentPosition + MemoryLayout<T>.size <= data.count else {
            throw DataReaderError.insufficientData
        }
        var fixedWithInteger: T = 0
        let range: Range<Int> = currentPosition ..< currentPosition + MemoryLayout<T>.size
        withUnsafeMutableBytes(of: &fixedWithInteger) {ptrT in
            let uint8Ptr = ptrT.baseAddress!.assumingMemoryBound(to: UInt8.self)
            data.copyBytes(to: uint8Ptr, from: range)
        }
        currentPosition += MemoryLayout<T>.size
        return fixedWithInteger.bigEndian
    }

    mutating func readFloat() throws -> Float {
        let floatBits: UInt32 = try readBigEndian()
        return Float(bitPattern: floatBits)
    }

    mutating func readUnsignedShort() throws -> Int {
        let ushortValue: UInt16 = try readBigEndian()
        return Int(ushortValue)
    }

    mutating func readInt() throws -> Int {
        let intValue: Int32 = try readBigEndian()
        return Int(intValue)
    }

    mutating func readUnsignedByte() throws -> Int {
        guard currentPosition < data.count else {
            throw DataReaderError.insufficientData
        }
        let byte = data[currentPosition]
        currentPosition += 1
        return Int(byte)
    }

    mutating func readBytes(_ n: Int) throws -> Data {
        guard currentPosition + n <= data.count else {
            throw DataReaderError.insufficientData
        }
        let subdata = data[currentPosition ..< currentPosition+n]
        currentPosition += n
        return subdata
    }

    mutating func readUTF() throws -> String {
        //Get byte size of the string
        let count = try readUnsignedShort()
        //Decoding Modified UTF-8
        var utf16: [UInt16] = []
        var offset = 0
        while offset < count {
            let firstByte = UInt16(data[currentPosition + offset])
            if firstByte & 0b1_0000000 == 0b0_0000000 {
                utf16.append(firstByte)
                offset += 1
            } else if firstByte & 0b111_00000 == 0b110_00000 {
                guard offset + 1 < count else {throw DataReaderError.missingFollowingByte}
                let secondByte = UInt16(data[currentPosition + offset + 1])
                guard secondByte & 0b11_000000 == 0b10_000000 else {throw DataReaderError.invalidFollowingByte}
                let codeUnit = ((firstByte & 0b000_11111) << 6) | (secondByte & 0b00_111111)
                utf16.append(codeUnit)
                offset += 2
            } else if firstByte & 0b1111_0000 == 0b1110_0000 {
                guard offset + 2 < count else {throw DataReaderError.missingFollowingByte}
                let secondByte = UInt16(data[currentPosition + offset + 1])
                guard secondByte & 0b11_000000 == 0b10_000000 else {throw DataReaderError.invalidFollowingByte}
                let thirdByte = UInt16(data[currentPosition + offset + 2])
                guard thirdByte & 0b11_000000 == 0b10_000000 else {throw DataReaderError.invalidFollowingByte}
                let codeUnit = ((firstByte & 0b0000_1111) << 12) | ((secondByte & 0b00_111111) << 6) | (thirdByte & 0b00_111111)
                utf16.append(codeUnit)
                offset += 3
            } else {
                throw DataReaderError.invalidFirstByte(byte: firstByte, offset: currentPosition+offset)
            }
        }
        currentPosition += offset
        return String(utf16CodeUnits: &utf16, count: utf16.count)

    }

    var isAtEnd: Bool {
        return currentPosition == data.count
    }
}

我们可以按如下方式解析您的MN.dat:

let mnUrl = Bundle.main.url(forResource: "MN", withExtension: "dat")!
do {
    let data = try Data(contentsOf: mnUrl)
    var reader = DataReader(data: data)
    reader.skipBytes(4)

    //First collect all blocks
    var blockData = Data()
    while !reader.isAtEnd {
        let contentType = try reader.readUnsignedByte()
        if contentType == 0x7A {//TC_BLOCKDATALONG
            let size = try reader.readInt()
            let block = try reader.readBytes(size)
            blockData.append(block)
        } else if contentType == 0x77 {//TC_BLOCKDATA
            let size = try reader.readUnsignedByte()
            let block = try reader.readBytes(size)
            blockData.append(block)
        } else {
            print("Unsupported content type")
            break
        }
    }
    //Then read the contents of blockData
    var blockReader = DataReader(data: blockData)
    while !blockReader.isAtEnd {
        let string = try blockReader.readUTF()
        print(string)
        let float1 = try blockReader.readFloat()
        print(float1)
        let float2 = try blockReader.readFloat()
        print(float2)
        //Use string, float1, float2 as you like
    }
} catch {
    print(error)
}

输出:

Albert Lea
43.648
-93.3683
Albertville
45.2377
-93.6544
Alexandria
45.8852
-95.3775
(... no errors...)
Woodbury
44.9239
-92.9594
Worthington
43.62
-95.5964
Wyoming
45.3364
-92.9972
Zimmerman
45.4433
-93.59

如果您的二进制数据可能包含其他内容类型,您可能需要修改上面的代码。

关于swift - 如何用字符串、 float 解析十六进制,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49731055/

相关文章:

swift - viewForAnnotation 未被调用(使用 Alamofire 解析 JSON)

ios - swift "Could not cast value of type "AppName.ToDoViewViewCtonroller“到 AppName.CalendarView”SIGBART

xcode - 如何向 Apple 提交 XCode 错误?

ios - 解析指针值返回 nil 的查询

ios - NSClassFromString swift 2.0 返回的 nil 值

ios - COCOS2D 聚光灯图标在 iPhone 6 上不显示

ios - 获取 func imagePickerController(picker : UIImagePickerController, didFinishPackingWithMediaInfo: [String : AnyObject]) 的返回值

ios - AppDelegate applicationDidEnterBackground 在模拟器上工作但不在设备上工作

xcode - 无限旋转按钮 : Swift

swift - 如何在小 Label.text 中显示大文本?