URL
試した環境
- Ubuntu Server 14.04 LTS
- Erlang/OTP 18
- Elixir 1.0.4
Binaries, strings and char lists
Basic Typesで文字列について、is_binary/1
を使ってチェックした。この章では、シングルクオートにでくくられたこの値がElixirではどのような意味を持つのかを説明する。
iex> string = "hello"
"hello"
iex> is_binary string
true
UTF-8 and Unicode
文字列はUTF-8でエンコードされたバイナリ。これが何を意味しているのかを理解するためには、バイトとコードポイントの違いを理解する必要がある。
ユニコード標準は、コードポイントを私たちが知っている多くの文字に割り当てている。
例えば、文字aは、コードポイント97を持っている。一方łはコードポイント322を持っている。もし、1つのコードポイントを1バイトで表現する場合、łは表現できない。なぜなら、łのコードポイントは322であるが、1バイトは0から255までしか表現できないから。しかし、実際はłは読めている。これがエンコーディングというものができた理由。
iex> [97]
'a' % ASCII文字は文字として表示される
iex> 'a'
'a'
iex> [322]
[322]
iex> 'ł'
[322]
バイトでコードポイントを表現する場合、私たちはどうにかしてエンコードする必要がある。ElixirはデフォルトのエンコーディングとしてUTF-8エンコードを選択した。私たちが文字列はUTF-8エンコードされたバイナリであると言うとき、文字列はあるコードポイントをある規則にのとって表現されたバイトの集まりであることを意味している。それはUTF-8エンコードで指定される。
コードポイントが322が割り当たっているłのような文字の場合、それを表現するには1バイト以上が必要になる。それが文字列をbyte_size/1とString.length/1で計算したときの計算結果の違い。
iex> string = "hełło"
"hełło"
iex> byte_size string
7
iex> String.length string
5
UTF-8で、h, eやoは1バイトで済むが、łを表現するには2バイト必要。
?
を使うとコードポイントの値を得ることができる。
iex> ?a
97
iex> ?ł
322
Stringモジュールの関数を使うとコードポイントに文字を分けることもできる。
iex> String.codepoints("hełło")
["h", "e", "ł", "ł", "o"]
シングルクオートで囲った場合は、コードポイントのリストとして表現される。
iex> 'hełło'
[104, 101, 322, 322, 111]
iex> ?h
104
iex> ?e
101
iex> ?ł
322
iex> ?o
111
Binaries (and bitstrings)
バイナリは <<
>>
で囲むことで定義できる。
iex> <<0,1,2,3,>>
<<0, 1, 2, 3>>
iex> byte_size <<0,1,2,3,>>
4
バイナリは単なるバイトの連なり。有効な文字列ではない場合もある。
iex> String.valid?(<<104, 101, 322, 322, 111>>)
true
iex> String.valid?(<<239, 191, 191>>)
false
String.valid?/1関数は引数が有効な文字しか含まれていないかどうかをチェックする関数。有効な関数が含まれている場合はtrueを返す。
文字列の結合操作 <>
は、実際にはバイナリの結合操作。
iex> <<0,1>> <> <<2,3>>
<<0, 1, 2, 3>>
nullバイト <<0>>
を連結して文字列のバイナリ表現を見ることはElixirではよくやる手法とのこと。
下記の場合、最後の0を除いたものが"hełło"
のバイト表現。
iex> "hełło" <> <<0>>
<<104, 101, 197, 130, 197, 130, 111, 0>>
iex> <<104, 101, 197, 130, 197, 130, 111>>
"hełło"
文字列のバイナリ表現でみられる数値は1バイトで表現されたもの。そのため255までの数値で表される。
255よりも大きな数字を保持したり、コードポイントからUTF8表現に変換できる。
iex> <<255>>
<<255>>
iex> <<256>>
<<0>>
iex> <<255 :: size(16)>>
<<0, 255>>
iex> <<256 :: size(16)>>
<<1, 0>>
iex> <<255 :: utf8>>
"ÿ"
iex> <<256 :: utf8>>
"Ā"
iex> <<256 :: utf8>> <> <<0>>
<<196, 128, 0>>
iex> <<256 :: utf8, 0>>
<<196, 128, 0>>
もし、バイトが8ビットのところに、1ビットのサイズを渡したらどうなるか。
iex> <<1 :: size(1)>>
<<1::size(1)>>
iex> <<2 :: size(1)>>
<<0::size(1)>>
iex> is_binary(<<1 :: size(1)>>)
false
iex> is_bitstring(<<1 :: size(1)>>)
true
iex> bit_size(<<1 :: size(1)>>)
1
iex> byte_size(<<1 :: size(1)>>)
1
この場合、値はバイナリではないが、ビット列ではある。単なるビットの集まり。つまり、バイナリは8で割り切れるビット列のこと。
バイナリ/ビット列もパターンマッチができる。
iex> <<0, 1, x>> = <<0, 1, 2>>
<<0, 1, 2>>
iex> x
2
iex> <<0, 1, x>> = <<0, 1, 2, 3>>
** (MatchError) no match of right hand side value: <<0, 1, 2, 3>>
上記のxのように、バイナリの各要素は8ビットにマッチすることが期待されることに気をつける。ただし、下記のようにバイナリ修飾子を使うことで、残りの部分にマッチさせることができる。
iex> <<0, 1, x :: binary>> = <<0, 1, 2, 3>>
<<0, 1, 2, 3>>
iex> x
<<2, 3>>
<<0, 1
の部分がマッチし、xの部分に<<0, 1
以降の部分が束縛される。文字列結合演算子の <>
に似ている。
iex> "he" <> rest = "hello"
"hello"
iex> rest
"llo"
文字列はUTF-8エンコードされたバイナリのこと。
バイナリは8で割り切れる数のビット列の集まり。
バイナリをビットとして扱う方法をみたが、大抵の場合はバイナリとして扱い、is_binary/1やbyte_size/1関数を使うだろう。
Char lists
文字リストは、文字のリスト以外のなにものでもない。
iex> 'hełło'
[104, 101, 322, 322, 111]
iex> is_list 'hełło'
true
iex> 'hello'
'hello'
iex> ?l % lのコードポイントを調べる
108
iex> [104, 101, 108, 108, 111]
'hello'
バイト列の代わりに、コードポイントのリストを含んでいる。
ただし、ASCIIの範囲内にあるコードポイントは文字として表示される。
ダブルクオートで囲まれたものは文字列(バイナリ)で、シングルクオートで囲まれたものは文字のリスト(コードポイントのリスト)。
実際には、文字リストはほとんどの場合、Erlangとのインタフェースに使われる。特に、バイナリを引数に受け取らない古いライブラリに使われる。
to_string/1やto_char_list/1関数を使うと文字リストを文字列に変換したり、戻したりできる。
iex> to_char_list "hello"
'hello'
iex> to_string 'hello'
"hello"
iex> to_char_list :hello
'hello'
iex> to_string :hello
"hello"
iex> to_char_list 1
'1'
iex> to_string 1
"1"
これらの関数はポリモーフィック。文字列や文字リストだけでなく、atomや数値も変換できる。