こんにちは。3回目となる今回は、文字列操作についてお話ししていきます。非常に身近で簡単に使っていますが、細かく見ていくと結構奥深いのが、この文字列操作です。
PHP技術者認定試験の上級では、こういった言語仕様の細部にも突っ込んでくることがありますので、少し詳しく見ていきましょう。
(クリックで拡大)
echoとprint
まずはPHPで文字列を出力する際に使われるechoとprintです。この2つは全く同じ動作をするように見えますが、何か違いがあるのでしょうか?私の感覚だとPHPからプログラミングを始めた方はecho、他の言語から入って来た方はprintを使う傾向があるように思います。
echoもprintも関数のように使われていますが、実はrequireやexitと同様に言語構造です。言語構造とはあらかじめ組み込まれているキーワードのことで、一見関数のようですが、使い方が関数とは異なっています。両者の違いとしてはprintは関数のように使用できますが、echoはそのような使い方ができません。「関数のように使用できない」というのは、以下のような書き方ができないということです。
3_1.php
2 | $string ? echo $string : echo null; |
上記のサンプルは、実行しようするとエラーになります。
出力結果
2 | Parse error: syntax error, unexpected T_ECHO in D:\xampp\htdocs\test\php_col\3_1.php on line 5 |
どうしてもechoを使いたければ、以下のように書けば良いだけですが…
1 | echo $string ? $string : null; |
一方printであれば、以下のような書き方ができます。
1 | $string ? print $string : print null; |
関数のように使えるprintは、常に1を戻り値として返します(3_2_print_re.php)。echoは戻り値を返さないため、同じような記述をするとエラーになります(3_2_echo_re.php)。
3_2_print_re.php
2 | $result = print 'Hello World!'; |
3_2_echo_re.php
2 | $result = echo 'Hello World!'; |
result
2 | Parse error: syntax error, unexpected T_ECHO in D:\xampp\htdocs\test\php_col\3_4_echo_re.php on line 3 |
では、色々な書き方を試してみましょう。以下のようなシンプルな書き方は、echo、printのいずれでも可能です。
カッコ付きの表記も可能です(3_4_echo.php、3_4_print.php)。
これに対して、複数の引数を指定する場合は出力に差が生じます。echoはカンマで複数の引数を指定できますが、printではできません(3_5_echo.php、3_5_print.php)。
3_5_echo.php
3 | echo 'Hello World!', 123, $ab; |
3_5_print.php
3 | print 'Hello World!', 123, $ab; |
出力結果
3 | Parse error: syntax error, unexpected ',' in D:\xampp\htdocs\test\php_col\3_4_print.php on line 4 |
また関数のように使えると言っても、echo、printのいずれも可変関数としては使えません。
3_6_echo.php
3 | $testFunction('Hello World!'); |
出力結果
3 | Fatal error: Call to undefined function echo() in D:\xampp\htdocs\test\php_col \3_6_echo.php on line 4 |
3_6_print.php
2 | $testFunction = 'print'; |
3 | $testFunction('Hello World!'); |
result
3 | Fatal error: Call to undefined function print() in D:\xampp\htdocs\test\php_col\3_6_print.php on line 4 |
以上の結果を関数ごとにまとめると、以下の表のようになります。
図2:echoとprintの違い(クリックで拡大)
シングルクォートとダブルクォート
文字列などを囲う際に使われる、シングルクォート「'」とダブルクォート「"」は何が違うのでしょう? どちらも動作的には同じように見えるので、深く考えずに使っている方も多いかと思います。(バッククォート「`」は、また別物です。バッククォートで囲んだ文字列はシェルとして実行されてしまうので、間違えないように注意してください。)
まずは普通に使ってみましょう。
出力結果
1 | >php 3_7_singlequote.php |
result
1 | >php 3_7_doublequote.php |
普通に文字列を表示させるだけなら、同じですね。では、どこが異なるのかと言うと、ダブルクォートでは変数やエスケープの展開ができる点です。
3_8_singlequote.php
3 | echo 'Hello $test!\nhello hello'; |
出力結果
1 | >php 3_8_singlequote.php |
2 | Hello $test!\nhello hello |
3_8_doublequote.php
3 | echo "Hello $test!\nhello hello"; |
出力結果
1 | >php 3_8_doublequote.php |
このように、ダブルクォートは変数やエスケープを展開できますが、シングルクォートはそのまま出力されてしまいます。
クォートの種類によるパフォーマンスの差
「ダブルクォートは変数展開やエスケープ処理などを行っているため、シングルクォートよりも遅く、通常の文字列を扱う場合はシングルクォートに統一すべき」という意見を、見聞きすることがあります。これは本当でしょうか。この辺りは少し疑問に思ったので、調べてみることにしましょう。「憶測するな、計測せよ」です。
3_9_doublequote.php
02 | $startTime = microtime(true); |
05 | for($i = 0; $i < 100000; $i++){ |
06 | echo "Hello $test!\nhello hello ${test}\n"; |
11 | echo microtime(true) - $startTime, '秒'; |
出力結果
1 | >php 3_9_doublequote.php |
3_9_singlequote.php
02 | $startTime = microtime(true); |
05 | for($i = 0; $i < 100000; $i++){ |
06 | echo 'Hello $test!\nhello hello ${test}'; |
12 | echo microtime(true) - $startTime, '秒'; |
出力結果
1 | >php 3_9_singlequote.php |
2 | Hello $test!\nhello hello ${test} |
3 | Hello $test!\nhello hello ${test} |
ダブルクォート |
シングルクォート |
10.779225111008秒
11.07807302475秒
10.614315986633秒 |
11.040370941162秒
10.77766084671秒
10.541589021683秒 |
何も変わりませんね。
確かにダブルクォートは変数やエスケープの展開処理がありますが、このように実際に動かして比較してみるとたいして違いが出ません。というよりほとんど一緒です。
「ぶっちゃけどっちもそんなに変わらない」ということがわかったので、どちらでも使いやすいほうを選びましょう。ただしチームで開発する場合は、宗教戦争になりがちなので、あらかじめコーディング規約で定めておくのが良いのではないでしょうか?
printfとsprintf
似たような文字列成形系の関数として、printfとsprintfがありますが、両者はどこが違うのでしょう?PHPマニュアルを見てみましょう。
printf |
フォーマット済みの文字列を出力する |
sprintf |
フォーマットされた文字列を返す |
それぞれ、フォーマットに従って文字列を成形してくれる点は一緒ですが、printfが結果を画面に出力してくれるのに対し、sprintfは値を返します。sprintfは成形した文字列を変数に入れたり、関数に引数として与えたい場合などに利用できます。
sprintf は出力を行いませんので、成形後に出力する場合は
とする必要があります。以下の例のように、いったん文字列に一回格納してから使いたい場合に便利ですね。
PHPは自動で型変換してくれるので、次に示す3_10_yen.phpのような書き方はあまりしませんが、数字を文字列に入れる時はこのようにします。
3_10_yen.php
2 | printf("今お財布には%d円あります", $yen); |
またprintfは、下記のように桁数が足りない場合に先頭を0で埋めて桁をそろえたり、年月日を揃えたりするのによく使われます。
3_10_time.php
2 | printf("%04d-%02d-%02d", 2010, 8, 15); |
今までなんとなく使って来た方も多いと思われる文字列操作ですが、細かいところを見ていくと実はこのように色々とクセがあります。PHPは自由な言語と言われていますが、その自由さを許容するために、このようなクセがあるのです。こういった細部にも気を配ってコードを書いていくと、より良いコードになるのではないでしょうか?
次回はオブジェクトと参照渡しについてお話ししていきます。