LoginSignup
11
8

More than 5 years have passed since last update.

SwiftのStringからCの文字列を生成するベスト・プラクティス

Last updated at Posted at 2018-07-12
検証環境
Apple Swift version 4.1 (swiftlang-902.0.48 clang-902.0.37.1)
Target: x86_64-apple-darwin17.6.0

TL;DR;

SwiftのStringからC用の文字列を生成するには、utf8CString プロパティから生成するのがベスト・プラクティスだと思われます。

SwiftにおけるC文字列を表す型

SwiftにてUTF8用のCの文字列を表す型は以下があります。

  • UnsafePointer<CChar>
  • UnsafeMutablePointer<CChar>
  • UnsafePointer<UInt8>
  • UnsafeMutablePointer<UInt8>

Note: CChar = Int8

Foundationを使わずに、上記の型を生成するためのベスト・プラクティスを考えてみました。

StringからC文字列への変換に使用するプロパティ

Cの文字列には終端を意味する null文字 を最後につける必要があります。

しかしながら、 utf8 プロパティで生成される String.UTF8View の要素内には、null文字が含まれません。
なのでnull終端文字が含まれる utf8CString プロパティを使用します。

utf8

utf8.swift
let s = "Hello!"
let bytes = Array(s.utf8)
print(bytes) 
// Prints "[72, 101, 108, 108, 111, 33]"

utf8CString

utf8cstring.swift
let s = "Hello!"
let bytes = s.utf8CString
print(bytes)
// Prints "[72, 101, 108, 108, 111, 33, 0]" 最後にnull終端文字の0が含まれる

String to cString

UnsafeMutablePointer<CChar> の生成

utf8CString プロパティで生成される配列の要素の型が CChar なので、配列をそのままUnsafePointerに変換して生成します。

Note: CChar = Int8

string2cstring_int8.swift
func makeCString(from str: String) -> UnsafeMutablePointer<Int8> {
    let count = str.utf8CString.count 
    let result: UnsafeMutableBufferPointer<Int8> = UnsafeMutableBufferPointer<Int8>.allocate(capacity: count)
    // func initialize<S>(from: S) -> (S.Iterator, UnsafeMutableBufferPointer<Element>.Index)
    _ = result.initialize(from: str.utf8CString)
    return result.baseAddress!
}

// EXAMPLE
let str = "朝日がさんさん おはよ〜お〜さ〜ん 🌟 Good morning everyone! 👪"
let cstr = makeCString(from: str)
let restr = String(cString: cstr)
print(str == restr) // true

UnsafeMutablePointer<UInt8> の生成

utf8cstring で生成される配列の要素の型が CChar なので、 withMemoryRebound メソッドを使って、要素の中身を UInt8 にキャストします。

Note: CChar = Int8

string2cstring_uint8.swift
func makeCString(from str: String) -> UnsafeMutablePointer<UInt8> {
    let utf8 = str.utf8CString
    let count = utf8.count 

    let result = UnsafeMutableBufferPointer<UInt8>.allocate(capacity: count)
    _ = utf8.withUnsafeBufferPointer { baseAddress in

        // 要素の中身を CChar から UInt8 にキャストする 
        baseAddress.withMemoryRebound(to: UInt8.self) { baseAddress2 in

            // baseAddress2: UnsafeMutableBufferPointer<UInt8> 
            result.initialize(from: baseAddress2)
        }
    }
    return result.baseAddress!
}

Appendix utf8プロパティからの生成

utf8 プロパティからC文字列を生成する場合は、null文字を追加すれば問題ありません。

string2cstring.swift
// UnsafeMutablePointer<UInt8>
func makeCString(from str: String) -> UnsafeMutablePointer<UInt8> {
    var utf8 = Array(str.utf8)
    utf8.append(0)  // adds null character
    let count = utf8.count
    let result = UnsafeMutableBufferPointer<UInt8>.allocate(capacity: count)
    _ = result.initialize(from: utf8)
    return result.baseAddress!
}

// UnsafeMutablePointer<Int8>
func makeCString(from str: String) -> UnsafeMutablePointer<Int8> {
    var utf8 = Array(str.utf8)
    utf8.append(0)  // adds null character
    let count = utf8.count
    let result = UnsafeMutableBufferPointer<Int8>.allocate(capacity: count)
    utf8.withUnsafeBufferPointer { baseAddress in
        baseAddress.withMemoryRebound(to: Int8.self) { (baseAddress2) in
            _ = result.initialize(from: baseAddress2)
        }
    }
    return result.baseAddress!
}
11
8
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
8