At this stage of Swift's coding, we still have many scenarios where we need to invoke some C functions. One of the frequently encountered problems in the combination of Swift and C is the need to convert strings to each other. In the C language, strings are usually represented by a char array, which is represented by the CChar array in Swift. As you can see from the definition of CChar, it is actually a Int8 type, as follows:
12345 |
/// The C ‘char‘ type. /// /// This will be the same as either `CSignedChar` (in the common /// case) or `CUnsignedChar`, depending on the platform. public typealias CChar = Int8 |
If we want to convert a string to a CChar array, you can use the Cstringusingencoding method of string, which is a method in the string extension, which is declared as follows:
1234 |
/// Returns a representation of the `String` as a C string /// using a given encoding. @warn_unused_result public func cStringUsingEncoding(encoding: NSStringEncoding) -> [CChar]? |
The parameter specifies the encoding format, which we typically specify as nsutf8stringencoding, so the following code:
123 |
let str: String = "abc1个" // String转换为CChar数组 let charArray: [CChar] = str.cStringUsingEncoding(NSUTF8StringEncoding)! |
The output is:
1 |
[97, 98, 99, 49, -28, -72, -86, 0] |
You can see that the word "single" is represented by three bytes, because Swift's string is Unicode encoded, and one character may consist of one or more bytes. It is also important to note that the last element of the CChar array is 0, which represents a string end identifier \ n.
We know that in C, an array can also be represented by a pointer, so the string can also be represented by a char *. In swift, pointers are wrapped using unsafepointer or unsafemutablepointer, so a char pointer can be represented as unsafepointer, but it is two different types than [CChar]. So the following code will report a compiler error:
12 |
// Error: Cannot convert value of type ‘[CChar]‘ to specified type ‘UnsafePointer‘ let charArray2: UnsafePointer = str.cStringUsingEncoding(NSUTF8StringEncoding)! |
But the interesting thing is that we can pass string strings directly to a function or method with the Unsafepointer parameter, as shown in the following code:
12345 |
func length(s: UnsafePointer) { print(strlen(s)) } length(str) // 输出:7\n |
String strings cannot be passed to a function or method with a [CChar] argument, as the following code will report an error:
12345 |
func length2(s: [CChar]) { print(strlen(s)) } // Error: Cannot convert value of type ‘String‘ to expected argument type ‘[CChar]‘ length2(str) |
In fact, in the C language, when we use array parameters, we seldom define the parameters in the form of arrays, most of which are pointers to the array parameters.
If you want to get a string string from the [CChar] array, you can use the Fromcstring method of string, which is declared as follows:
1234567 |
/// Creates a new `String` by copying the nul-terminated UTF-8 data /// referenced by a `CString`. /// /// Returns `nil` if the `CString` is `NULL` or if it contains ill-formed /// UTF-8 code unit sequences. @warn_unused_result public static func fromCString(cs: UnsafePointer) -> String? |
As you can see from the note, it copies the UTF-8 data into a new string. The following example:
123 |
let chars: [CChar] = [99, 100, 101, 0] let str2: String = String.fromCString(chars)! // 输出:cde |
One problem to note here is that the CChar array must end with 0, otherwise there will be unpredictable results. In my playground sample code, if there is no 0, the following error is reported:
1 |
Execution was interrupted. reason: EXC_BAD_INSTRUCTION |
It is also possible that the storage area of the CChar array covers exactly the area of the previous object, which has an identity bit that can represent the end of the string, then the STR2 output may be "cde1 one".
Summary
In Swift, a string is made up of independently encoded Unicode characters, that is, character. A character may include one or more bytes. So when converting string strings to char * in C, the number of array elements is not necessarily the same as the number of string characters (that is, in swift, the values computed from Str.characters.count are not necessarily equal). This is a point to note. It is also important to note that when converting a CChar array to a string, the last element of the array should be the end-of-string marker, which is 0.
Conversion of String to CChar array in Swift