正規表現間違い探しクイズ その3

正規表現間違い探しクイズシリーズです。

正規表現単体テストを書いている場合でもバグを発見しづらいものです。そのため、自身での検証が欠かせません。 今回は仕事中に見つけたものでなく、個人開発中にネットで見つけた間違いのうち印象的だったものを少し変えて紹介します。

問題編

仕様

  • 行頭及び行末の#記号を取り除くString#strip_hashを定義する
  • #記号は連続している可能性がある
  • #記号がない場合もある

動作イメージ

  • ###本文本文
  • #大切#大切
  • 普通普通

ソースコード

ソースコードです。 今回はRubyで実装します。

class String
  def strip_hash
    sub(/^#*([^#]+)#*$/, '\1')
  end
end

正規表現^#*([^#]+)#*$は、行頭に続き#の0回以上の繰り返し、続いて#以外の文字の繰り返し、#の0回以上の繰り返し、行末、を意味します。subメソッドを使用して、1つ目のグループ([^#]+)を抜き出しています。

f:id:yucatio:20190107194148p:plain

さて、この正規表現には明らかな間違いがあります。どのような間違いでしょうか。また、どのように修正するべきでしょうか。

テスト

以下のテストはパスしています。RSpecで書いています。

describe '#strip_hash' do
  subject { str.strip_hash }

  context '#が含まれているとき' do
    context '行頭に#がある場合' do
      let(:str) { '###文字列' }
      it { is_expected.to eq '文字列' }
    end

    context '行末に#がある場合' do
      let(:str) { '文字列###' }
      it { is_expected.to eq '文字列' }
    end

    context '行頭と行末に#がある場合' do
      let(:str) { '###文字列###' }
      it { is_expected.to eq '文字列' }
    end
  end

  context '#が含まれていないとき' do
    let(:str) { '文字列' }
    it { is_expected.to eq '文字列' }
  end
end

解答編

少し考えてから解答編を見てください

よいですか?

間違いの部分

入力する文字列の中間に#が存在する場合に、正規表現にマッチせず、行頭及び行末の#が削除されません。また、入力に#だけ含まれる場合もマッチしません。

テストを追加してみましょう。

describe '#strip_hash' do
  subject { str.strip_hash }

  context '#が含まれているとき' do
    # 略

    context '行頭と行末と文中に#がある場合' do
      let(:str) { '###文#字#列###' }
      it { is_expected.to eq '文#字#列' }
    end

    context '#だけの場合' do
      let(:str) { '###' }
      it { is_expected.to eq '' }
    end
  end
end

これを実行すると、以下の理由でテストが失敗します。

# 行頭と行末と文中に#がある場合
expected: "文#字#列"
     got: "###文#字#列###"

# #だけの場合
expected: ""
     got: "###"

どう直すか

このように書き換えてみました。

class String
  def strip_hash
    gsub(/(^#+)|(#+$)/, '')
  end
end

正規表現(^#+)|(#+$)は、行頭に続く#の1回以上の繰り返し、または`#``の1回以上の繰り返しの直後に行末にマッチします。gsubはパターンにマッチする部分をすべて置き換えるので、行頭と行末両方を置き換えることができます。

f:id:yucatio:20190107194211p:plain

テストも無事通りました。

あとがき

行頭と行末の全角空白と半角空白両方を削除する正規表現を探していて、問題に掲載したような正規表現が、"コレでできるよ"って紹介されていて、信じてしまったのですが、全然うまく動かねえ、と思って書いたのがこの記事です。

参考

元ネタはこちらです。

yucatio.hatenablog.com

文中の正規表現可視化ツールはこちらです→Regulex:JavaScript Regular Expression Visualizer