swift 将 UInt8 数组的一部分包装为字符串的正确方法是什么?

问题描述

我要在 iPhone 应用中处理一些原始数据。字符串总是来自一个非常大的底层字节数组,所以我希望能够从数组中提取字符串而不会触发内存不足问题。

我可以在文档中看到一个 String(bytesNocopy: ...),这是我想要的吗?应该如何使用它?

假设一个名为 data 的 uint8 数组和 index一个数字,显示字符串在数组中的位置。

var myData:[UInt8] = [
    4,// String 1 length
    65,66,67,68,// String 1 data
    4,// String 2 length
    69,70,71,0     // String 2 data
]

var index = 0
let string1 = readString(&myData,&index)
let string2 = readString(&myData,&index)
print(string1,string2)

// Read a string located at a specific
// position in a byte array,and increment
// the pointer into the array into the next
// position
func readString(_ data:inout [UInt8],_ index:inout Int)  -> String {
    // Read string length out of data array
    let l = Int(readUInt8(&data,&index))

    // Read string out of data array without copy
    let s = String(
        bytesNocopy: UnsafeMutableRawPointer(data + index),// <-- what goes here??
        length: l,encoding: .utf8,freeWhenDone: false)
    index = index + l
    if s == nil {
        return ""
    }
    return s!
}

// Read a byte as an integer from a 
// data array,and increment the pointer into
// the data array to the next position.
func readUInt8(_ data:inout [UInt8],_ x:inout Int)  -> UInt8 {
    let v = data[x]
    x = x + 1
    return v
}

注意:此问题已更新以包含示例数据,并将变量 x 重命名index 以更清楚地表明该问题是在询问如何创建字符串来自字节数组的

解决方法

这是您可以尝试的方法-

import Foundation

func readString(_ data: inout [UInt8],_ x: inout Int) -> String {
    let l = 4
    var slice: ArraySlice<UInt8> = data[x..<x+l] // No copy,view into existing Array
    x += l
    
    return slice.withUnsafeBytes({ pointer in
        // No copy,just making compiler happy (assumption that it is bound to UInt8 is correct
        if let bytes = pointer.baseAddress?.assumingMemoryBound(to: UInt8.self) {
            return String(
                bytesNoCopy: UnsafeMutableRawPointer(mutating: bytes),// No copy
                length: slice.count,encoding: .utf8,freeWhenDone: false
            ) ?? ""
        } else {
            return ""
        }
    })
}

测试

var a: [UInt8] = [
    65,66,67,68,69,70,71,72
]
var x = 0

let test1 = readString(&a,&x)
print("test1 : \(test1)")
// test1 : ABCD

let test2 = readString(&a,&x)
print("test2 : \(test2)")
// test2 : EFGH