Elixir入門 06: バイナリと文字列および文字リスト
Tags: elixir, webdev, tutorial, programming
Published: 2018-10-23 00:58:40

---
title: Elixir入門 06: バイナリと文字列および文字リスト
published: true
description: Elixir公式サイトの許諾を得て「Binaries, strings, and charlists」の解説にもとづき、加筆補正を加えてElixirのバイナリと文字列および文字リストについてご説明します。
tags: #elixir, #webdev, #tutorial, #programming
---

本稿はElixir公式サイトの許諾を得て「[Binaries, strings, and charlists](https://elixir-lang.org/getting-started/binaries-strings-and-char-lists.html)」の解説にもとづき、加筆補正を加えてElixirのバイナリと文字列および文字リストについてご説明します。

# UTF-8とUnicode
文字列はUTF-8エンコーディングのバイナリです。バイナリであることは[`is_binary/1`関数](https://hexdocs.pm/elixir/Kernel.html#is_binary/1)で確かめられます。はじめにバイトとコードポイントの違いについて確かめておきましょう。

```elixir
iex> is_binary("hello")
true
```

Unicode標準は数多くの文字にコードポイントを割り振っています(「[Unicode](https://ja.wikipedia.org/wiki/Unicode)」参照)。たとえば、「a」のコードポイントは97です。文字列はコードポイントを並べて表します。そして、コンピュータで扱うために数値はバイトに直さなければなりません。1バイトは0から255までの整数です。

ところが、日本語の「あ」はコードポイント12354です。つまり、ひとつの文字が1バイトでは表せないことになります。そこで、エンコーディングが求められるのです。Elixirがデフォルトで採用するUTF-8のエンコーディングは、数バイトを使って文字のコードポイントを定めます。文字列はUTF-8エンコーディングで管理されたコードポイントを示すバイトの集まりといえるのです。

基本的なアルファベットは1バイトずつで表せるのに対して、日本語の文字はひと文字で複数バイトを使います。文字列のバイトサイズは関数[`byte_size/1`](https://hexdocs.pm/elixir/Kernel.html#byte_size/1)、文字数は[`String.length/1`](https://hexdocs.pm/elixir/String.html#length/1)調べればよいでしょう。


```elixir
iex> byte_size("hello")
5
iex> byte_size("拝啓")
6
iex> String.length("hello")
5
iex> String.length("拝啓")
2
```

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

```elixir
iex> ?a
97
iex> ?あ
12354
```

さらに、文字列をひと文字ずつのリストにして返すのが関数[`String.codepoints/1`](https://hexdocs.pm/elixir/String.html#codepoints/1)です。

```elixir
iex> String.codepoints("hello")
["h", "e", "l", "l", "o"]
iex> String.codepoints("拝啓")
["拝", "啓"]
```


# バイナリ
Elixirのバイナリは、関数[`<<>>/1`](https://hexdocs.pm/elixir/Kernel.SpecialForms.html#%3C%3C%3E%3E/1)にバイトシーケンスを渡して定めます。

```elixir
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`メソッド](https://hexdocs.pm/elixir/String.html#valid?/1)です。

```elixir
iex> String.valid?(<<239, 191, 19>>)
false
iex> String.valid?(<<230, 139, 157, 229, 149, 147>>)
true
```

文字列を結ぶ[`<>/2`演算子](https://hexdocs.pm/elixir/Kernel.html#%3C%3E/2)は、バイナリをつなぐこともできます。Elixirにおける文字列は、バイナリとして扱われているからです。つまり、`<>/2`は実はバイナリを結ぶ演算子なのです。

```elixir
iex> <<0, 1>> <> <<2, 3>>
<<0, 1, 2, 3>>
iex> <<230, 139, 157>> <> <<229, 149, 147>>
"拝啓"
```

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

```elixir
iex> "拝啓" <> <<0>>
<<230, 139, 157, 229, 149, 147, 0>>
```

バイナリの各バイトは0から255までの整数で表されます。256以上の整数を与えても構いません。デフォルトでは剰余が取られます。また、`size/1`によるビット数やエンコーディングの指定により、値を変換することも可能です。

```elixir
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`](https://hexdocs.pm/elixir/Kernel.html#is_binary/1)と[`is_bitstring/1`](https://hexdocs.pm/elixir/Kernel.html#is_bitstring/1)で確かめられます。

```elixir
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ビットずつに分けたビットストリングです。

```elixir
iex>  is_binary(<<1 :: size(16)>>)
true
iex> is_bitstring(<<1 :: size(16)>>)
true
iex>  is_binary(<<1 :: size(15)>>)
false
```

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

```elixir
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`の指定を添えることでマッチさせられます。

```elixir
iex(86)> <<97, x :: binary>> = <<97, 196, 128>>
"aĀ"
iex(87)> x
"Ā"
```

また、[`<>`](https://hexdocs.pm/elixir/Kernel.html#%3C%3E/2)演算子でもパターンマッチングが使えます。

```elixir
iex> "he" <> rest = "hello"
"hello"
iex> rest
"llo"
```


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

```elixir
iex> 'hello'
'hello'
iex> '拝啓'
[25309, 21843]
```

文字リストはErlangとのインタフェースによく使われてきました。文字列と文字リストの間の変換には、関数[`to_string/1`](https://hexdocs.pm/elixir/Kernel.html#to_string/1)と[`to_charlist/1`](https://hexdocs.pm/elixir/Kernel.html#to_charlist/1)を用います。なお、`to_string/1`の引数に渡せるのはリストだけにかぎりません。

```elixir
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`演算子](https://hexdocs.pm/elixir/Kernel.html#++/2)で結ばなければなりません。

```elixir
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: コードを書いて試してみる](https://dev.to/gumi/elixir-01--2585)
- [Elixir入門 02: 型の基本](https://dev.to/gumi/elixir-02--30n1)
- [Elixir入門 03: 演算子の基本](https://dev.to/gumi/elixir-03--33im)
- [Elixir入門 04: パターンマッチング](https://dev.to/gumi/elixir-04--1346)
- [Elixir入門 05: 条件 - case/cond/if](https://dev.to/gumi/elixir-05----casecondif-60o)
- Elixir入門 06: バイナリと文字列および文字リスト
- [Elixir入門 07: キーワードリストとマップ](https://dev.to/gumi/elixir-07--39hi)
- [Elixir入門 08: モジュールと関数](https://dev.to/gumi/elixir-08--1c4c)
- [Elixir入門 09: 再帰](https://dev.to/gumi/elixir-09--1a0p)
- [Elixir入門 10: EnumとStream](https://dev.to/gumi/elixir-10-enumstream-4fpb)
- [Elixir入門 11: プロセス](https://dev.to/gumi/elixir-11--2mia)
- [Elixir入門 12: 入出力とファイルシステム](https://dev.to/gumi/elixir-12--4og6)
- [Elixir入門 13: aliasとrequireおよびimport](https://dev.to/gumi/elixir-13-aliasrequireimport-55c1)
- [Elixir入門 14: モジュールの属性](https://dev.to/gumi/elixir-14--3511)
- [Elixir入門 15: 構造体](https://dev.to/gumi/elixir-15--4f43)
- [Elixir入門 16: プロトコル](https://dev.to/gumi/elixir-16--lif)
- [Elixir入門 17: 内包表記](https://dev.to/gumi/elixir-17--5gci)
- [Elixir入門 18: シギル](https://dev.to/gumi/elixir-18--5791)
- [Elixir入門 19: tryとcatchおよびrescue](https://dev.to/gumi/elixir-19-trycatchrescue-50i8)
- [Elixir入門 20: 型の仕様とビヘイビア](https://dev.to/gumi/elixir-20--j50)
- [Elixir入門 21: デバッグ](https://dev.to/gumi/elixir-21--21a1)
- [Elixir入門 22: Erlangライブラリ](https://dev.to/gumi/elixir-22-erlang-2492)
- [Elixir入門 23: つぎのステップ](https://dev.to/gumi/elixir-23--50ik)

##### 番外
- [Elixir入門: Plugを使うには](https://dev.to/gumi/elixir-plug-40lb)