Swift で文字を文字コードに変換する
Swift プログラミング
Swift で文字列内の文字を文字コードで扱う方法です。
Objective-C では UTF16 で扱うのが基本でしたが、Swift では扱う文字コードを明確にした上で取り出す必要があるようです。
Swift での文字列の扱い
Swift言語 では文字列をString型 で扱いますが、この型は「文字」の「コレクション」という扱いになっています。
文字はCharacter型 で扱えるようになっているのですが、この型では文字コード を取得できないようです。文字コードを取得したい場合には、あらかじめ文字セットを特定して、そこから数値に変換する必要があります。
文字列を文字毎のデータで取得する
Swift言語 の文字列は、次のようにすることで、さまざまな文字毎のデータとして取得できます。
書式 | 取得できる型 | 取得できる内容 |
---|---|---|
str | String | 文字列自体が、文字を Character 型で扱うコレクションになっています。 |
str.unicodeScalars | String.UnicodeScalarView | 文字を UnicodeScalar 型で扱うコレクションです。 |
str.utf8 | String.UTF8View | 文字を UTF8.CodeUnit = UInt8 型で扱うコレクションです。 |
str.utf16 | String.UTF16View | 文字を UInt16 型で扱うコレクションです。 |
str.cStringUsingEncoding(NSStringEncoding) | [Char] | 文字を CChar = Int8 型で扱うコレクションです。 |
Swift言語 では「文字」として抽象的に扱っているのでしょう。
Objective-C言語 では文字列を内部ではUTF16 の数値で扱うようになっていて、unichar型 ですぐに文字コードを扱えるようになっていましたが、よく考えると、文字コードが決まらないと文字をコードで扱えないのは自然な設計です。
文字を文字コードで取得する
String型 の文字を文字コード で扱うためには、上記の方法を使って扱いたい文字コードのデータセットを取得して、それを扱う必要があります。
文字を Unicode 文字コードで取得する
文字をそのまま扱うという観点では Unicode がいちばん単純かもしれません。
Unicode は多国語対応の文字セットですが、文字を基本的に 2 バイトのコード (UCS-2
) で扱います。ただ、世界中の文字を 2 バイトで扱うことができないため、一部 4 バイトのコード (UCS-4
) で扱うようになっています。
Swift言語 ではそれらの違いをプログラマーが意識することなく、1 文字をひとつのUnicodeScalar型 として扱えるようです。
Unicode を 1 文字毎に処理する
文字列を Unicode 文字コードにして順次処理したいときは、先ほど紹介した「文字をコレクションにして扱う」ための機能とfor in
構文を使います。
for code in str.unicodeScalars {
println(code.value)
}
UnicodeScalar型 ではvalueプロパティ でUInt32型 のコードを取得できるので、これで文字コードを普通の数値で扱えます。
指定した位置のコードを取り出す
UnicodeScalar型
のコレクションから、任意の場所のUnicodeScalar
の値を取り出したいときは、String.UnicodeScalarView.Index型
で場所を指定することになります。
これを数値からは簡単には作れないため、次のようにstartIndexプロパティ とadvance関数 を使って目的の位置を作成します。
let unicodes:String.UnicodeScalarView = str.unicodeScalars
let index:String.UnicodeScalarView.Index = advance(unicodes.startIndex, 3)
let code:UInt32 = unicodes[index]
こうすることで、最初のインデックスから 3 つ先に進めたインデックス、つまり 4 番目の文字コードを取得できます。
1 文字を Unicode 文字コードに変換する
ある 1 文字の文字コードを取得したいときは、目的の文字 1 つでString型 を用意して、それとfirst関数 を使うと簡単です。
let code:UInt32 = first(str.unicodeScalars)!.value
こうすることで、文字列str
をUnicode
として、最初の 1 文字の文字コードを取得できます。
文字を UTF8 文字コードで取得する
文字を ASCII コードと同じように扱うには、よくUTF8 が使われます。
UTF8 はアルファベットで一般的な ASCII コードと互換性がある文字コードなので、たとえば HTML のような『アルファベットと他の言語で構成された文字列内の、アルファベットを解析する』といったときに勝手が良かったりします。
ただし UTF8 は、文字が何バイトで構成されているかは調べないと判らないため、ASCII コードを解析するときには便利ですが、文字単位でデータを扱いたいときには不向きかもしれません。
Swift言語 では UTF8 の 1 バイトをUTF8.CodeUnit型 として扱えます。これを幾つかひとまとまりで 1 文字を表します。
UTF8 を 1 コード単位毎に処理する
文字列を UTF8 文字コードにして順次処理する方法は、Unicode を扱うときとほぼ同等です。ただし、コード 1 つが 1 文字ではなく 1 コード単位 (UTF8.CodeUnit型 ) なことに注意します。
for code in str.utf8 {
println(code)
}
UTF8.CodeUnit型 はUInt8型 の別名なので、そのまま 8 ビット符号なし整数として扱えます。
指定した位置のコードを取り出す
UTF8.CodeUnit型 のコレクションから、任意の場所の値を取り出す方法も、Unicode を扱うときと型が違うだけで処理は同じです。
let utf8s:String.UTF8View = str.utf8
let index:String.UTF8View.Index = advance(utf8s.startIndex, 3)
let code:UTF8.CodeUnit = utf8s[index]
1 文字を UTF8 文字コードに変換する
ある 1 文字の文字コードを取得したいときも、Unicode のときとほぼ同じです。
Unicode
のUnicodeScalar
とは違って、UTF8.CodeUnit型
はUInt8型
そのものなので、そのまますぐに数値として利用できます。
let code:UTF8.CodeUnit = first(str.utf8)!
文字を UTF16 文字コードで取得する
Swift言語 では、文字をUTF16 で扱うと、コード的には簡単に書ける様子です。
UTF16
はUnicode
と同じで、文字を基本的に 2 バイトのコード (UCS-2
) で扱います。ただ、世界中の文字を 2 バイトで扱うことができないため、一部 4 バイトのコード (UCS-4
) で扱うようになっています。
UTF16 の留意点
UTF16 のUnicode と違うところは、Swift言語 では、データをUnicode のような 1 文字単位ではなく、2 バイト単位で扱うところです。
一般的な文字であれば 2 バイトで表現されるようなのですが、4 バイトで表現される場合に特別な扱いをしないといけないところが厄介です。たとえば絵文字は 4 バイトで構成されています。
ただ、Objective-C言語
では内部でUTF16
だったこともあって、たとえばNSString
のlengthプロパティ
でも、この 2 バイトを 1 文字として扱っているようです。
そのため、文字列に絵文字が含まれていると、文字位置を示すインデックスが狂ってきたり、4 バイト文字を途中で分割すると表示されなくなったりと、何かと処理が狂ってきます。もちろんcharacterAtIndex:メソッド で取得できる文字も 2 バイト文字か 4 バイト文字かに関係なく、指定した位置にある 2 バイトのデータです。
逆に言うと、そうでありながらObjective-C言語 では特に問題にならなかったことを考えると、基本的にはUTF16 で扱っても問題ないのかもしれません。
少なくとも、objc と同等の文字列操作を期待するなら、Swift言語 ではUTF16 で扱うのが良いでしょう。
なお、Swift言語 ではUTF16 の文字コードを単純なUInt16型 として扱え、文字位置を指定するときも独自のインデックス型ではなくInt型 で扱えるため、コードはとてもシンプルになります。
UTF16 を 1 コード単位毎に処理する
文字列を UTF16 文字コードにして順次処理する方法は、UTF8 を扱うときと同等です。ただしこちらも、コード 1 つが 1 文字ではなく 1 コード単位なことに注意します。
for code in str.utf16 {
println(code)
}
UTF16 では 1 コード単位をUInt16型 で取得できるため、そのまま 16 ビット符号なし整数として扱えます。
指定した位置のコードを取り出す
UTF16 の場合、コレクションから任意の場所を取得するときに、位置をInt型 で指定するため、コードがとても簡単です。たとえば 4 番目のコードを取り出すときは次のとおりです。
let code:UInt16 = str.utf16[3]
1 文字を UTF16 文字コードに変換する
ある 1 文字の文字コードを取得したいときは、UTF8 のときと同じです。
let code:UInt16 = first(str.utf16)!
文字を任意の文字コードで取得する
String型 には、任意の文字コードに変換したC 文字列 を取得する機能があるので、それを使えばこれまで以外の文字コードで文字を扱えます。
普通の文字列であればcStringUsingEncoding:メソッド を使ってCChar型 の配列でデータを取得できますが、CChar型 にとらわれたくない場合はdataUsingEncoding:メソッド を使ってNSData型 で取得する方法もあります。
ちなみにCChar型 はInt8型 の別名です。このCChar型 を幾つ使って 1 文字を表現するかは、エンコード方法によって違ってきます。
文字列を C 文字列に変換する
たとえば、文字列をUTF8型 形式の C 文字列に変換するには次のようにします。
let cstr:[CChar] = str.cStringUsingEncoding(NSUTF8StringEncoding)!
これで、Swift の文字列を C 文字列として、CChar型 を格納する配列として取得できます。
指定した位置のコードを取り出す
取得した C 文字列はシンプルな配列なので、添え字を使って簡単に要素を取得できます。たとえば 4 番目の要素を取り出すときは次のとおりです。
let char:CChar = cstr[3]
CChar型 を扱う際の留意点
上の例では文字列をUTF8 として取り出したので、文字コードもUTF8 のコードが得られてますが、String型 に標準で用意されているutf8プロパティ で取得したときとは『値が違う場合がある』ところに注意が必要です。
今回の方法で得られる文字コードはCChar型 はInt8型 なのに対して、標準のutf8プロパティ を使った場合はUInt8型 が得られます。
そのため、文字コードが 128 以上の文字の場合、CChar型 では負の整数が得られます。文字コードは何かと正の整数で扱われることが多い気がします。そしてSwift言語 では型の違いにコードが多く左右されるため、何かとUInt8 で揃えたくなります。
そんなときは次のようにして、CChar型 をUTF8.CodeUnit型 に変換します。
let char:CChar = cstr[3]
let code = UTF8.CodeUnit(bitPattern: char)
UTF8.CodeUnit型 の代わりにUInt8型 としてもまったく同じです。
1 文字を文字コードに変換する
ある 1 文字の先頭の文字コードを取得したいときは、UTF8 のときと同じで、文字 1 つで文字列を作ってその最初の文字コードを取得します。
let char:CChar = first(str.cStringUsingEncoding(NSUTF8StringEncoding)!)!