🔗 1. 一般的な「string interpolation」とは
まずは一般的な話から。プログラミング言語で文字列を出力するときに、文字列の一部を変数の値や式の評価結果に差し替えたいことが非常によくあります。
"信号: 赤"
"信号: 黄"
"信号: 青"
上の文字列リテラル(" "
で囲まれている部分)のうち、「赤」「黄」「青」を何らかの方法で変数から送り込んで差し替える操作を一般にstring interpolationと呼びます。
"信号: ●" # ←この●を「赤」「黄」「青」に差し替えたい
3つの文字列リテラルを使い分けるより、●を「赤」「黄」「青」だけ差し替える方が楽ですし、文字列リテラルが無駄に長くならずに済みます。
なお、次のような文字結合によるベタなやり方は、結果は同じでもstring interpolationとは呼ばれないようです。
● = "赤" # 「赤」「黄」「青」のいずれかが入る
"信号: " + ● #=> "信号: 赤" など
上はいずれも説明用の擬似言語であり、特定の言語ではありません。
🔗 他の言語でのstring interpolation
string interpolationは言語によってさまざまな構文があり、1つの言語に複数の構文があることもあります。置き換えの場所は「プレースホルダ(placeholder)」とも呼ばれます。
string interpolationは他の言語では多くの場合「文字列補間」という訳語が使われていますが、Rubyでは「式展開」と訳されるのが普通です。
他の言語では、以下もstring interpolationとだいたい同じ意味で使われます。
- 変数置換: variable interpolation
- 変数補間: variable substitution
- 変数置換: variable expansion
本記事では以後「式展開」で統一します。
🔗 Rubyの式展開とは
Rubyで文字列の式展開というと以下のような形で説明されることがよくあります。二重引用符" "
の中に#{ }
と変数を記述すると変数の値が展開されます。なお、式展開が効くのは二重引用符の中だけであり、一重引用符' '
の中では式展開は無効です。
year = "2019"
puts "Year: #{year}"
#=> Year: 2019
#{ }
の中にはRubyのどんな式でもそのまま書けます(引用符も書けます)。以下のようにリテラルも書こうと思えば書けますが、この形ではリテラルにする意味はないと思います。
puts "Year: #{'The last year of Heisei'}"
#=> Year: The last year of Heisei
Rubyの#{ }
では「#to_s
が自動的に効く」という重要な特性があります。このおかげで、#{ }
の中にどんな式を置いても文字列に変換されます。詳しくは以下をご覧ください。
🔗 式展開のバリエーション(1)
あまり使わないと思いますが、式展開#{}
の中に#
でコメントを書くこともできます。ただしその#
から行末の改行までがすべてコメントとみなされるため、式展開の閉じ}"
は次の行に書く必要があります。
puts "Year: #{year # 平成最後の年
}"
#=> Year: 2019
🔗 式展開のバリエーション(2)
これもあまり使わないと思いますが、式展開に入れる変数が$
で始まる変数(グローバル変数)や@
で始まる変数(インスタンス変数やクラス変数)の場合、#{}
の{}
を省略できます。
@year=2019
puts "Year: #@year"
#=> Year: 2019
🔗 式展開のバリエーション(3)
式展開#{}
の前にバックスラッシュ\
を置くと、式展開を抑制できます。
puts "Enter the \#{year}"
#=> Enter the #{year}
🔗 式展開のバリエーション(4)-- %
を使う場合
%
記号を用いて、式展開と書式設定を一度に行うこともできます。書式設定のためのものなので、式展開と呼んでよいかどうかというのはありますが、利便性のためここに書きました。
puts "Year: %{year}" % { year: 2019 }
#=> Year: 2019
%
記号について詳しくは以下の記事をご覧ください。
🔗 式展開のバリエーション(5)%記法で使う場合(2021/09/30追記)
参考: %記法 -- リテラル (Ruby 3.0.0 リファレンスマニュアル)
Rubyの「%記法」には、式展開が使えるものと使えないものがあります。本記事では%記法の囲みの代表として[]
を使っていますが、実際の囲みには任意の非英数文字を利用できます。
以下の%[]
と%Q[]
(二重引用符と同等)、%W[]
(要素が文字列の配列)、%I[]
(要素がシンボルの配列)では式展開を利用できます。
%[foo bar #{RUBY_VERSION}]
#=> "foo bar 3.0.2"
%Q[foo bar #{RUBY_VERSION}]
#=> "foo bar 3.0.2"
%W[foo bar #{RUBY_VERSION}]
#=> ["foo", "bar", "3.0.2"]
%I[foo bar #{RUBY_VERSION}]
#=> [:foo, :bar, :"3.0.2"]
以下の%q[]
(一重引用符と同等)、%w[]
(要素が文字列の配列)、%s[]
(シンボル)、%i[]
(要素がシンボルの配列)では式展開が効かず、そのまま出力されます。
%q[foo bar #{RUBY_VERSION}]
#=> "foo bar \#{RUBY_VERSION}"
%w[foo bar #{RUBY_VERSION}]
=> ["foo", "bar", "\#{RUBY_VERSION}"]
%s[foo, bar, #{RUBY_VERSION}]
#=> :"foo, bar, \#{RUBY_VERSION}"
%i[foo, bar, #{RUBY_VERSION}]
#=> [:"foo,", :"bar,", :"\#{RUBY_VERSION}"]
%記法は引用符をエスケープせずに済む点が便利ですが、エディタでのシンタックスハイライトがうまく効かない可能性もあります。
🔗 参考: Rubyスタイルガイドでの引用符の使い分け
上述のとおり、Rubyでは二重引用符" "
の中で式展開が効き、一重引用符' '
の中では効きません。
RubocopのRubyスタイルガイド↓では、二重引用符と一重引用符をスタイル上使い分ける際にこの点も意識するとよいでしょう。
🔗 参考: Pythonの式展開
Pythonでは、引用符の中に{}
を記述し、format()
を用いて変数を指定することで式展開を行います。
# dateやtimeに値が入っているとする
print("Date: {}".format(date))
a = "Time: {}".format(time)
print(a)
ただし、正規表現でたまたま量指定子などの波かっこ{}
が使われていると、そこにformat()
が干渉してしまうので、文字列を分割するなどして避けてあげる必要があります。
re = "[^a-zA-Z]{2,3}[{}]$".format(word) # 末尾の[{}]ではなく{2,3}のところにwordが送り込まれてしまう
re = "[^a-zA-Z]{2,3}" + "[{}]$".format(word) # たとえばこのように分割して回避する
なお、{{}}
とすることで波かっこをエスケープできるそうです。format()
の対象外にはできなさそうですが。
参考: フォーマット文字列内での波括弧のエスケープ - Qiita
Pythonの場合、{}
の中にインラインで書けないので、{}
の数が増えてくるとformat()
側の変数がずれないように気を遣わないといけないのがちょっと不便に思いました(´・ω・`)。
🔗 Railsで生SQLを書くときの?
による式展開
SQLを生書きするときに使える疑問符?
による式展開(プレースホルダ)はRubyではなく、Railsがサポートしている機能です。Railsでは?
ですが、プレースホルダの書式はWebフレームワークやRDBMSによって異なることがあります。
RailsのActive Recordでは、以下のように生SQLの中で変数を渡す箇所に?
を置き、カンマに続けて変数を書くことで、?
の部分に変数(この場合params[:name]
)が展開されます。このとき変数の内容が適切にサニタイズ(sanitize: 機能を持つ文字や式を安全のためにエスケープすること)されます。
Person.where('name = ? AND hidden_at IS NULL', params[:name])
ユーザーから受け取った文字列を変数として生SQLを書く場合、変数のサニタイズは必須です。さもないとSQLインジェクションという危険な脆弱性の原因となります(参考↓)。少なくともユーザー入力を文字列結合してクエリを組み立てるのは絶対やめましょう。
RailsのActive Recordにはsanitize_sql_array
をはじめとするsanitize_sql_*
系メソッドがあります。生SQLを書く場合、特に変数を複数使う場合は、ぜひこれを使うべきです。
参考: ActiveRecord::Sanitization::ClassMethods
sanitize_sql_*
系メソッドは以前はprivateメソッドでしたが、Rails 5.2からはpublicになったので気兼ねなく使えます↓。
5.2からpublicやで https://t.co/lmuaeXnblI
— Ryuta Kamizono (@kamipo) August 8, 2018
更新情報