在现阶段Swift
的编码中,我们还是有很多场景需要调用一些C
函数。在Swift
与C
的混编中,经常遇到的一个问题就是需要在两者中互相转换字符串。在C
语言中,字符串通常是用一个char数组
来表示,在Swift
中,是用CChar数组
来表示。从CChar
的定义可以看到,其实际上是一个Int8
类型,如下所示:
1 | /// The C 'char' type. |
如果我们想将一个String
转换成一个CChar数组
,则可以使用String
的cStringUsingEncoding
方法,它是String
扩展中的一个方法,其声明如下:
1 | /// Returns a representation of the `String` as a C string |
参数指定的是编码格式,我们一般指定为NSUTF8StringEncoding
,因此下面这段代码:
1 | let str: String = "abc1个" |
其输出结果是:
1 | [97, 98, 99, 49, -28, -72, -86, 0] |
可以看到"个"
字由三个字节表示,这是因为Swift
的字符串是Unicode
编码格式,一个字符可能由1个或多个字节组成。另外需要注意的是CChar
数组的最后一个元素是0
,它表示的是一个字符串结束标志符\n
。
我们知道,在C
语言中,一个数组还可以使用指针来表示,所以字符串也可以用char *
来表示。在Swift中
,指针是使用UnsafePointer
或UnsafeMutablePointer
来包装的,因此,char指针
可以表示为UnsafePointer<CChar>
,不过它与[CChar]
是两个不同的类型,所以以下代码会报编译器错误:
1 | // Error: Cannot convert value of type '[CChar]' to specified type 'UnsafePointer<CChar>' |
不过有意思的是我们可以直接将String
字符串传递给带有UnsafePointer<CChar>
参数的函数或方法,如以下代码所示:
1 | func length(s: UnsafePointer<CChar>) { |
而String
字符串却不能传递给带有[CChar]
参数的函数或方法,如以下代码会报错误:
1 | func length2(s: [CChar]) { |
实际上,在C
语言中,我们在使用数组参数时,很少以数组的形式来定义参数,则大多是通过指针方式来定义数组参数。
如果想从[CChar]
数组中获取一上String
字符串,则可以使用String
的fromCString
方法,其声明如下:
1 | /// Creates a new `String` by copying the nul-terminated UTF-8 data |
从注释可以看到,它会将UTF-8
数据拷贝以新字符串中。如下示例:
1 | let chars: [CChar] = [99, 100, 101, 0] |
这里需要注意的一个问题是,CChar
数组必须以0
结束,否则会有不可预料的结果。在我的Playground
示例代码中,如果没有0
,报了以下错误:
1 | Execution was interrupted. reason: EXC_BAD_INSTRUCTION |
还有可能出现的情况是CChar
数组的存储区域正好覆盖了之前某一对象的区域,这一对象有一个可以表示字符串结尾的标识位,则这时候,str2
输出的可能是"cde1一"
。
小结
在Swift
中,String
是由独立编码的Unicode
字符组成的,即Character
。一个Character
可能包括一个或多个字节。所以将String
字符串转换成C
语言的char *
时,数组元素的个数与String
字符的个数不一定相同(即在Swift
中,与str.characters.count
计算出来的值不一定相等)。这一点需要注意。另外还需要注意的就是将CChar
数组转换为String
时,数组最后一个元素应当为字符串结束标志符,即0
。