DEV Community

gumi TECH for gumi TECH Blog

Posted on • Edited on

4 3

Elixir入門 06: バイナリと文字列および文字リスト

本稿はElixir公式サイトの許諾を得て「Binaries, strings, and charlists」の解説にもとづき、加筆補正を加えてElixirのバイナリと文字列および文字リストについてご説明します。

UTF-8とUnicode

文字列はUTF-8エンコーディングのバイナリです。バイナリであることはis_binary/1関数で確かめられます。はじめにバイトとコードポイントの違いについて確かめておきましょう。

iex> is_binary("hello")
true
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

また、文字のコードポイントは?で得られます。

iex> ?a
97
iex> ?あ
12354
Enter fullscreen mode Exit fullscreen mode

さらに、文字列をひと文字ずつのリストにして返すのが関数String.codepoints/1です。

iex> String.codepoints("hello")
["h", "e", "l", "l", "o"]
iex> String.codepoints("拝啓")
["拝", "啓"]
Enter fullscreen mode Exit fullscreen mode

バイナリ

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
Enter fullscreen mode Exit fullscreen mode

バイトシーケンスはさまざまに操作できます。文字列を表すこともありますし、そうでないこともあります。文字列として有効かどうか確かめるのはString.valid?/1メソッドです。

iex> String.valid?(<<239, 191, 19>>)
false
iex> String.valid?(<<230, 139, 157, 229, 149, 147>>)
true
Enter fullscreen mode Exit fullscreen mode

文字列を結ぶ<>/2演算子は、バイナリをつなぐこともできます。Elixirにおける文字列は、バイナリとして扱われているからです。つまり、<>/2は実はバイナリを結ぶ演算子なのです。

iex> <<0, 1>> <> <<2, 3>>
<<0, 1, 2, 3>>
iex> <<230, 139, 157>> <> <<229, 149, 147>>
"拝啓"
Enter fullscreen mode Exit fullscreen mode

文字列のバイトシーケンスは、0バイト<<0>>をつなげば確かめられます。

iex> "拝啓" <> <<0>>
<<230, 139, 157, 229, 149, 147, 0>>
Enter fullscreen mode Exit fullscreen mode

バイナリの各バイトは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>>
Enter fullscreen mode Exit fullscreen mode

1ビット(size(1))に変換すると、値は0か1にかぎられます。すると、バイナリでなくビットストリングとして扱われるのです。バイナリかビットストリングかは、それぞれ関数is_binary/1is_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
Enter fullscreen mode Exit fullscreen mode

バイナリも8ビットずつに分けたビットストリングです。

iex>  is_binary(<<1 :: size(16)>>)
true
iex> is_bitstring(<<1 :: size(16)>>)
true
iex>  is_binary(<<1 :: size(15)>>)
false
Enter fullscreen mode Exit fullscreen mode

バイナリやビットストリングにパターンマッチングを使うこともできます。

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>>
Enter fullscreen mode Exit fullscreen mode

バイナリの各バイトは8ビットとみなされます。サイズのわからないバイナリを加えたいときは、パターンの最後にbinaryの指定を添えることでマッチさせられます。

iex(86)> <<97, x :: binary>> = <<97, 196, 128>>
"aĀ"
iex(87)> x
"Ā"
Enter fullscreen mode Exit fullscreen mode

また、<>演算子でもパターンマッチングが使えます。

iex> "he" <> rest = "hello"
"hello"
iex> rest
"llo"
Enter fullscreen mode Exit fullscreen mode

文字リスト

文字リストはコードポイントのリストです。シングルクォーテーション('')でかこんだリテラルによりつくることもできます。iexでは、ASCII範囲外の文字が含まれると、コードポイントのリストが示されます。Elixirでは、ダブルクォーテーション("")は文字列つまりバイナリ、シングルクォーテーション('')は文字リストを表すということです。

iex> 'hello'
'hello'
iex> '拝啓'
[25309, 21843]
Enter fullscreen mode Exit fullscreen mode

文字リストはErlangとのインタフェースによく使われてきました。文字列と文字リストの間の変換には、関数to_string/1to_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"
Enter fullscreen mode Exit fullscreen mode

文字列つまりバイナリをつなぐには<>/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"
Enter fullscreen mode Exit fullscreen mode

Elixir入門もくじ

番外

Reinvent your career. Join DEV.

It takes one minute and is worth it for your career.

Get started

Top comments (0)

Billboard image

Deploy and scale your apps on AWS and GCP with a world class developer experience

Coherence makes it easy to set up and maintain cloud infrastructure. Harness the extensibility, compliance and cost efficiency of the cloud.

Learn more