usar resueltos nombres leer funciones ejercicios ejemplos como caracteres cadenas cadena arreglo almacenar arrays swift interop tuples

arrays - resueltos - Convertir una matriz de caracteres C en una cadena



funciones de cadenas de caracteres en c++ (8)

Tengo un programa 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 (tipeados (Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8) ), que no tengo idea de cómo transformar en una String con Swift.

No hay un inicializador de String que acepte una tupla Int8 , y no parece posible obtener un puntero al primer elemento de la tupla (dado 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, e ir con eso.

¿Existe, sin embargo, una forma pura y rápida de hacerlo?


Detalles

  • Xcode 11.2.1 (11B500), Swift 5.1

Solución

extension String { init?(fromTuple value: Any) { guard let string = Tuple(value).toString() else { return nil } self = string } init?(cString: UnsafeMutablePointer<Int8>?) { guard let cString = cString else { return nil } self = String(cString: cString) } init?(cString: UnsafeMutablePointer<CUnsignedChar>?) { guard let cString = cString else { return nil } self = String(cString: cString) } init? (cString: Any) { if let pointer = cString as? UnsafeMutablePointer<CChar> { self = String(cString: pointer) return } if let pointer = cString as? UnsafeMutablePointer<CUnsignedChar> { self = String(cString: pointer) return } if let string = String(fromTuple: cString) { self = string return } return nil } } // https://.com/a/58869882/4488252 struct Tuple<T> { let original: T private let array: [Mirror.Child] init(_ value: T) { self.original = value array = Array(Mirror(reflecting: original).children) } func compactMap<V>(_ transform: (Mirror.Child) -> V?) -> [V] { array.compactMap(transform) } func toString() -> String? { let chars = compactMap { (_, value) -> String? in var scalar: Unicode.Scalar! switch value { case is CUnsignedChar: scalar = .init(value as! CUnsignedChar) case is CChar: scalar = .init(UInt8(value as! CChar)) default: break } guard let _scalar = scalar else { return nil } return String(_scalar) } if chars.isEmpty && !array.isEmpty { return nil } return chars.joined() } }

Uso (muestra completa)

Código en lenguaje C (Encabezado.h)

#ifndef Header_h #define Header_h #ifdef __cplusplus extern "C" { #endif char c_str1[] = "Hello world!"; char c_str2[50] = "Hello world!"; char *c_str3 = c_str2; typedef unsigned char UTF8CHAR; UTF8CHAR c_str4[] = {72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 32, 0}; UTF8CHAR *c_str5 = c_str4; UTF8CHAR c_str6[] = {''H'', ''e'', ''l'', ''l'', ''o'', '' '', ''w'', ''o'', ''r'', ''l'', ''d'', ''!'', ''/0''}; UTF8CHAR *c_str7 = 0; UTF8CHAR *c_str8 = ""; #define UI BYTE #ifdef __cplusplus } #endif #endif /* Header_h */

...- Bridging-Header.h

#include "Header.h"

Código SWIFT

func test() { printInfo(c_str1) printInfo(c_str2) printInfo(c_str3) printInfo(c_str4) printInfo(c_str5) printInfo(c_str6) printInfo(c_str7) printInfo(c_str8) print(String(fromTuple: c_str1) as Any) print(String(fromTuple: c_str2) as Any) print(String(cString: c_str3) as Any) print(String(fromTuple: c_str4) as Any) print(String(cString: c_str5) as Any) print(String(fromTuple: c_str6) as Any) print(String(fromTuple: c_str7) as Any) print(String(cString: c_str8) as Any) } var counter = 1; func printInfo(_ value: Any?) { print("name: str_/(counter)") counter += 1 guard let value = value else { return } print("type: /(type(of: value))") print("value: /(value)") print("swift string: /(String(cString: value))") print("/n-----------------") }

Salida

name: str_1 type: (Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8) value: (72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33, 0) swift string: Optional("Hello world!/0") ----------------- name: str_2 type: (Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8) value: (72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) swift string: Optional("Hello world!/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0") ----------------- name: str_3 type: UnsafeMutablePointer<Int8> value: 0x000000010e8c5d40 swift string: Optional("Hello world!") ----------------- name: str_4 type: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) value: (72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 32, 0) swift string: Optional("Hello world /0") ----------------- name: str_5 type: UnsafeMutablePointer<UInt8> value: 0x000000010e8c5d80 swift string: Optional("Hello world ") ----------------- name: str_6 type: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) value: (72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33, 0) swift string: Optional("Hello world!/0") ----------------- name: str_7 name: str_8 type: UnsafeMutablePointer<UInt8> value: 0x000000010e8c0ae0 swift string: Optional("") ----------------- Optional("Hello world!/0") Optional("Hello world!/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0") Optional("Hello world!") Optional("Hello world /0") Optional("Hello world ") Optional("Hello world!/0") Optional("") Optional("")


Acabo de experimentar un problema similar con Swift 3. (3.0.2). Intentaba convertir una Matriz de CChar, [CChar] en una Cadena en Swift. Resulta que Swift 3 tiene un inicializador de String 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 String da como resultado un Opcional. Debe desenvolverse a la fuerza cuando se usa en la función String.init que crea b. Y b también es Opcional ... lo que significa que ambos podrían terminar siendo nulos, por lo que también se debe usar la verificación de errores.


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

func arrayForTuple<T,E>(tuple:T) -> [E] { let reflection = reflect(tuple) var arr : [E] = [] for i in 0..<reflection.count { if let value = reflection[i].1.value as? E { arr.append(value) } } return arr } public extension String { public static func fromTuple<T>(tuple:T) -> String? { var charArray = arrayForTuple(tuple) as [Int8] var nameString = String.fromCString(UnsafePointer<CChar>(charArray)) if nameString == nil { nameString = String.fromCStringRepairingIllFormedUTF8(UnsafePointer<CChar>(charArray)).0 } return nameString } }


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 memoria de las estructuras importadas de C, según lo confirma 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 en un puntero UInt8 , al inicializador de 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 UTF-8 válida terminada en NUL.


En realidad, puede recopilar una tupla en una matriz utilizando la sintaxis de parámetros variables 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")


Estoy interesado en resolver esto también 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)")

la salida resultante es:

testSwiftString = prueba


Prueba esto:

func asciiCStringToSwiftString(cString:UnsafePointer<UInt8>, 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 }


Swift 3. Solo usa la reflexión. Esta versión deja de construir 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 }