文字列補間

プログラミングにおいて、文字列補間(もじれつほかん、string interpolation)とは、文字列リテラル内に埋め込まれたプレースホルダーを実行時に評価し、そのプレースホルダーを対応する値に置き換える処理である。変数補間 (へんすうほかん、variable interpolation)、変数置換(へんすうちかん、variable substitution)、変数展開(へんすうてんかい、variable expansion)ともいう。この処理は、単純なテンプレートエンジンであり[1]、正式な用語で言えば準引用英語版の一形態である。文字列補間は、文字列連結よりも簡単でより直観的に文字列のフォーマットを規定できる[2]

文字列補間は、データの文字列表現を多用する多くのプログラミング言語C言語PerlPHPPythonRubyGroovyScalaSwiftなど、および多くのUnixシェル)で使用できる。

文字列リテラルの表現には、文字列補間が使えるものと、使えないもの(raw文字列)がある。プレースホルダーは、無名もしくは名前のついたシギル英語版で示される。一般的には$%が使用され、名前つきの場合は$placeholder%123のようになる。文字列の補間は実行時に行われる。

変種

[編集]

いくつかの言語は文字列補間に対応していない。例えばC言語printf関数では、第1引数が書式化文字列であり、第2引数以降に書式化文字列内のプレースホルダーを置き換える定数・変数や式が置かれる。

Rubyでは"#"を補間のための記号として使用するが、変数だけでなくどんな式でも補間できる。他の言語では、printfのような特別なフォーマット用の関数でより進んだ補間法に対応しているものもある。その場合、第1引数(フォーマット)で第2引数以降が代えられるパターンを指定する。

アルゴリズム

[編集]

文字列補間のための変数を展開するアルゴリズムには、主に2つの方法がある[3]

  1. プレースホルダーの置換と展開 (Replace and expand placeholders) : オリジナルの文字列を元にして、検索置換 (find-replace) 演算によって新しい文字列を作成する。プレイスホルダーを見つけたら、それを変数の値に置き換える。このアルゴリズムは、キャッシュが使用できない。
  2. 文字列の分割と結合 (Split and join string) : 文字列を配列に分割する。そして、それを対応する値の配列に合併し、最後に、全ての配列を結合する。分割した文字列は、再理用のためにキャッシュしておくことができる。

セキュリティ上の問題

[編集]

文字列連結と同様、文字列補間はセキュリティ上の問題を招く可能性がある。プログラマがきちんとユーザー入力データをエスケープするかフィルターに通すかしないならば、システムはSQLインジェクションスクリプトインジェクションXML外部エンティティインジェクション (XXE)、クロスサイトスクリプティング (XSS) などの攻撃にさらされることになる[4]

以下は、SQLインジェクションを引き起こす文字列補間の例である。

query = "SELECT x, y, z FROM Table WHERE id='$id' " 

ここで、$id"'; DELETE FROM Table; SELECT * FROM Table WHERE id='"に補間された場合、このクエリを実行するとテーブルの全てのデータが削除されてしまう。

[編集]

以下のPerlのコードは、PHPでも同じように動作する。

$name = "Alice"; print "${name} said Hello World to the crowd of people."; 

このコードは、Alice said Hello World to the crowd of people.と出力する。

apples = 4 print("I have $(apples) apples") # または print("I have {0} apples" % apples) 

上記のコードは以下のように出力する。

I have 4 apples 
var apples = 4; // Before C# 6.0 System.Console.WriteLine(String.Format("I have {0} apples", apples)); // Before C# 6.0 WriteLineメソッド自体が書式指定に対応しているため、簡潔にこう書ける。 System.Console.WriteLine("I have {0} apples", apples); // C# 6.0 System.Console.WriteLine($"I have {apples} apples"); 

[5]

上記のコードは以下のように出力する。

I have 4 apples 

Script syntax:

apples = 4; writeOutput("I have #apples# apples"); 

Tag syntax:

<cfset apples = 4> <cfoutput>I have #apples# apples</cfoutput> 

上記のコードは以下のように出力する。

I have 4 apples 
apples = 4 console.log "I have #{apples} apples" 

上記のコードは以下のように出力する。

I have 4 apples 
int apples = 4, bananas = 3; print('I have $apples apples.'); print('I have ${apples+bananas} fruit.'); 

上記のコードは以下のように出力する。

I have 4 apples. I have 7 fruit. 
def quality = 'superhero' def sentence = "A developer is a ${quality}" print sentence 

上記のコードは以下のように出力する。

A developer is a superhero 
var apples = 4; var bananas = 3; trace('I have $apples apples.'); trace('I have ${apples+bananas} fruit.'); 

上記のコードは以下のように出力する。

I have 4 apples. I have 7 fruit. 
def apples = 4; def bananas = 3; Console.WriteLine($"I have $apples apples."); Console.WriteLine($"I have $(apples + bananas) fruit."); 

上記のコードは以下のようにも書ける。

def fruit = ["apple", "banana"]; Console.WriteLine($<#I have ..$(fruit; "\n"; f => f + "s")#>); 

上記のコードは以下のように出力する。

apples bananas 
my $apples = 4; my $bananas = 3; print "I have $apples apples.\n"; print "I have @{[$apples+$bananas]} fruit.\n";  # Uses the Perl array (@) interpolation. 

上記のコードは以下のように出力する。

I have 4 apples. I have 7 fruit. 
<?php class foo {     var $foo;     var $bar;     function foo() {         $this->foo = 'Foo';         $this->bar = array('Bar1', 'Bar2', 'Bar3');     } } $foo = new foo(); $name = 'Jason'; echo <<<EOT My name is "$name". I am printing some $foo->foo. Now, I am printing some {$foo->bar[1]}. This should print a capital 'A': \x41 EOT; ?> 

上記のコードは以下のように出力する。

My name is "Jason". I am printing some Foo. Now, I am printing some Bar2. This should print a capital 'A': A 
# in Python 2 apples = 4 print "I have %d apples" % apples print "I have %(apples)d apples" % locals() # or in Python 2.6 print "I have {} apples".format(apples) print "I have {a} apples".format(a=apples) # or in Python 3 print("I have {apples} apples".format(**locals())) # or with Python 3.6 print(f"I have {apples} apples") 

[7] [8]

上記のコードは以下のように出力する。

I have 4 apples 
apples = 4 puts "I have #{apples} apples" # or puts "I have %s apples" % apples # or puts "I have %{a} apples" % {a: apples} 

上記のコードは以下のように出力する。

I have 4 apples 

Scala 2.10以降には、s, f, rawの3つの文字列補間子が実装されている。

f補間子はString.formatを呼び出すための組み込み表現で、書式付き文字列を書き直すコンパイラ・マクロである。

val apples = 4 //before Scala 2.10 printf("I have %d apples\n", apples) println("I have %d apples" format apples) //Scala 2.10+ println(s"I have $apples apples") println(f"I have $apples%d apples") 

[9] 上記のコードは以下のように出力する。

I have 4 apples 

Swiftでは、定数・変数・リテラル・式の組合せから、それらの値を文字列リテラルの中に含むことによって、新しい文字列の値を作ることができる。文字列リテラルに含むそれぞれのアイテムは、一対の括弧で囲まれ、その前にバックスラッシュ(日本語環境では円記号)が置かれる。

let apples = 4 print("I have \(apples) apples") 

上記のコードは以下のように出力する。

I have 4 apples 

バージョン1.4より、TypeScriptバッククォート `` を使用した文字列補間に対応した。以下はその例である。

var apples: number = 4; console.log(`I have ${apples} apples`); 

上記のコードは以下のように出力する。

I have 4 apples 

console.log関数はprintf関数と同じように使用できる。上記の列は、以下のようにも書き表せる。

var apples: number = 4; console.log("I have %d apples", apples); 

関連項目

[編集]

出典

[編集]