ちくちく日記

DTP系備忘録。真面目にやってます。

rubyで正規表現

rubyでテキスト処理をやってるんだけど、簡単そうなところなのにつまずいてしまった。

InDesignタグの入った、こういうテキスト↓があるとして

'111×222〜333<4444>の555<666a>7777袋88セットが'

これを、タグの部分に影響をあたえず、2桁と3桁の数字のみにタグ付けしたい。
(つまり、InDesignの縦中横で2桁と3桁になる部分は、等幅半角、等幅3分字形に置き換える、というよくある処理。車輪の再発明の気がしまくり。)

書いてみたのはこういう処理

#全角数字を半角に
def zenkaku_to_hankaku(change_txt)
change_txt.tr!('0-9','0-9')
return change_txt
end
#半角数字を全角に
def hankaku_to_zenkaku(change_txt)
change_txt.tr!('0-9','0-9')
return change_txt
end

test_txt = '111×222〜333<4444>の555<666a>7777袋88セットが'

#3桁のタグ付け

##すでに入っているタグを全角にして避難
test_txt.gsub!(/(<[^>]+>)/){hankaku_to_zenkaku($1)}
##数字以外の文字で挟まれた3桁数字をタグ付け
test_txt.gsub!(/([^0-9])(\d{3})([^0-9])/){$1 + "<たぐ>" + $2 + "<たぐ>" + $3}
##行の先頭にある3桁数字をタグ付け
test_txt.gsub!(/^(\d{3})([^0-9])/){"<たぐ>" + $1 + "<たぐ>" + $2}
##行の最後にある3桁数字をタグ付け
test_txt.gsub!(/([^0-9])(\d{3})$/){$1 + "<たぐ>" + $2 + "<たぐ>" }
##退避してたタグを半角に戻す
test_txt.gsub!(/(<[^>]+>)/){zenkaku_to_hankaku($1)}

行中、行頭、行終わりと3回に分けて変換しているのが、なんともかっこわるい感じ。
でも

test_txt.gsub!(/([^0-9]?)(\d{3})([^0-9]?)/){$1 + "<たぐ>" + $2 + "<たぐ>" + $3}

とすると、4桁数字などもひっかかってしまうので…。

で、これを実行すると、結果が
'<たぐ>111<たぐ>×<たぐ>222<たぐ>〜333<4444>の<たぐ>555<たぐ><666a>7777袋88セットが'

問題。'111×222〜333'の'333'が処理されてません…orz

多分、([^0-9]?)(\d{3})([^0-9]?)で'×222〜'が処理されるので、その後に続く'333'が引っかからないのかな…。
正規表現についての知識が足りてない気がします。

この問題を解決しつつ、もっとかっこよく書くにはどうしたらいいんでしょう?教えて偉い人!




=======追記==========
偉い人に教えていただき、解決しました。

test_txt.gsub!(/(^|[^0-9\n])(\d{3})(?!\d)/) {"#{$1}<たぐ>#{$2}<たぐ>"}

と書けばよいのだそうです。

(?!\d)という書き方を初めて知りました。否定先読みというのですね。
勉強になりました!