将结构体内的C字符串转换为Swift字符串

时间:2014-06-24 01:01:58

标签: struct tuples swift

我在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字符串的方法。

8 个答案:

答案 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)!
            }
        }
    }
}