如何在Swift中将[Int8]投射到[UInt8]

如何在Swift中将[Int8]投射到[UInt8],swift,Swift,我有一个只包含字符的缓冲区 let buffer: [Int8] = .... 然后我需要将其传递给一个函数进程,该函数将[UInt8]作为参数 func process(buffer: [UInt8]) { // some code } 将[Int8]缓冲区传递给[Int8]强制转换的最佳方式是什么?我知道下面的代码可以工作,但在这种情况下,缓冲区只包含一串字符,不需要使用map之类的函数 process(buffer.map{ x in UInt8(x) }) // OK pro

我有一个只包含字符的缓冲区

let buffer: [Int8] = ....
然后我需要将其传递给一个函数
进程
,该函数将[UInt8]作为参数

func process(buffer: [UInt8]) {
    // some code
}

将[Int8]缓冲区传递给[Int8]强制转换的最佳方式是什么?我知道下面的代码可以工作,但在这种情况下,缓冲区只包含一串字符,不需要使用map之类的函数

process(buffer.map{ x in UInt8(x) }) // OK
process([UInt8](buffer)) // error
process(buffer as! [UInt8]) // error

我使用的是Xcode7 b3 Swift2。

不能在Swift中强制转换数组。看起来你可以,但真正发生的是你正在一个接一个地铸造所有元素。因此,只有在可以对元素进行强制转换时,才能对数组使用强制转换表示法

在Swift中,不能在数字类型之间转换。您必须强制,这是一件完全不同的事情-也就是说,您必须基于原始对象创建一个不同数值类型的新对象。在预期使用UInt8的情况下使用Int8的唯一方法是强制它:
UInt8(x)


因此,对于一个Int8是正确的,对于整个Int8数组也是正确的。不能从Int8数组强制转换为UInt8数组,就像不能转换其中一个数组一样。最终得到一个UInt8数组的唯一方法是强制所有元素。这正是您的
map
调用所做的。这是做这件事的方法;说它是“不必要的”是毫无意义的。

IMO,最好的方法是在整个应用程序中坚持使用相同的基类型,以避免执行强制转换/强制。也就是说,可以到处使用
Int8
,也可以使用
UInt8
,但不能同时使用两者

如果您没有选择,例如,如果您使用两个单独的框架,而您无法控制它们,其中一个使用
Int8
,而另一个使用
UInt8
,那么如果您确实想使用Swift,您应该使用
map
。示例中的后两行(
process([UInt8](buffer))
process(buffer as![UInt8])
)看起来更像是C解决问题的方法,也就是说,我们不关心内存中的这个区域是单整数上的数组,我们现在将它视为无符号。这基本上把所有关于强类型的想法都抛到了窗外

我可能会尝试使用惰性序列。例如,检查是否可以使用类似以下内容来馈送
process()
方法:

let convertedBuffer = lazy(buffer).map() {
    UInt8($0)
}

process(convertedBuffer)

这至少可以为您节省额外的内存开销(否则您将不得不保留2个阵列),并可能为您节省一些性能(由于懒惰)。

我大体上同意您应该坚持使用
map
,但是,如果您的阵列确实很大,仅仅为了转换到相同的位模式而创建一个完整的第二个缓冲区确实很痛苦,您可以这样做:

// first, change your process logic to be generic on any kind of container
func process<C: CollectionType where C.Generator.Element == UInt8>(chars: C) {
    // just to prove it's working...
    print(String(chars.map { UnicodeScalar($0) }))
}

// sample input
let a: [Int8] = [104, 101, 108, 108, 111]  // ascii "Hello"

// access the underlying raw buffer as a pointer
a.withUnsafeBufferPointer { buf -> Void in
    process(
        UnsafeBufferPointer(
            // cast the underlying pointer to the type you want
            start: UnsafePointer(buf.baseAddress), 
            count: buf.count))
}
// this prints [h, e, l, l, o]

还值得注意的是,这些解决方案与
a.map{UInt8($0)}
不同,因为如果传递负整数,后者将在运行时陷入陷阱。如果这是一种可能性,您可能需要先过滤它们

“没有必要使用像map这样的函数”,所以你问“最佳方式”,但你说这种方式“没有必要”?你把答案排除在外,使你自己的问题变得毫无意义。耶,对。。。我只是想找到如何强制转换缓冲区,而不是处理每个字节,从而消耗额外的CPU开销。如果您有缓冲区,则可以使用
和unsafebufferpointer
@AirspeedVelocity合法地访问阵列的底层缓冲区,我担心您会遇到这种情况。:)@你认为我应该删除我的答案吗?我不想误导任何人。呃,你说的“施法”毫无意义是对的,我只想删除无缓冲区评论。至少你没有把人们引入不安全的土地:)@AirspeedVelocity是的,我更愿意让你这么做!:“”(“”)谢谢你的提示和想法。总的来说,我同意无论是签字还是未签字都要注意这一点。但我不确定字符和二进制数据是有符号的还是无符号的有多重要:-)无论如何,我的极端大小写
unsafeBitCast
应该足以将utf8字符串传递给只接受[UInt8]的第三方库。多亏了
lazy
。它在Swift1.2上运行,但在beta 3下的SWIFT2上编译时出错,我想知道为什么…奇怪。我在XCode 7 beta 3中试用了该代码。你的代码是什么?错误是什么?也许值得再问一个问题。
process(unsafeBitCast(a, [UInt8].self))