本稿はElixir公式サイトの許諾を得て「Binaries, strings, and charlists」の解説にもとづき、加筆補正を加えてElixirのバイナリと文字列および文字リストについてご説明します。
UTF-8とUnicode
文字列はUTF-8エンコーディングのバイナリです。バイナリであることはis_binary/1
関数で確かめられます。はじめにバイトとコードポイントの違いについて確かめておきましょう。
iex> is_binary("hello")
true
Unicode標準は数多くの文字にコードポイントを割り振っています(「Unicode」参照)。たとえば、「a」のコードポイントは97です。文字列はコードポイントを並べて表します。そして、コンピュータで扱うために数値はバイトに直さなければなりません。1バイトは0から255までの整数です。
ところが、日本語の「あ」はコードポイント12354です。つまり、ひとつの文字が1バイトでは表せないことになります。そこで、エンコーディングが求められるのです。Elixirがデフォルトで採用するUTF-8のエンコーディングは、数バイトを使って文字のコードポイントを定めます。文字列はUTF-8エンコーディングで管理されたコードポイントを示すバイトの集まりといえるのです。
基本的なアルファベットは1バイトずつで表せるのに対して、日本語の文字はひと文字で複数バイトを使います。文字列のバイトサイズは関数byte_size/1
、文字数はString.length/1
調べればよいでしょう。
iex> byte_size("hello")
5
iex> byte_size("拝啓")
6
iex> String.length("hello")
5
iex> String.length("拝啓")
2
また、文字のコードポイントは?
で得られます。
iex> ?a
97
iex> ?あ
12354
さらに、文字列をひと文字ずつのリストにして返すのが関数String.codepoints/1
です。
iex> String.codepoints("hello")
["h", "e", "l", "l", "o"]
iex> String.codepoints("拝啓")
["拝", "啓"]
バイナリ
Elixirのバイナリは、関数<<>>/1
にバイトシーケンスを渡して定めます。
iex> <<0, 1, 2, 3>>
<<0, 1, 2, 3>>
iex> byte_size(<<0, 1, 2, 3>>)
4
iex> <<230, 139, 157, 229, 149, 147>>
"拝啓"
iex> byte_size(<<230, 139, 157, 229, 149, 147>>)
6
バイトシーケンスはさまざまに操作できます。文字列を表すこともありますし、そうでないこともあります。文字列として有効かどうか確かめるのはString.valid?/1
メソッドです。
iex> String.valid?(<<239, 191, 19>>)
false
iex> String.valid?(<<230, 139, 157, 229, 149, 147>>)
true
文字列を結ぶ<>/2
演算子は、バイナリをつなぐこともできます。Elixirにおける文字列は、バイナリとして扱われているからです。つまり、<>/2
は実はバイナリを結ぶ演算子なのです。
iex> <<0, 1>> <> <<2, 3>>
<<0, 1, 2, 3>>
iex> <<230, 139, 157>> <> <<229, 149, 147>>
"拝啓"
文字列のバイトシーケンスは、0バイト<<0>>
をつなげば確かめられます。
iex> "拝啓" <> <<0>>
<<230, 139, 157, 229, 149, 147, 0>>
バイナリの各バイトは0から255までの整数で表されます。256以上の整数を与えても構いません。デフォルトでは剰余が取られます。また、size/1
によるビット数やエンコーディングの指定により、値を変換することも可能です。
iex> <<256>>
<<0>>
iex> <<256 :: size(16)>>
<<1, 0>>
iex> <<256 :: utf8>>
"Ā"
iex> <<256 :: utf8, 97>>
"Āa"
iex> <<256 :: utf8, 97, 0>>
<<196, 128, 97, 0>>
1ビット(size(1)
)に変換すると、値は0か1にかぎられます。すると、バイナリでなくビットストリングとして扱われるのです。バイナリかビットストリングかは、それぞれ関数is_binary/1
とis_bitstring/1
で確かめられます。
iex> <<1 :: size(1)>>
<<1::size(1)>>
iex> <<2 :: size(1)>> # truncated
<<0::size(1)>>
iex> is_binary(<<1 :: size(1)>>)
false
iex> is_bitstring(<<1 :: size(1)>>)
true
iex> bit_size(<< 1 :: size(1)>>)
1
バイナリも8ビットずつに分けたビットストリングです。
iex> is_binary(<<1 :: size(16)>>)
true
iex> is_bitstring(<<1 :: size(16)>>)
true
iex> is_binary(<<1 :: size(15)>>)
false
バイナリやビットストリングにパターンマッチングを使うこともできます。
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>>
バイナリの各バイトは8ビットとみなされます。サイズのわからないバイナリを加えたいときは、パターンの最後にbinary
の指定を添えることでマッチさせられます。
iex(86)> <<97, x :: binary>> = <<97, 196, 128>>
"aĀ"
iex(87)> x
"Ā"
また、<>
演算子でもパターンマッチングが使えます。
iex> "he" <> rest = "hello"
"hello"
iex> rest
"llo"
文字リスト
文字リストはコードポイントのリストです。シングルクォーテーション(''
)でかこんだリテラルによりつくることもできます。iex
では、ASCII範囲外の文字が含まれると、コードポイントのリストが示されます。Elixirでは、ダブルクォーテーション(""
)は文字列つまりバイナリ、シングルクォーテーション(''
)は文字リストを表すということです。
iex> 'hello'
'hello'
iex> '拝啓'
[25309, 21843]
文字リストはErlangとのインタフェースによく使われてきました。文字列と文字リストの間の変換には、関数to_string/1
とto_charlist/1
を用います。なお、to_string/1
の引数に渡せるのはリストだけにかぎりません。
iex> to_charlist("hello")
'hello'
iex> to_charlist("拝啓")
[25309, 21843]
iex> to_string('hello')
"hello"
iex> to_string([25309, 21843])
"拝啓"
iex> to_string(:hello)
"hello"
iex> to_string(1)
"1"
文字列つまりバイナリをつなぐには<>/2
演算子を用いました。けれど、文字リストは++/2
演算子で結ばなければなりません。
iex> 'this ' <> 'fails'
** (CompileError) iex:n: invalid literal 'this ' in <<>>
(elixir) src/elixir_bitstring.erl:19: :elixir_bitstring.expand/6
(elixir) src/elixir_bitstring.erl:12: :elixir_bitstring.expand/4
(elixir) expanding macro: Kernel.<>/2
iex:n: (file)
iex> 'this ' ++ 'works'
'this works'
iex> "he" ++ "llo"
** (ArgumentError) argument error
:erlang.++("he", "llo")
iex> "he" <> "llo"
"hello"
Elixir入門もくじ
- Elixir入門 01: コードを書いて試してみる
- Elixir入門 02: 型の基本
- Elixir入門 03: 演算子の基本
- Elixir入門 04: パターンマッチング
- Elixir入門 05: 条件 - case/cond/if
- Elixir入門 06: バイナリと文字列および文字リスト
- Elixir入門 07: キーワードリストとマップ
- Elixir入門 08: モジュールと関数
- Elixir入門 09: 再帰
- Elixir入門 10: EnumとStream
- Elixir入門 11: プロセス
- Elixir入門 12: 入出力とファイルシステム
- Elixir入門 13: aliasとrequireおよびimport
- Elixir入門 14: モジュールの属性
- Elixir入門 15: 構造体
- Elixir入門 16: プロトコル
- Elixir入門 17: 内包表記
- Elixir入門 18: シギル
- Elixir入門 19: tryとcatchおよびrescue
- Elixir入門 20: 型の仕様とビヘイビア
- Elixir入門 21: デバッグ
- Elixir入門 22: Erlangライブラリ
- Elixir入門 23: つぎのステップ
Top comments (0)