我有一个与C库互操作的Swift程序。这个C库返回一个内部带有char[]
数组的结构,如下所示:
struct record
{
char name[8];
};
将定义正确导入Swift。但是,该字段被解释为8个Int8
元素的元组(类型为(Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8)
),我不知道如何使用Swift转换为String
没有String
初始值设定项接受Int8
元组,并且似乎无法获得指向元组的第一个元素的指针(因为类型可能是异构的,这不是真的令人惊讶)。
现在,我最好的想法是创建一个微小的C函数,它接受指向结构本身的指针并返回name
作为char*
指针而不是数组,然后继续使用它。 / p>
然而,是否有纯粹的Swift方式呢?
答案 0 :(得分:26)
C数组char name[8]
作为元组导入Swift:
(Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8)
name
的地址与name[0]
的地址相同,并且
Swift 保留从C导入的结构的内存布局,如
confirmed by Apple engineer Joe Groff:
...您可以保留在C中定义的结构并将其导入Swift。斯威夫特将尊重C的布局。
因此,我们可以传递record.name
的地址,
转换为UInt8
指针,转换为
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)
}
}
注意:假设name[]
中的字节是有效的NUL终止的UTF-8序列。
答案 1 :(得分:3)
您可以使用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")
答案 2 :(得分:2)
我也有兴趣为自己的目的而努力,所以我添加了一个新功能:
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
}
我用以下方法调用此函数:
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)")
结果输出为:
testSwiftString = test
答案 3 :(得分:2)
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
}
答案 4 :(得分:2)
我刚刚使用Swift 3遇到过类似的问题。(3.0.2)。我试图将一个CChar数组,[CChar]转换为Swift中的String。事实证明,Swift 3有一个String初始化器,它将采用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 =可选(&#34; abc&#34;)
请注意,String上的cString函数会产生一个Optional。在String.init函数创建b时使用它必须强制解包。并且b也是可选的...意味着两者最终都可以为零,因此也应该使用错误检查。
答案 5 :(得分:1)
试试这个:
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
}
答案 6 :(得分:1)
这是我提出的一个解决方案,它使用反射实际将元组转换为[Int8](请参阅Any way to iterate a tuple in swift?),然后使用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
}
}
答案 7 :(得分:0)
...
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
'hash' => false,
],
'foo' => [
'driver' => 'session',
'provider' => 'foo',
],
],
...
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
'foo' => [
'driver' => 'foo', // Using a 3rd party for auth.
'model' => App\MyUser::class, // User model for auth.
],
// 'users' => [
// 'driver' => 'database',
// 'table' => 'users',
// ],
],
使用 C语言(Header.h)的代码
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()
}
}
...- Bridging-Header.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 */
快捷代码
#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-----------------")
}
答案 8 :(得分:0)
关于此主题已经有多个答案,但是没有一个答案是简单的一行,也不能解决非null终止的答案。
假设字符串NULL
被终止:
struct record {
char name[8];
};
//Might by unsafe, depends
String(cString: &record.name.0)
//Safe
String(cString: unsafeBitCast(UnsafePointer(&record.name), to: UnsafePointer<Int8>.self))
对于未NULL
终止的字符串:
//Might by unsafe, depends
String(cString: &record.name.0).prefix(MemoryLayout.size(ofValue: record.name))
//Safe
String(bytesNoCopy: UnsafeMutableRawPointer(mutating: &record.name), length: MemoryLayout.size(ofValue: record.name), encoding: .utf8, freeWhenDone: false)
––––
关于@MartinR仅传递一个字节的问题,您也可以传递指向整个变量的指针,但是就我个人而言,我从未经历过仅传递一个字节的快速操作,因此应该是安全的。