Swiftにおける「文字」に関する型がとても多い件について

## 三行まとめ

* Swiftの文字型には、Character / UnicodeScalar / UInt8 / UInt16 / CChar がある
* 第一の文字型であるCharacterは他の表現では複数の値になる可能性がある
* それぞれの型は暗黙の変換は原則としてできないし、明示的な変換も注意が必要

## 解説

```shell-session
$ xcrun swift --version
Swift version 1.0 (swift-600.0.51.3)
Target: x86_64-apple-darwin13.3.0
```

SwiftのStringはよく抽象化されていていいと思うのですが、それだけに概念がとても複雑なのでちゃんと理解しておく必要があります。

まずSwiftの「文字」の構成に関わる型は5つあります。それぞれ簡単に説明します。

* Character
* UnicodeScalar
* UInt8
* UInt16
* CChar

## Character

CharacterはStringの第一の構成要素で、 `String#generate()` はCharacterを扱います。つまり、Stringをfor-in loopで列挙するとCharacterごとに処理が分割されます。

CharacterはSwiftにおける「一文字」を表現する型ですが、後述するようにCharacterだけが文字の表現ではありません。StringをCharacter以外のデータ列としてアクセスするメソッドもあります。

```swift
func countSequence<T: SequenceType>(s: T) -> Int {
    var count = 0
    for _ in s {
        count++
    }
    return count
}

// U+29E3Dは𩸽（ホッケ）。実際にソースコードでは、文字列リテラルのなかに直接書ける。

println(countSequence("\u{29E3D}")) // => 1
for c in "\u{29E3D}"{ 
    println(c) // 𩸽
}
```

なお、`String#substringFromIndex()` の引数として、ランダムアクセス可能なIndex（つまりInt）をとることができず、 `String.Index` しか受け付けられません。Character以外の文字のデータ列もランダムアクセスできないため、スライスを得るのは若干面倒です。

```swift
let s = "Hello, world!"
// s[s.startIndex + 7 ..< s.endIndex] // compile error!
s[advance(s.startIndex, 7) ..< s.endIndex]
```

## UnicodeScalar

UnicodeScalarは21bitの数値による表現で、単一のユニコード文字を表します。一方、Characterは任意の数のUnicodeScalarからなる合成文字でも一つのCharacterです。言い換えれば、 **Characterは単一のユニコード文字とは限らない**ので注意してください。

たとえば、「か」＋「U+29E3D (濁点)」の結合文字は、Characterでは1文字、UnicodeScalarでは2文字にカウントされます。

文字をUnicodeScalarごとに列挙するには、`String#unicodeScalars` を使います。

```Swift
println(countSequence("か\u{3099}"))                // => 1
println(countSequence("か\u{3099}".unicodeScalars)) // => 2
```

## UInt8

UTF8でエンコードした1文字で、 `Stirng#utf8` で列挙したときの要素の型です。

```swift
println(countSequence("か\u{3099}".utf8)) // => 2
println(countSequence("\u{29E3D}.utf8")) // => 4
```

## UInt16

UTF16でエンコードされた1文字で、`String#utf16` で列挙したときの要素の型です。U+29E3Dのカウントが2になるのはサロゲートペアのためです。

```swift
println(countSequence("か\u{3099}".utf16)) // => 2
println(countSequence("\u{29E3D}".utf16)) // => 2
```

## CChar

Cのcharに対応する型です。pure Swiftなコードで使うことは少ないのですが、 `String.fromCString(UnsafePointer<CChar>)` で Cの関数から得る `const char*` からSwiftの文字列に変換したり、 `String#cStringUsingEncoding(NSStringEncoding)` で `[CChar]` に変換したりできます。

## それぞれの値の相互変換

Swiftの型システムは非常に厳密で、原則として暗黙の型変換を認めません。

ただし、リテラルの場合のみ、情報量を落とさないかぎりにおいて暗黙の型変換が認められるケースがあります。

```swift
let c: Character = "a" // OK
let u: UnicodeScalar = "a" // OK
```

情報量が同じ型でも、たとえばCharとUInt8の変換は簡単ではありません。同じビット表現でも、値が変化する型変換は実行時例外（クラッシュ）になるからです。

```swift
let copyrightSign: UInt8 = 0xA9 // © (copyright sign) 
CChar(copyrightSign) // crash!
```

あまりいい方法をまだ見つけられていないのですが、以下のように泥臭い方法でCと同じ意味でのキャストができます（※ もっといい方法があります。コメント欄参照のこと）。

```swift
// typealias Byte = UInt8 がデフォルトで宣言されている
func byte2cchar(b: Byte) -> CChar {
    if b < 0x80 {
        return CChar(b)
    } else {
        return -0x80 + CChar(b & ~Byte(0x80))
    }
}

byte2cchar(copyrightSign) // => -87
```

`byte2ccha()` は `String#utf8` でバラしたバイト列を `String.fromCString()` で文字列にしなおすときに必要になるかもしれません。このあたりはまだ調べているところで、もしかしたらもっといい方法があるかもしれないので鵜呑みにしないでください。

なお、 `UnsafePointer<T>` であれば、 `unsafeBitCast()` で *ポインタ型の再解釈* ができます。

## 参考文献

* “Unicode Representations of Strings” - Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks. https://itun.es/jp/jEUH0.l