Conversión de una matriz C char en una cadena

Tengo un progtwig Swift que interopera con una biblioteca C. Esta biblioteca C devuelve una estructura con una matriz char[] dentro, como esta:

 struct record { char name[8]; }; 

La definición se importa correctamente en Swift. Sin embargo, el campo se interpreta como una tupla de 8 elementos Int8 (typescripts (Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8) ), que no tengo idea de cómo transformarme en una String con Swift.

No hay ningún inicializador de String que acepte una tupla Int8 , y no parece posible obtener un puntero al primer elemento de la tupla (ya que los tipos pueden ser heterogéneos, eso no es realmente sorprendente).

En este momento, mi mejor idea es crear una pequeña función C que acepte un puntero a la estructura misma y devolver el name como un puntero char* lugar de una matriz, y vaya con eso.

¿Existe, sin embargo, una forma pura de Swift para hacerlo?

El char name[8] matriz C char name[8] se importa a Swift como una tupla:

 (Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8) 

La dirección del name es la misma que la dirección del name[0] , y Swift conserva el diseño de la memoria de las estructuras importadas desde C, como confirmó el ingeniero de Apple Joe Groff:

… Puede dejar la estructura definida en C e importarla a Swift. Swift respetará el diseño de C.

Como consecuencia, podemos pasar la dirección de record.name , convertida a un puntero UInt8 , al inicializador String:

 var record = someFunctionReturningAStructRecord() // Swift 2: let name = withUnsafePointer(&record.name) { String.fromCString(UnsafePointer($0))! } // Swift 3: let name = withUnsafePointer(to: &record.name) { $0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout.size(ofValue: record.name)) { String(cString: $0) } } 

NOTA: Se supone que los bytes en el name[] son una secuencia válida UTF-8 terminada en NUL.

De hecho, puede recostackr una tupla en una matriz mediante la syntax de parámetros variadic de Swift:

 let record = getRecord() let (int8s: Int8...) = myRecord // int8s is an [Int8] let uint8s = int8s.map { UInt8($0) } let string = String(bytes: uint8s, encoding: NSASCIIStringEncoding) // myString == Optional("12345678") 

También estoy interesado en resolver esto para mis propios fines, así que agregué una nueva función:

 func asciiCArrayToSwiftString(cString:Int8...) -> String { var swiftString = String() // The Swift String to be Returned is Intialized to an Empty String var workingCharacter:UnicodeScalar = UnicodeScalar(UInt8(cString[0])) var count:Int = cString.count for var i:Int = 0; i < count; i++ { workingCharacter = UnicodeScalar(UInt8(cString[i])) // Convert the Int8 Character to a Unicode Scalar swiftString.append(workingCharacter) // Append the Unicode Scalar } return swiftString // Return the Swift String } 

Llamo a esta función con:

  let t:Int8 = Int8(116) let e:Int8 = Int8(101) let s:Int8 = Int8(115) let testCString = (t, e, s, t) let testSwiftString = wispStringConverter.asciiCArrayToSwiftString(testCString.0, testCString.1, testCString.2, testCString.3) println("testSwiftString = \(testSwiftString)") 

el resultado resultante es:

TestSwiftString = prueba

Prueba esto:

 func asciiCStringToSwiftString(cString:UnsafePointer, maxLength:Int) -> String { var swiftString = String() // The Swift String to be Returned is Intialized to an Empty String var workingCharacter:UnicodeScalar = UnicodeScalar(cString[0]) var count:Int = 0 // An Index Into the C String Array Starting With the First Character while cString[count] != 0 // While We Haven't reached the End of the String { workingCharacter = UnicodeScalar(cString[count]) // Convert the ASCII Character to a Unicode Scalar swiftString.append(workingCharacter) // Append the Unicode Scalar Version of the ASCII Character count++ // Increment the Index to Look at the Next ASCII Character if count > maxLength // Set a Limit In Case the C string was Not NULL Terminated { if printDebugLogs == true { swiftString="Reached String Length Limit in Converting ASCII C String To Swift String" } return swiftString } } return swiftString // Return the Swift String } 

Aquí hay una solución que se me ocurrió que utiliza la reflexión para convertir realmente la tupla en un [Int8] (ver ¿Hay alguna forma de iterar una tupla en swift? ), Y luego la convierte en una cadena utilizando los métodos fromCString … ().

 func arrayForTuple(tuple:T) -> [E] { let reflection = reflect(tuple) var arr : [E] = [] for i in 0..(tuple:T) -> String? { var charArray = arrayForTuple(tuple) as [Int8] var nameString = String.fromCString(UnsafePointer(charArray)) if nameString == nil { nameString = String.fromCStringRepairingIllFormedUTF8(UnsafePointer(charArray)).0 } return nameString } } 

Swift 3. Solo usa reflexión. Esta versión detiene la construcción de la cadena cuando encuentra un byte nulo. Probado.

 func TupleOfInt8sToString( _ tupleOfInt8s:Any ) -> String? { var result:String? = nil let mirror = Mirror(reflecting: tupleOfInt8s) for child in mirror.children { guard let characterValue = child.value as? Int8, characterValue != 0 else { break } if result == nil { result = String() } result?.append(Character(UnicodeScalar(UInt8(characterValue)))) } return result } 

Acabo de experimentar un problema similar con Swift 3. (3.0.2). Estaba intentando convertir una matriz de CChar, [CChar] en una cadena en Swift. Resulta que Swift 3 tiene un inicializador de cadena que tomará un cString.

Ejemplo:

 let a = "abc".cString(using: .utf8) // type of a is [CChar] let b = String(cString: a!, encoding: .utf8) // type of b is String print("a = \(a)") print("b = \(b)") 

resultados en

a = Opcional ([97, 98, 99, 0])

b = Opcional (“abc”)

Tenga en cuenta que la función cString en Cadena da como resultado un Opcional. Se debe desenrollar por la fuerza cuando se usa en la función String.init creando b. Y b también es opcional … lo que significa que ambos podrían terminar siendo nulos, por lo que también se debería usar la verificación de errores.