我在Swift应用程序中使用现有的C库并尝试将C字符缓冲区转换为Swift String。
Bridging.h
typedef struct { char mfg[8]; char model[8]; } motorcycle;
void GetMotorcycle(motorcycle *m);
Example.swift
var cycle = motorcycle(mfg: (0,0,0,0,0,0,0,0), model: (0,0,0,0,0,0,0,0));
GetMotorcycle(&cycle)
var manufacturer : String = String.fromCString(cycle.mfg) // Error
这会产生"无法找到来自CString'的过载。接受提供的参数"
由于Swift将C字符数组视为元组,我无法找到将其转换为Swift字符串的方法。
答案 0 :(得分:3)
好吧,至少那个C字符数组只有8个字符长,因为目前没有办法迭代元组。以下是转换它的方法:
func char8ToString(tuple: (CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar)) -> String {
let arr: unichar[] = [unichar(tuple.0), unichar(tuple.1),
unichar(tuple.2), unichar(tuple.3),
unichar(tuple.4), unichar(tuple.5),
unichar(tuple.6), unichar(tuple.7)]
let len = arr.reduce(0) { $1 != 0 ? $0 + 1 : $0 }
return NSString(characters: arr, length: len)
}
var manufacturer: String = char8ToString(cycle.mfg)
希望我们能够获得语言支持,以便更快地处理此案例!
答案 1 :(得分:1)
过去几天我一直在同一条船上。我最终提出了一个通用方案来处理来自C API的C结构中的C数组,如果必须处理大数组,那就好了,但它必须使用“unsafeBitCast()”。此外,它必须适用于每个固定的C阵列。这是回答当前问题的适应性(代码已经过测试并且正在运行):
// This is our C array in this case char[8]
typealias CArray8Chars = (
CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar
)
let zero8Chars: CArray8Chars = (
0, 0, 0, 0, 0, 0, 0, 0
)
struct CString8 {
private var value: CArray8Chars = zero8Chars
var asString: String {
mutating get {
var cString = [CChar]()
for i in 0..<sizeofValue(value) {
cString += [self[i]]
if self[i] == 0 {
break
}
}
return String.fromCStringRepairingIllFormedUTF8(&cString).0!
}
set {
let cchars = Array(newValue.utf8).map { CChar(Int8(bitPattern: UInt8($0))) }
for i in 0..<min(cchars.count,sizeof(CArray8Chars))-1 { self[i] = cchars[i] }
self[min(cchars.count,sizeof(CArray8Chars))-1] = 0
}
}
subscript(i: Int) -> CChar {
mutating get {
let bytePtr = withUnsafePointer(&value, { (ptr) -> UnsafePointer<CChar> in
return unsafeBitCast(ptr, UnsafePointer<CChar>.self)
})
return bytePtr[i]
}
set {
let bytePtr = withUnsafeMutablePointer(&value, { (ptr) -> UnsafeMutablePointer<CChar> in
return unsafeBitCast(ptr, UnsafeMutablePointer<CChar>.self)
})
bytePtr[i] = newValue
}
}
}
现在在摩托车C struct的情况下,它将如下:
struct MotorCycle {
var mfg = CString8()
var model = CString8()
}
var cycle = MotorCycle()
GetMotorcycle(&cycle)
var manufacturer = cycle.mfg.asString // Since the string comes from C it works fine
这也允许使用Unicode字符串设置mfg / model,但需要注意:
// "" utf8 encoding makes the C string too big, so it gets cut. What's left represents an unknown char
cycle.model.asString = "I do Motor Bikes"
println(cycle.model.asString)
并使元组可转换:
println(cycle.mfg[2])
cycle.model[2] = 32 // space. They are CChars, thus Int8
如果有人喜欢CString8可以进一步扩展,允许获取/设置子串等。但这超出了这个问题的范围。
答案 2 :(得分:1)
Swift的内置reflect()
和MirrorType
非常整洁,允许我们检查元组,确定其长度,并迭代其内容。
let tuple = (100, 120, 49, 100)
let mirror = reflect(tuple)
var string = String()
for index in 0..<mirror.count {
let value = mirror[index].1.value as! Int
let character = UnicodeScalar(value)
string.append(character)
}
println(string) // prints "dx1d"
答案 3 :(得分:1)
迅捷3:
String(cString: UnsafeRawPointer([myTuple]).assumingMemoryBound(to: CChar.self))
答案 4 :(得分:0)
let motorcycleStringFieldLength = 8
var characterArray = [CChar](count: motorcycleStringFieldLength + 1, repeatedValue:0) // make sure we always are null-terminated!
bcopy(&cycle.model, &characterArray, motorcycleStringFieldLength)
node.name = String.fromCString(characterArray)
对于'model'或'mfg'正好是8个字符且不是以null结尾的情况,这是安全的,并且适用于不同长度的struct数组。
答案 5 :(得分:0)
这是一个适用于swift 2.1的类型安全解决方案。
SELECT DISTINCT model FROM cars
但是,对于较大的数组来说这是非常慢的,因为它需要检查每个字符的类型,因为编译器无法知道传入的元组是否都是相同的类型。
答案 6 :(得分:0)
以下是 Swift 3 和 Swift 4 支持的解决方案。
linedelimiter
或者,如果你想在行内使用带有字节元组的函数,试试这个。
dask.bag.text
答案 7 :(得分:0)
Swift 5.2 —此解决方案可以接受任意长度的元组,不执行任何额外的复制操作,并使用最新的约定进行安全的内存绑定。
extension String {
init<T>(tupleOfCChars: T, length: Int = Int.max) {
self = withUnsafePointer(to: tupleOfCChars) {
let lengthOfTuple = MemoryLayout<T>.size / MemoryLayout<CChar>.size
return $0.withMemoryRebound(to: UInt8.self, capacity: lengthOfTuple) {
String(bytes: UnsafeBufferPointer(start: $0, count: Swift.min(length, lengthOfTuple)), encoding: .utf8)!
}
}
}
}