Elixir入門 18: シギル
Tags: elixir, webdev, tutorial, programming
Published: 2019-01-21 16:40:52

---
title: Elixir入門 18: シギル
published: true
description: Elixir公式サイトの許諾を得て「Sigils」の解説にもとづき、加筆補正を加えて、Elixirにおけるシギルの使い方についてご説明します。
tags: #elixir, #webdev, #tutorial, #programming
---

本稿はElixir公式サイトの許諾を得て「[Sigils](https://elixir-lang.org/getting-started/sigils.html)」の解説にもとづき、加筆補正を加えて、Elixirにおけるシギルの使い方についてご説明します。

Elixirが扱うテキストには、ダブルクォーテーション(`""`)の文字列とシングルクォテーションの文字リスト(`''`)があります。ほかにアトムもテキスト表現のひとつです。さらに、Elixirでテキスト表現を扱う仕組みとして、シギルについてご説明します。

Elixirは拡張性を重視します。言語が拡張しやすければ、開発者やコミュニティなどがそれぞれの目的に合うように機能が加えられるからです。シギルもまたElixirのテキスト表現を拡張できます。

シギルはチルダ(`~`)ではじまる識別子に区切り文字を添えて表します。そのあとに、オプションの修飾子が加わることもあります。


# 正規表現
Elixirでもっとも使われるシギルは`~r`で、[正規表現](https://hexdocs.pm/elixir/Regex.html#content)をつくります。Elixirの正規表現は、Perl互換の[PCRE](https://ja.wikipedia.org/wiki/Perl_Compatible_Regular_Expressions)ライブラリによる実装です。修飾子も使えます。たとえば、`i`は大文字小文字を区別しません。正規表現との等価比較には、[`=~/2`](https://hexdocs.pm/elixir/Kernel.html#=~/2)をお使いください。

```elixir
iex> "hello" =~ ~r/hello/
true
iex> "HELLO" =~ ~r/hello/
false
iex> "HELLO" =~ ~r/hello/i
true
iex> "world" =~ ~r/hello|world/
true
```

つぎのコードは、正規表現で文字列が半角スペース区切りの2単語以上かどうかを確かめます。

```elixir
iex> words = ~r/(\w+)\s(\w+)/
~r/(\w+)\s(\w+)/
iex> "hello" =~ words
false
iex> "hello world" =~ words
true
```

さらに、ElixirではErlangの正規表現にもとづくAPIが使えます。つぎのコードは、文字列のリストに分ける[`Regex.split/3`](https://hexdocs.pm/elixir/Regex.html#split/3)と文字列を置き替える[`Regex.replace/4`](https://hexdocs.pm/elixir/Regex.html#replace/4)の使用例です。詳しくは「[`Regex`](https://hexdocs.pm/elixir/Regex.html)」モジュールの解説をお読みください。

```elixir
iex> string = "100_000_000"
"100_000_000"
iex> Regex.split(~r/_/, string)
["100", "000", "000"]
iex> Regex.replace(~r/_/, string, ",")
"100,000,000"
```

ご紹介するコード例では、正規表現の区切り文字にバックスラッシュ(`\`)を用いています。けれども、シギルにはつぎの8つの区切り文字が使えます。

```elixir
~r/hello/
~r|hello|
~r"hello"
~r'hello'
~r(hello)
~r[hello]
~r{hello}
~r<hello>
```

区切り文字をいくつも使い分けられるのは、リテラルに区切り文字が含まれてもエスケープせずに済むようにするためです。

```elixir
iex> ~r(^https?://) == ~r/^https?:\/\//
true
```


# 文字列と文字リストおよび単語リストのシギル
正規表現のほかに、文字列と文字リストおよび単語リストのシギルがあります。

## 文字列
シギル`~s`は文字列をつくります。ダブルクォーテーション(`""`)の文字列と同じです。けれど、文字列にダブルクォーテーションが含まれるときに便利です。

```elixir
iex> ~s(this is a string with "double" quotes, not 'single' ones)
"this is a string with \"double\" quotes, not 'single' ones"
```

## 文字リスト
文字リストをつくるのはシギル`~c`です。シングルクォーテーション(`''`)の含まれる文字リストが簡単に書けます。

```elixir
iex> ~c(this is a char list containing 'single' and "double" quotes)
'this is a char list containing \'single\' and "double" quotes'
```

## 単語リスト
シギル`~w`は単語のリストをつくるために用いられます。単語はスペースで区切られた文字列です。修飾子として`s`か`a`あるいは`c`が添えられます。変わるのは、単語リスト要素のデータ型です。`s`はデフォルトの文字列、`a`がアトム、`c`は文字リストになります。

```elixir
iex> ~w(hello tokyo japan)
["hello", "tokyo", "japan"]
iex> ~w(hello tokyo japan)a
[:hello, :tokyo, :japan]
iex> ~w(hello tokyo japan)c
['hello', 'tokyo', 'japan']
iex> ~w(拝啓　時下ますます)
["拝啓", "時下ますます"]
```

## NaiveDateTime
シギル`~N`がつくるのは特別な[`NaiveDateTime`構造体](https://hexdocs.pm/elixir/NaiveDateTime.html)です。タイムゾーンのない日時を表します。`NaiveDateTime`構造体を直につくることはあまりないでしょう。ただし、パターンマッチングに使うことが考えられます。

```elixir
iex> new_year_18 = NaiveDateTime.from_iso8601("2018-01-01 00:00:00")
{:ok, ~N[2018-01-01 00:00:00]}
iex> new_year_18 == {:ok, ~N[2018-01-01 00:00:00]}
true
```


# シギルの補間とエスケープ
シギルには`~N`を除いて、それぞれ小文字と大文字があります。違いはエスケープ文字と補間に対応するかどうかです(表001)。大文字のシギルはエスケープ文字もそのまま文字として扱い、補間をしません。

```elixir
iex> intr = "inter "
"inter "
iex> pol = "polation"
"polation"
iex> ~s(String with escape codes \x26 #{intr <> pol})
"String with escape codes & inter polation"
iex> ~S(String without escape codes \x26 #{intr <> pol})
"String without escape codes \\x26 \#{intr <> pol}"
iex> ~w(String without escape codes \x26 #{intr <> pol})
["String", "without", "escape", "codes", "&", "inter", "polation"]
iex> ~W(String without escape codes \x26 #{intr <> pol})
["String", "without", "escape", "codes", "\\x26", "\#{intr", "<>", "pol}"]
```

#### 表001■シギルとエスケープ
| エスケープあり | エスケープなし | 表現 |
|:-----------:|:------------:|:----|
| ~r | ~R | 正規表現 |
| ~s | ~S | 文字列 |
| ~c | ~C | 文字リスト |
| ~w | ~W | 単語リスト |
|    | ~N | `NaiveDateTime`構造体 |

使えるエスケープコードはつぎのとおりです(「[ASCII制御文字](https://ja.wikipedia.org/wiki/ASCII#ASCII%E5%88%B6%E5%BE%A1%E6%96%87%E5%AD%97)」参照)。

- `\\`: バックスラッシュ(`\`)
- `\a`: ベル(警告)
- `\b`: バックスペース
- `\d`: 削除
- `\e`: エスケープ
- `\f`: 書式送り
- `\n`: 改行
- `\r`: 行頭復帰
- `\s`: スペース
- `\t`: タブ
- `\v`: 垂直タブ
- `\0`: ヌル文字
- `\xDD`: 16進数1バイト
    - 例: `\x13`
- `\uDDDD`または`\u{D...}`: 16進数Unicodeコードポイント
    - 例: \u{1F600})

(ダブルまたはシングル)クォーテーションで囲んだ中に同じ引用符が含まれると`\`でエスケープしなければなりません。

それに加えて、二重引用符で囲まれた文字列の中の二重引用符は\ "としてエスケープする必要があり、一重引用符で囲まれた文字リスト内の一重引用符は\ 'としてエスケープする必要があります。そういうとき、大文字シギルを使えばエスケープのわずらわしさが避けられるのです。

シギルは[ヒアドキュメント](https://ja.wikipedia.org/wiki/%E3%83%92%E3%82%A2%E3%83%89%E3%82%AD%E3%83%A5%E3%83%A1%E3%83%B3%E3%83%88)でも使えます。区切り文字は3連クォーテーションです。

```elixir
iex> ~s"""
...> これは
...> ヒア文字列
...> """
"これは\nヒア文字列\n"
```

ヒアドキュメントにシギルを使う場合としてもっとも考えられるのは、ドキュメントを書くときです。エスケープ文字が含まれることは少なくありません(図001)。とくにエスケープ文字が続くと見にくく、エラーも起きやすくなります。

```elixir
defmodule Example do
  @doc """
    ダブルクォーテーションをシングルクォーテーションに変換する。

    ## 例

      iex> convert("\\\"hello\\\"")
      "'hello'"

  """
  def convert(str) do

  end
end
```

#### 図001■表示されたドキュメント
![elixir_18_001.png](https://thepracticaldev.s3.amazonaws.com/i/n3kr0pucd4kim4hp0nz7.png)

こういうとき、大文字シギル`~S`を使えば、文字列をそのまま書けるのです。

```elixir
defmodule Example do
  @doc ~S"""
    ダブルクォーテーションをシングルクォーテーションに変換する。

    ## 例

      iex> convert("\"hello\"")
      "'hello'"

  """
  def convert(str) do

  end
end
```


# カスタムシギル
Elixirのシギルは拡張できます。たとえば、シギル`~r`を使うのは、[`sigil_r/2`](https://hexdocs.pm/elixir/Kernel.html#sigil_r/2)の呼び出しと同じです。第1引数にバイナリまたは文字リスト、第2引数にはオプションの修飾子を渡します。

```elixir
iex> r_hello = sigil_r(<<"hello">>, 'i')
~r/hello/i
iex> "HELLO" =~ r_hello
true
```

`sigil_{identifier}`のパターンにしたがった関数を実装するとシギルが定められます。[`String.upcase/2`](https://hexdocs.pm/elixir/String.html#upcase/2)と[`String.downcase/2`](https://hexdocs.pm/elixir/String.html#downcase/2)は、文字列をそれぞれ大文字あるいは小文字に変える関数です。これらふたつの関数で、つぎのように文字列を大文字や小文字にするシギルができます。小文字は第2引数の文字リストに修飾子として加えました。

```elixir
defmodule MySigils do
  def sigil_u(string, []), do: String.upcase(string)
  def sigil_u(string, [?d]), do: String.downcase(string)
end
```

```elixir
iex> import MySigils
MySigils
iex> ~u(hello world)
"HELLO WORLD"
iex> ~u(Hello World)d
"hello world"
```

シギルはマクロにして定めれば、コンパイル時の作業にも使えます。たとえば、正規表現は、ソースコードをコンパイルすると、表現が最適化されます。すると、実行時にはこの手順が省かれるのです。


#### 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: バイナリと文字列および文字リスト](https://dev.to/gumi/elixir-06--35na)
- [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: シギル
- [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)