LoginSignup
2
0

More than 1 year has passed since last update.

【Ruby のまずいコード】正規表現で特定の文字を囲む

Last updated at Posted at 2021-12-09

お題

引数として与えられた文字列中の小書きの片仮名を 【 】 で囲んだ文字列を返すメソッドを書いてください。
小書きの仮名とは「っ」「ァ」などのことです1
ここでは,片仮名の「ァ」「ィ」「ゥ」「ェ」「ォ」「ッ」「ャ」「ュ」「ョ」「ヮ」のみを対象とします。

例えば

puts kogaki_mark("キャッシュ")
# => "キ【ャ】【ッ】シ【ュ】"

となります。

コード

def kogaki_mark(str)
  str.gsub(/[ァ|ィ|ゥ|ェ|ォ|ッ|ャ|ュ|ョ|ヮ]/, '【\0】')
end

問題点

このコードは,引数によっては正しく動作しません:

puts kogaki_mark("A|B") # => "A【|】B"

与えられた文字列中の | という文字が 【 】 で囲まれてしまいました。

正規表現の書き方が間違っているのです。
文字クラスの [ ] の中で選択2| を使ってしまっています。どうやらやりたいことを二重に表現してしまったようですね。

文字クラスの [ ] の中では | はメタ文字ではなくリテラル3なので,

[ァ|ィ|ゥ|ェ|ォ|ッ|ャ|ュ|ョ|ヮ]

という正規表現は「 とか | とか とか | とか とか | とか とか | とか とか | とか とか | とか とか | とか とか | とか とか | とか とか」という意味になります。
なお,[ ] の中に同じ文字が複数回書かれていても 1 回だけ書いたのと同じです。

正規表現の初心者でこのような書き方をしてしまう方は少なくありません。
テストした文字列に | が含まれていなければ正しい結果を返すので,ミスに気づきにくいのです。

改善

正しい書き方は

def kogaki_mark(str)
  str.gsub(/[ァィゥェォッャュョヮ]/, '【\0】')
end

または

def kogaki_mark(str)
  str.gsub(/ァ|ィ|ゥ|ェ|ォ|ッ|ャ|ュ|ョ|ヮ/, '【\0】')
end

です。

「いずれかの文字」という検索パターンは,このように,文字クラスでも選択でも書けます。
どちらがいいのでしょうか?
私は前者を勧めます。
一つの理由は,この例を見れば明らかなように,前者のほうが短いからです。
しかし,二つの文字のいずれか,という検索パターンだと

[AB]

よりも

A|B

のほうが 1 字短くなっています。
短さだけを追求するなら後者に軍配があがりますが,文字クラスで書くことにはほかにもいろいろ利点があります。

まず,保守性です。文字の種類を加減するとき,文字クラスで書いておけば当該の文字を追加・削除するだけで済みますが,選択で書くと | も一緒に追加・削除しなければなりません。

また,[ ] の中では,さまざまな記号がリテラルと解釈されることもポイントです。例えば,「丸括弧またはピリオド」は選択で書くと

\(|\)|\.

となりますが,文字クラスで書けば

[().]

のようにエスケープせずにすみます。

また,ほかの正規表現と組み合わせる場合には,文字クラスのほうが簡潔に済む場合があります。例えば「第一次産業」「第二次産業」「第三次産業」という文字列を検索したいとき,もちろん

第一次産業|第二次産業|第三次産業

とも書けますが,文字クラス を使って

第[一二三]次産業

と書けます。この [一二三] の部分を選択に置き換えようとすると,単純に 一|二|三 として

第一|二|三次産業

と書いたのではダメで

第(?:一|二|三)次産業

のようにグルーピングしなければなりません。
(?: )( ) とすれば若干簡素になりますがキャプチャーというオマケもついてきます4)。


  1. 小書きの仮名は拗音,促音,外来語その他の表記に使う。 

  2. 選言とも言う。要するに「または」のこと。 

  3. 正規表現として特別な意味をもたない「そのまんま」の文字のこと。英単語の「literal」には「文字通りの」という意味がある。 

  4. もちろんキャプチャーが必要な場合はそれでいいわけですが,不要なキャプチャーはパフォーマンスを落とし,複雑な正規表現ではキャプチャー番号が大きくなってややこしくなる。 

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0