こんにちは!Webコーダー・プログラマーの貝原(@touhicomu)です。
今日は、Rubyの文字列の比較について書きたいと思います。
Rubyの文字列の比較のしかたは、色々あって、意外と難しいですよね。
Rubyの文字列の比較演算子をちょっと並べてみますと、
- 等号メソッド
- ==
- ===
- eql?
- equal?
- 不等号メソッド
- <
- >
- <=
- >=
と、本当に様々です。
(なお、Rubyでは、比較演算子は、実質上、比較メソッドというメソッドで実装されています。)
これだけたくさん比較のメソッドがあると、「いろいろな比較メソッドがあるけど、どう違うの?」という疑問が出てきそうですね。
また、「文字列の不等号の比較ってどういう意味なの?」という疑問もわいてきます。確かに、文字列同士を不等号で比較するなんて、どんな意味があるかわかりにくいですよね。
そこで!今回は、Rubyの文字列の比較メソッドのすべてについて、分かりやすく整理してまとめてみました。
等号メソッド
比較メソッド「==」
文字列の等号比較メソッド「==」は、文字列の「値」が一致しているかどうかを比較します。
文字列の「値」とは、文字列そのものです。”abc”という文字列があったら、文字列の値は”abc”です。
文字列の値が一致しているかどうかを比較してみましょう。
サンプルコード:
if "abc" == "abc" then p "一致" else p "不一致" end
サンプルコードの実行結果:
"一致"
文字列の値”abc”が一致していることが分かりますね。
今度は、文字列の値が一致しない場合を確認してみましょう。
サンプルコード:
if "abc" == "def" then p "一致" else p "不一致" end
サンプルコードの実行結果:
"不一致"
今度は、文字列の値”abc”と”def”が一致しているかどうかを確認していますので、結果は不一致となっています。
このように、文字列の比較メソッド「==」は、文字列の「値」そのものが一致しているかどうかを比較します。
比較メソッド「===」
文字列の等号比較メソッド「===」は、先ほど解説した等号比較メソッド「==」と同じ比較を行います。
同じ比較を行うなら、なぜ「===」というメソッドがあるのでしょうか?
実は、「===」は、case文で暗黙的に使用するために用意されている専用の比較メソッドなのです。
実際に、両方のやり方を試してみましょう。
サンプルコード:
["a","b","z"].each do |str| case str when "a" then p "A" when "b" then p "B" else p "C" end end ["a","b","z"].each do |str| if str === "a" then p "A" elsif str === "b" then p "B" else p "C" end end
サンプルコードの実行結果:
"A" "B" "C" "A" "B" "C"
全く同じ結果になりましたね。
「===」比較メソッドは、case用ですので、普段は「==」を使用してOKです。
比較メソッド「eql?」
文字列の等号比較メソッド「eql?」も、「==」と全く同じ比較を行います。
サンプルコード:
abc ="abc" abc2 = "abc" if abc.eql? abc2 then p "一致" else p "不一致" end
サンプルコードの実行結果:
"一致"
なぜ、「==」と全く同じ機能のメソッドがあるのでしょうか?
一つは、可読性のためです。
「==」は「同じ値」という意味を表すRuby上の式ですが、数学では単に「=」と書きます。
このように、記述に違いがあり、あいまい性があるため、コードを読んでいる人がコードの意味を読み違う場合もあります。
また、誤って代入演算子「=」を使用してしまう可能性もあります。
そこで、Rubyでは、英文ではありますが、文字風の記述「eql?」も用意しています。これなら、意味を読み違えることもありません。「?」という語尾疑問形もついていて、より意味が把握しやすいですね!
比較メソッド「equal?」
文字列の等号比較メソッドの最後は、「equal?」です。
このメソッドは要注意です。
なぜ、「eql?」メソッドがあるのに、わざわざ似たような形の「equal?」メソッドがあるのでしょうか?
それは、「eql?」メソッドではできない、より厳密な等号比較を行うためです。
「eql?」メソッドは「==」メソッドと同じ比較を行うと上にも書きましたが、「==」メソッドは、そもそも文字列の「値」が等しいかどうかを比較するメソッドでした。
実は、Rubyのプログラミングをする上で、この「==」の文字列の「値」が一致するかどうかしか比較できない点が問題になってきます。
例えば、変数abcの値が”abc”で、変数abc2の値が”abc”だったとします。
abcとabc2は文字列としての「値」は、ともに”abc”です。
しかし、abcとabc2は、別々にClass.newされた変数だった場合、abcとabc2のオブジェクトはそれぞれ別のオブジェクトです。
この場合、
- 文字列の値が同じ値かどうかを比較して処理を分けたい。
- オブジェクトが一致するかどうかを比較して処理を分けたい。
という、別々のプログラムの書き分け方があります。
1.の場合は、if文内で、「==」もしくは「eql?」を使用することで対処できます。
しかし、2.の場合は、「==」や「eql?」では対処できません。どちらも「値」が一致するため、trueとなりますがが、ここではそれぞれのオブジェクトが違うため、falseとならなければいけません。
ここで使用するオブジェクトの等号比較メソッドが「equal?」です。
実際に、同じ値で別々のオブジェクトを生成して、「equal?」メソッドで比較してみましょう。
サンプルコード:
def check(val1,val2) if val1.equal? val2 then p "一致" else p "不一致" end end abc = "abc" #Class.newと等価 abc2 = "abc" #Class.newと等価 check(abc,abc2)
サンプルコードの実行結果:
"不一致"
値が同じオブジェクトでも、「equal?」で比較すると、別々のオブジェクトを比較した場合は、falseになることがわかりましたね。
比較メソッドの注意点
ところで、数値型クラスと文字列型クラスは、別々のクラスです。
そもそも、クラスが違うため、比較すると、「両者は違うモノ」という結果が返ってきます。
実際に両者を比較すると、等号比較メソッドの「==」や「===」、「eql?」、「equal?」は、falseを返します。
サンプルコード:
def check(val1,val2) if val1.eql? val2 then p "一致" else p "不一致" end end abc = 1 #数値の1 abc2 = "1" #文字列の"1" check(abc,abc2)
サンプルコードの実行結果:
"不一致"
数値の「1」と文字列の”1″は、別の「値」であることが分かりました。
実際にRubyは、両者は「別の値である」と認識します。
ただ、「値」を比較するメソッド「==」、「===」、「eql?」を使用する場合は、値を一致させれば良い訳です。
その例として、ここで、数値を文字列の値へ変換するメソッド「to_s」を使って、値を一致させてみましょう。
サンプルコード:
def check( val1, val2 ) # to_s した後に比較 if val1.to_s.eql? val2.to_s then p "一致" else p "不一致" end end abc = 1 #数値の1 abc2 = "1" #文字列の"1" check(abc,abc2)
サンプルコードの実行結果:
"一致"
今度は、結果が”一致”になりました。
数値の変数のto_sメソッドで、数値を文字列の値にしたため、両者の「値」が一致し、「eql?」メソッドがtrueを返しています。
この方法は、「eql?」メソッドと等価な「==」、「===(caseも同様)」において使用できます。
to_sメソッドは、ほとんどのクラスに使えるため、比較を行う際は、比較対象を一旦「to_s」で文字列の値にしてから比較すると、思い通りの結果を得られます。
この方法は、覚えておくと便利です。
不等号比較メソッド
文字列の不等号比較メソッドは、以下の4つがあります。
- <
右辺より左辺が先に辞書に載っている場合はtrue
そうでない場合はfalse - <=
右辺よりか、または、右辺と同じ順番で、左辺が先に辞書に載っている場合はtrue
そうでない場合はfalse - >
左辺より右辺が先に辞書に載っている場合はtrue
そうでない場合はfalse - >=
左辺よりか、または、左辺と同じ順番で、右辺が先に辞書に載っている場合はtrue
そうでない場合はfalse
辞書とは、一般にある英語辞書や国語辞書のことです。
不等号メソッドのそれぞれは、両辺の文字列が辞書引きでどちらが先に載っているかどうかを判定します。
辞書引きで先に載っているかどうか、ということについては、以下の事例を参考にされてください。
- ”abcd”と”efghi” では、”abcd”の方が先に辞書に載っている
- ”abcd”と”bbcd”では、”abcd”の方が先に辞書に載っている
- ”zyxw”と”ABCD”では、”zyxw”の方が先に辞書に載っている
(Rubyの設計上、大文字は小文字の後です) - ”あいうえお”と”かきくけこ”では、”あいうえお”が先に辞書に載っている
- ”あいうえお”と”アイウエオ”では、”あいうえお”が先に辞書に載っている
(Rubyの設計上、カタカナは、ひらがなの後です)
なお、日本語の漢字だけは、Rubyの不等号比較メソッドでは正確に比較できません。
なぜなら、漢字の辞書は漢字は音読み・訓読みもありますし、読み方も複数あるため、単純なよみがなだけでは、辞書に載っている順番が決まらないためです。
基本的に日本語をRubyの不等号比較メソッドで比較する際は、ひらがな、カタカナ、全角英数字のみ有効だと考えておいてください。
サンプルコード:
if "abcd" < "efghi" then p "abcd" else p "efghi" end if "abcd" <= "bbcd" then p "abcd" else p "bbcd" end if "あいうえお" > "かきくけこ" then p "あいうえお" else p "かきくけこ" end if "あいうえお" >= "アイウエオ" then p "あいうえお" else p "アイウエオ" end
サンプルコードの実行結果:
"abcd" "abcd" "かきくけこ" "アイウエオ"
まとめ
いかがでしたでしょうか。
数値の比較と比べて、文字列の比較は使用頻度は少ないですが、使い分けを整理して理解しておかないと、用途を間違い思わぬバグを起こしてしまう原因にもなります。
この機会に、文字列の比較メソッドの使い分けを整理して理解されておくと、今後の役に立つかと思います。
そうすれば、Rubyが大変使いやすいものになります。
文字列の比較メソッドの使い方を忘れたら、またこの記事を読み返してみてください。
Rubyの文字列をしっかり理解したい方に
この記事は文字列を体系的に解説した3ステップで簡単! Rubyの文字列を 完全理解の一部となります。
文字列をさらに理解したい方はこちらを読んでどんどん理解を深めていくようにしましょう。