Arrays 将C字符数组转换为字符串
我有一个Swift程序,可以与C库进行互操作。此C库返回一个内部带有Arrays 将C字符数组转换为字符串,arrays,swift,interop,tuples,Arrays,Swift,Interop,Tuples,我有一个Swift程序,可以与C库进行互操作。此C库返回一个内部带有char[]数组的结构,如下所示: struct record { char name[8]; }; 定义已正确导入Swift。但是,该字段被解释为8个Int8元素的元组(键入(Int8,Int8,Int8,Int8,Int8,Int8)),我不知道如何使用Swift将其转换为字符串 没有接受Int8元组的String初始值设定项,而且似乎不可能获得指向元组第一个元素的指针(因为类型可能是异构的,这并不奇怪) 现在,我
char[]
数组的结构,如下所示:
struct record
{
char name[8];
};
定义已正确导入Swift。但是,该字段被解释为8个Int8
元素的元组(键入(Int8,Int8,Int8,Int8,Int8,Int8)
),我不知道如何使用Swift将其转换为字符串
没有接受Int8
元组的String
初始值设定项,而且似乎不可能获得指向元组第一个元素的指针(因为类型可能是异构的,这并不奇怪)
现在,我的最佳想法是创建一个小型C函数,它接受指向结构本身的指针,并将name
作为char*
指针而不是数组返回,然后执行该操作
然而,有没有一种纯粹快捷的方法可以做到这一点呢?试试这个:
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
}
func ascicstringtoswiftstring(cString:UnsafePointer,maxLength:Int)->String
{
var swiftString=String()//要返回的Swift字符串被初始化为空字符串
变量工作字符:UnicodeScalar=UnicodeScalar(cString[0])
var count:Int=0//C字符串数组的索引,从第一个字符开始
当cString[count]!=0//尚未到达字符串末尾时
{
workingCharacter=UnicodeScalar(cString[count])//将ASCII字符转换为Unicode标量
append(workingCharacter)//追加ASCII字符的Unicode标量版本
count++//递增索引以查看下一个ASCII字符
如果count>maxLength//设置一个限制,以防C字符串不是以NULL结尾的
{
如果printDebugLogs==true
{
swiftString=“将ASCII C字符串转换为Swift字符串时已达到字符串长度限制”
}
返回快捷键
}
}
return swiftString//返回Swift字符串
}
您可以使用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")
C数组字符名[8]
作为元组导入Swift:
(Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8)
name
的地址与name[0]
的地址相同,并且
Swift保留从C导入的结构的内存布局,如下所示
。。。您可以保留C中定义的结构并将其导入Swift。斯威夫特将尊重C的布局
因此,我们可以传递record.name
的地址,
转换为UInt8
指针,以
字符串初始值设定项。以下代码已为Swift 4.2及更高版本更新:
let record = someFunctionReturningAStructRecord()
let name = withUnsafePointer(to: record.name) {
$0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout.size(ofValue: $0)) {
String(cString: $0)
}
}
注意:假定name[]
中的字节是有效的以NUL结尾的UTF-8序列
对于较旧版本的Swift:
我也有兴趣为自己的目的解决这个问题,所以我添加了一个新函数:
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
}
结果是:
testSwiftString=test我提出了一个解决方案,它使用反射将元组实际转换为[Int8](请参阅),然后使用fromCString…()方法将其转换为字符串
func数组四元组(元组:T)->[E]{
让反射=反射(元组)
变量arr:[E]=[]
对于0..String中的i{
var charArray=arrayForTuple(元组)作为[Int8]
var nameString=String.fromCString(UnsafePointer(charArray))
如果名称字符串==nil{
nameString=String.fromCStringRepairingIllFormedUTF8(未安全指针(charArray)).0
}
返回名称字符串
}
}
Swift 3。只使用反射。此版本在遇到空字节时停止构建字符串。测试
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
}
我刚刚在使用Swift 3时遇到了类似的问题。(3.0.2). 我试图在Swift中将CChar数组[CChar]转换为字符串。事实证明,Swift 3有一个字符串初始值设定项,它将采用cString
例如:
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)")
导致
a=可选([97,98,99,0])
b=可选(“abc”)
请注意,字符串上的cString函数会产生一个可选的字符串。在创建b的String.init函数中使用时,必须强制展开。b也是可选的。。。这意味着两者最终都可能为零,因此也应该使用错误检查。详细信息
- 代码11.2.1(11B500),Swift 5.1
解决方案
…桥接头.h
Swift代码
输出
名称:str_1
类型:(Int8,Int8,Int8,Int8,Int8,Int8,Int8,Int8,Int8,Int8,Int8,Int8,Int8,Int8)
值:(72、101、108、108、111、32、119、111、114、108、100、33、0)
swift字符串:可选(“Hello world!\0”)
-----------------
姓名:stru 2
类型:(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)
值:(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、0)
swift字符串:可选(“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”)
-----------------
姓名:str_3
类型:UnsafeMutablePointer
值:0x000000010e8c5d40
swift字符串:可选(“Hello world!”)
-----------------
姓名:str_4
类型:(UInt8,UInt8,UInt8,UInt8,UInt8,UInt8,UInt8,UInt8,UInt8,UInt8,UInt8,UInt8,UInt8)
值:(72、101、108、108、111、32、119、111、114、108、100、32、0)
swift字符串:可选(“Hello world\0”)
-----------------
姓名:str_5
类型:UnsafeMutablePointer
值:0x000000010e8c5d80
swift字符串:可选(“Hello world”)
-----------------
姓名:str_6
类型
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
}
}
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
}
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)")
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://stackoverflow.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()
}
}
#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 */
#include "Header.h"
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-----------------")
}
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("")