在 Swift 中将两个字节的 UInt8 数组转换为 UInt16
Posted
技术标签:
【中文标题】在 Swift 中将两个字节的 UInt8 数组转换为 UInt16【英文标题】:Convert a two byte UInt8 array to a UInt16 in Swift 【发布时间】:2014-08-12 14:35:48 【问题描述】:使用 Swift 我想将字节从 uint8_t 数组转换为整数。
“C”示例:
char bytes[2] = 0x01, 0x02;
NSData *data = [NSData dataWithBytes:bytes length:2];
NSLog(@"data: %@", data); // data: <0102>
uint16_t value2 = *(uint16_t *)data.bytes;
NSLog(@"value2: %i", value2); // value2: 513
快速尝试:
let bytes:[UInt8] = [0x01, 0x02]
println("bytes: \(bytes)") // bytes: [1, 2]
let data = NSData(bytes: bytes, length: 2)
println("data: \(data)") // data: <0102>
let integer1 = *data.bytes // This fails
let integer2 = *data.bytes as UInt16 // This fails
let dataBytePointer = UnsafePointer<UInt16>(data.bytes)
let integer3 = dataBytePointer as UInt16 // This fails
let integer4 = *dataBytePointer as UInt16 // This fails
let integer5 = *dataBytePointer // This fails
在 Swift 中从 UInt8 数组创建 UInt16 值的正确语法或代码是什么?
我对 NSData 版本感兴趣,正在寻找不使用临时数组的解决方案。
【问题讨论】:
当然,像 Swift 这样的新语言有一种优雅的方式可以将NSData
中接收的数据转换为整数变量。这就是我要找的。span>
[0x01, 0x02]的uint16是0x0102还是0x0201?这取决于处理器的字节顺序。在java中它是定义的。在 C 中打开。在斯威夫特我不知道。技巧*(uint16)(uint8[2])
也存在地址对齐问题。
@JoopEggen Endian-ness 不是问题。
更好的是索引数据:let u16 = UnsafePointer<UInt16>(data.bytes)[index]
【参考方案1】:
如果你想通过NSData
,那么它会像这样工作:
let bytes:[UInt8] = [0x01, 0x02]
println("bytes: \(bytes)") // bytes: [1, 2]
let data = NSData(bytes: bytes, length: 2)
print("data: \(data)") // data: <0102>
var u16 : UInt16 = 0 ; data.getBytes(&u16)
// Or:
let u16 = UnsafePointer<UInt16>(data.bytes).memory
println("u16: \(u16)") // u16: 513
或者:
let bytes:[UInt8] = [0x01, 0x02]
let u16 = UnsafePointer<UInt16>(bytes).memory
print("u16: \(u16)") // u16: 513
两种变体都假定字节按主机字节顺序排列。
Swift 3 (Xcode 8) 更新:
let bytes: [UInt8] = [0x01, 0x02]
let u16 = UnsafePointer(bytes).withMemoryRebound(to: UInt16.self, capacity: 1)
$0.pointee
print("u16: \(u16)") // u16: 513
【讨论】:
我对@987654325@ 版本感兴趣,但正在寻找不使用临时数组的解决方案。 @Zaph:哪个临时数组?也许let u16 = UnsafePointer<UInt16>(data.bytes).memory
是您要找的?
这正是我要找的!谢谢! let u16 = UnsafePointer<UInt16>(data.bytes).memory
@Zaph 确保您确实有更好的性能,并且您确实需要它。使用不安全的代码最终会使你的性能变差,因为编译器可能会忘记你的代码做了什么,它不能用更高效的东西替换你的代码。
@pqnet 我不关心这个级别的性能,当测量显示需要并查明问题所在时,性能是最好的解决方案。至于安全,当然我想要安全的东西,请提供这样的解决方案。请记住,在这种情况下,以及在同一类中的许多其他原始数据字节是在没有 JSON、XML 或其他更高级别格式的情况下接收的。【参考方案2】:
在 Swift 5 或更高版本中,您可以使用 withUnsafeBytes $0.load(as: UInt16.self)
将字节 [UInt8]
转换为 UInt16
值
let bytes: [UInt8] = [1, 2]
加载为UInt16
let uint16 = bytes.withUnsafeBytes $0.load(as: UInt16.self) // 513
为了摆脱冗长,我们可以创建一个扩展 ContiguousBytes
的泛型方法:
extension ContiguousBytes
func object<T>() -> T
withUnsafeBytes $0.load(as: T.self)
用法:
let bytes: [UInt8] = [1, 2]
let uint16: UInt16 = bytes.object() // 513
并访问集合中任何位置的字节:
extension Data
func subdata<R: RangeExpression>(in range: R) -> Self where R.Bound == Index
subdata(in: range.relative(to: self) )
func object<T>(at offset: Int) -> T
subdata(in: offset...).object()
extension Sequence where Element == UInt8
var data: Data .init(self)
extension Collection where Element == UInt8, Index == Int
func object<T>(at offset: Int = 0) -> T
data.object(at: offset)
用法:
let bytes: [UInt8] = [255, 255, 1, 2]
let uintMax: UInt16 = bytes.object() // 65535 at offset zero
let uint16: UInt16 = bytes.object(at: 2) // 513 at offset two
【讨论】:
【参考方案3】:怎么样
let bytes:[UInt8] = [0x01, 0x02]
let result = (UInt16(bytes[1]) << 8) + UInt16(bytes[0])
使用循环,这很容易推广到更大的字节数组,并且可以包装在函数中以提高可读性:
let bytes:[UInt8] = [0x01, 0x02, 0x03, 0x04]
func bytesToUInt(byteArray: [UInt8]) -> UInt
assert(byteArray.count <= 4)
var result: UInt = 0
for idx in 0..<(byteArray.count)
let shiftAmount = UInt((byteArray.count) - idx - 1) * 8
result += UInt(byteArray[idx]) << shiftAmount
return result
println(bytesToUInt(bytes)) // result is 16909060
【讨论】:
是的,let b:UInt16 = UInt16(dataBytePointer[0]) * 256 + UInt16(dataBytePointer[1])
也可以。 UInt32 非常不雅,并且开始看起来更糟。
为什么 UInt32 更糟?您可以通过循环遍历数组并按当前索引缩放移位量来组装多个字节。
更糟糕的是因为语句变得更长更复杂。
只上4就不用一概而论了。把公式写在一行,手动写班次。如果你坚持一个通用的形式使用递归而不是循环,会更好看。
@pqnet:我喜欢它很容易推广到 2、4 或 8 以分别产生 UInt16、UInt32 或 UInt64。我还打算将循环/函数实现作为对 Zaph 声明的回应,即当模式如此清晰以至于几乎可以轻松实现自动化时,情况会更糟。我个人更喜欢它是一个全 Swift 的解决方案,尽管有人声明了基于 NSData 的解决方案的偏好。我喜欢将其保留在基础语言中并避免不安全的指针构造。【参考方案4】:
我不知道 swift 的语法,但是像这样的东西呢:
let a:UInt16 = UInt16(bytes[0]) * 256 + UInt16(bytes[1])
【讨论】:
关闭,这确实有效:let a:UInt16 = UInt16(bytes[0]) * 256 + UInt16(bytes[1])
.
@Zaph 我会复制粘贴你的代码,它肯定比我的更正确【参考方案5】:
如果字节在NSData
对象中,您可以这样做(假设data:NSData
):
var number: UInt16 = 0
data.getBytes(&number, length: sizeof(UInt16))
getBytes
方法在number
的内存位置最多写入两个字节(类似于C 的memcpy
。
如果data
没有足够的字节,这不会使您的应用程序崩溃。
(编辑:如果从缓冲区的开头开始,则无需使用范围)
【讨论】:
我希望 Swift 允许安全直接的操作,你能说可选吗?这仍然让人怀疑那里是否有字节为 0,或者是否只有一个或没有。所以仍然需要进行长度检查。只需先进行长度检查,然后让u16 = UnsafePointer<UInt16>(data.bytes)[index]
也是安全的。一旦完成了长度检查,就可以简单地通过索引进一步索引数据。
@Zaph 不管什么人,这是你的代码。我不是负责的人
@Zaph 误会是两个人的错。对不起,我真的以为你在骗我【参考方案6】:
Martin R 的回答非常棒,并且针对 beta 6 进行了很好的更新。
但是,如果您需要获取不在缓冲区开头的字节,则建议的 withMemoryRebound
方法不提供重新绑定的范围。
我对此的解决方案,例如。从数组中挑选出第二个 UInt16 是:
var val: UInt16 = 0
let buf = UnsafeMutableBufferPointer(start: &val, count: 1)
_ = dat.copyBytes(to: buf, from: Range(2...3))
【讨论】:
【参考方案7】:假设小端编码。
要从 [UInt8] 转换为 UInt16,您可以执行类似的操作
var x: [UInt8] = [0x01, 0x02]
var y: UInt16 = 0
y += UInt16(x[1]) << 0o10
y += UInt16(x[0]) << 0o00
为了转换为 UInt32,此模式扩展到
var x: [UInt8] = [0x01, 0x02, 0x03, 0x04]
var y: UInt32 = 0
y += UInt32(x[3]) << 0o30
y += UInt32(x[2]) << 0o20
y += UInt32(x[1]) << 0o10
y += UInt32(x[0]) << 0o00
移位量的八进制表示可以很好地指示移位了多少完整字节(8 变为 0o10,16 变为 0o20 等)。
对于 UInt16,这可以简化为以下内容:
var x: [UInt8] = [0x01, 0x02]
let y: UInt16 = reverse(x).reduce(UInt16(0))
$0 << 0o10 + UInt16($1)
以及 UInt32 的以下内容:
var x: [UInt8] = [0x01, 0x02, 0x03, 0x04]
let y: UInt32 = reverse(x).reduce(UInt32(0))
$0 << 0o10 + UInt32($1)
精简版也适用于 UInt64,并且还可以处理字节编码不使用所有字节的值,例如 [0x01, 0x02, 0x03]
【讨论】:
这会产生 2049,但正确的结果应该是 513。将 010 更改为 8 会产生当前答案。 010 是以 10 为底,0o10 是以 8 为底。虽然过于聪明,但它也很昂贵,需要一段时间才能看到发生了什么。 更正了答案,因此现在可以正确使用八进制表示。感谢您指出我错过了 o。 八进制?为什么是八进制,只是为了晦涩难懂? 8 不是更明智/更容易理解吗?唯一比较模糊的是八进制 ascii,如 Tar 中所见。 更新了答案以解释为什么使用八进制。 如果你必须通过所有这些来证明使用八进制的合理性,那可能是聪明的。最好的代码对普通读者来说是显而易见的。以上是关于在 Swift 中将两个字节的 UInt8 数组转换为 UInt16的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Swift 3 中将 [UInt8] 数组复制到 C 指针?