Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

Rubyの文字エスケープ完全ガイド:クォート・正規表現・改行の基本を解説

この記事では、Rubyにおける文字のエスケープについて詳しく解説します。文字をエスケープする方法、その仕組み、そして一部のユースケースではエスケープ自体を回避する方法まで学べます。「\ のことは全部知っている」と思っている方も、最後まで読めば意外な発見があるかもしれません。

それでは、文字列内の文字エスケープから始めましょう。

クォートのエスケープ

Rubyで文字列を扱うとき、文字列の定義に使った引用符と同じ記号を、文字列の中身として含めたいことがあります。その場合は、バックスラッシュ \ を使ってクォート文字をエスケープします。

# ダブルクォート文字列内でクォートをエスケープ
"Hello \"world\"!"
# => "Hello \"world\"!"
 
# シングルクォート文字列内でクォートをエスケープ
'Hello \'world\'!'
# => "Hello 'world'!"

この例からわかるように、Rubyは出力時にデフォルトでダブルクォート形式の文字列を使用し、ダブルクォートだけを出力時にエスケープします。

エスケープシーケンス

クォート以外にも、文字列内でエスケープできる記号はいくつかあります。たとえば改行は \n で表現されます。これを「エスケープシーケンス」と呼びます。

"Hello\nworld"
# => "Hello\nworld"

返ってくるのは作成したときと同じ文字列ですが、\n がハイライト表示されているのがわかるでしょう。これはエスケープシーケンスであることを示しています。この文字列を puts で出力すると、リテラルの \n ではなく実際の改行が出力されます。

puts "Hello\nworld"
# => Hello
# world

ところが、これはシングルクォート文字列では機能しません。シングルクォート文字列では \n はリテラルとしてそのまま解釈されます。同じ結果を得たい場合は、ダブルクォート文字列でバックスラッシュ自体をエスケープする必要があります。

'\n'
# => "\\n"

同様に動作する他のエスケープシーケンスの例としては、タブを表す \t、スペースを表す \s、バックスペースを表す \b などがあります。

シングルクォートとダブルクォートの違い

Rubyにおけるシングルクォートとダブルクォートの文字列の違いは、エスケープシーケンスの扱い方にあります。

  • ダブルクォート文字列では、エスケープシーケンスを書くと、Rubyがその意味に変換して出力します。\n は改行になります。
  • 一方シングルクォート文字列では、エスケープシーケンスがそのままリテラルとして返されます。\n\n のままです。

もちろん両方の文字列タイプにはそれぞれ用途があります。多くの場合、改行やタブといった変換後の意味を使いたいので、エスケープシーケンス入りのダブルクォート文字列を使うことになるでしょう。

一方、シングルクォートは、エスケープシーケンス自体をエスケープせずに済ませたいときに便利です。エスケープシーケンスの使い方を説明するときや、意図しないエスケープシーケンスの誤用を防ぎたいときに役立ちます。

puts "Line 1\nLine 2"
# => Line 1
# Line 2
 
puts 'Using a \n we can indicate a newline.'
# => Using a \n we can indicate a newline.

式展開(インターポレーション)のエスケープ

Rubyは文字列の中で式展開(インターポレーション)をサポートしています。しかし、ここでもすべての文字列定義が同じというわけではありません。式展開が機能するのはダブルクォート文字列だけです。

次のコード例では、ダブルクォート文字列では式展開が機能する一方、シングルクォート文字列ではRubyが式展開の構文をエスケープしてしまい、無効になっていることがわかります。

name = "world"
 
"Hello #{name}"
# => "Hello world"
 
'Hello #{name}'
# => "Hello \#{name}"

この挙動は、ほぼすべての種類の文字列の「マジック」に当てはまります。ダブルクォートでは有効、シングルクォートでは無効です。

文字列のためのパーセント記法

Rubyのパーセント記法はPerlプログラミング言語に着想を得たもので、よく使われる型定義のための省略記法を多数提供してくれます。

シングルクォートとダブルクォートの文字列に関する挙動の違いは、パーセント記法にもそのまま当てはまります。

  • %()%Q() はダブルクォート文字列と同じように振る舞います。
  • %q() はシングルクォート文字列と同じように振る舞います。

パーセント記法の問題点は、括弧の対応が取れていない場合、クォートの代わりに括弧文字 () をエスケープする必要が生じることです。

そこでRubyでは、パーセント記法に他の記号も使えるようになっています。%[foo]%{foo}%-foo-%?foo? などです。さらに %"foo"%'foo' までサポートされています。

パーセント記法を使うと、Rubyが自動的に文字列をエスケープしてくれることがわかります。

%(Hello "world"!)
# => "Hello \"world\""
 
# 括弧の対応が取れていない場合
%(Hello world\)!)
# => "Hello world)!"
 
# 括弧の対応が取れている場合
%(Hello (world)!)
# => "Hello (world)!"

ここでも通常のシングルクォート・ダブルクォート文字列と同じルールが適用されます。シングルクォート相当の記法では、エスケープシーケンスと式展開はデフォルトでエスケープされます。

name = "world"
 
%(Hello\n "#{name}"!)
# => "Hello\n \"world\"!"
 
%q(Hello\n "#{name}"!)
# => "Hello\\n \"\#{name}\"!"
 
%Q(Hello\n "#{name}"!)
# => "Hello\n \"world\"!"

正規表現での文字のエスケープ

文字のエスケープは正規表現でも機能します。正規表現では、多くの文字がリテラル以上の特別な意味を持ちます。

たとえば . は任意の一文字にマッチするワイルドカード、角括弧 [] は範囲または選択肢、丸括弧 () は式のグループ化などを表します。これらをリテラルとして使いたい場合も、バックスラッシュでエスケープできます。

/Hello \[world\]/
# => /Hello \[world\]/
 
/\[world\]/ =~ "Hello [world]"
# => 6

もし角括弧をエスケープしなければ、正規表現は括弧内のいずれかの文字を探し、「Hello」の中の l という文字に最初にマッチすることになります。

/[world]/ =~ "Hello [world]"
# => 2 # インデックスは0始まり

また、正規表現の定義に使われるスラッシュ / 自体をエスケープしたくない場合は、別のパーセント記法が使えます。

%r{/world/}
# => /\/world\//

%r{} を使えば、スラッシュをエスケープせずに正規表現を定義できます。結果を見ると、Rubyが自動的にスラッシュをエスケープしてくれていることがわかります。

改行のエスケープ

これまでは文字列内の「改行」エスケープシーケンス \n を扱ってきましたが、実はRubyコード自体の改行もエスケープできることをご存知でしたか?

たとえば、引数の多いメソッド呼び出しの行が非常に長くなり、コードスタイルガイドの最大行長を超えてしまうようなケースを考えてみましょう。

ruby_method_with_many_arguments "Hello world", split: " ", join: "\n"

引数を別々の行に置けば、1行あたりの長さを短くできます。これは、前の行がカンマ , やプラス記号 + などの演算子で終わっている場合、あるいは丸括弧 () で囲まれたメソッド呼び出しの一部である場合に機能します。

ruby_method_with_many_arguments "Hello world",
  split: " ",
  join: "\n"
 
ruby_method_with_many_arguments(
  "Hello world",
  split: " ",
  join: "\n"
)

もし括弧で囲まずに、すべての引数を同じインデントレベルに揃えたい場合は、バックスラッシュ \ で改行をエスケープします。

ruby_method_with_many_arguments \
  my_string,
  split: " ",
  join: "\n"

ここで行っているのは、厳密にはRubyに改行をエスケープさせることではなく、「文が次の行に続く」ことを伝える処理です。

文字列定義での改行のエスケープ

行末のエスケープは、文字列定義のような場面でも活用できます。通常、複数行にわたる文字列を作りたい場合は、プラス記号 + を使って2行の文字列を結合します。

"foo" +
  "bar"
# => "foobar"

この場合、2行にそれぞれ文字列オブジェクトが作られ、その後1つに結合されます。つまり実際には3つの文字列オブジェクトが生成されているのです。

プラス記号の代わりに、ここでもバックスラッシュで改行をエスケープできます。

"foo" \
  "bar"
# => "foobar"

結果は同じように見えますが、本当に同じなのでしょうか?

最初の例では実際には2つの新しい文字列を結合していますが、2番目の例ではRubyは1つの文字列しか生成しません。Rubyインタプリタはバックスラッシュ \ を文字列定義の継続とみなし、2行に基づいて1つの文字列だけを作るのです。つまり、アプリのメモリ使用量の面でも有利になります。

まとめ

Rubyをはじめ多くのプログラミング言語における文字のエスケープは、かなり混乱しやすいトピックです。ある文字の組み合わせが、ダブルクォート文字列の中では特別な意味を持つのに、シングルクォート文字列の中ではそうならない——突然そのような違いが現れます。

この小さなガイドが、Rubyで文字やシーケンスを適切にエスケープする方法の理解に役立てば幸いです。鍵となるのは、ダブルクォートとシングルクォートの文字列の違いです。

エスケープシーケンスや式展開といった文字列の「マジック」が本当に機能するのはダブルクォート文字列だけであり、シングルクォート文字列では機能しません。

また、Ruby自身のコードをエスケープする方法も他にあります。パーセント記法を使えば、文字列や正規表現で特定の文字をいちいちエスケープする手間を避けられます。

さらに、バックスラッシュは複数行の文字列を書いたり、Rubyコードの1行を次の行に継続させたりするのにも非常に便利です。筆者もコードをきれいで読みやすい状態に保つために頻繁に活用しています。

Rubyの文字エスケープについてさらに詳しく知りたいことや具体的な質問があれば、ぜひお気軽にお問い合わせください。

余談ですが、この記事はMarkdownで書かれているため、バックスラッシュ記号をかなり多用しました。Rubyと同じようにMarkdownでも特定の文字をエスケープできます。通常、Markdownではバックスラッシュをコードブロックで囲むことができません。バックスラッシュがコードブロック自体をエスケープしてしまうからです。代わりに、バックスラッシュの後にスペースを置いてエスケープする必要がありました。

`\` # これは動かない
`\ ` # これは動く :)
  1. JavaScriptのエスケープ文字とは?特殊文字のエスケープ方法と一覧を解説

    エスケープ文字とは?エスケープ文字とは、プログラム上で本来意図した意味とは別の特別な意味に解釈されてしまう文字のことです。たとえば、ダブルクォートやシングルクォート、改行コードなどが該当します。こうした文字を「そのままの文字」として扱い、画面に出力したい場合には、直前にバックスラッシュ「\」を付けて記述します。この操作を「エスケープ処理」と呼びます。JavaScriptの主なエスケープ文字一覧コード意味・出力結果\bバックスペース\fフォームフィード\n改行(ニューライン)\rキャリッジリターン\t水平タブ\v垂直タブ\シングルクォート()\ダブルクォート()\\バックスラッシュ(\)バックス

  2. JavaScriptでアクセント付き文字を含む文字列を並べ替える方法

    JavaScriptの標準的な sort() メソッドは、文字列をUnicodeコードポイントの順序に基づいて比較します。そのため、「é」や「ó」のようなアクセント付き文字を含む文字列を単純にソートすると、期待通りのアルファベット順にならないことがあります。この問題を解決するには、localeCompare() メソッドを使用します。このメソッドは、指定されたロケールの言語規則に従って文字列を比較できるため、アクセント付き文字も正しく並べ替えられます。localeCompare() の基本的な使い方以下は、スペイン語のアクセント付き文字を含む配列を localeCompare() を使ってソー