Ruby におけるバイナリ文字列に対する正規表現マッチング
Ruby 1.9 系において、バイナリ文字列 (ASCII-8BIT の String
オブジェクト) に対する正規表現マッチングをさせる方法について記します。 Ruby 1.9 系において URL デコードを行う際などに役に立ちます。
正規表現リテラルの n
オプション
正規表現リテラルの後ろに n
と書くことで、その正規表現をバイナリ扱い (エンコーディング ASCII-8BIT) にすることができます。 ASCII-8BIT は ASCII 互換なので、ASCII 文字列を中身に書くことができます。
regexp = /abcd/n
しかし、ASCII に含まれないものを中に入れるとエラーになります。
regexp = /abcdあ/n
この結果は以下のようになります。
SyntaxError: (irb):1: regexp encoding option 'n' differs from source encoding 'UTF-8' /.../n has a non escaped non ASCII character in non ASCII-8BIT script: /abcdあ/ from /usr/local/bin/irb:12:in `<main>'
じゃあどうすればいいかというと、16 進表記のエスケープ文字 ((正式名称がよくわからないのでこう書いてます。。 "\xXX
" 形式のアレです。)) を使います。
regexp = /abcd\xEE\xF0/n
URI デコードを行う関数
まあ ASCII-8BIT の正規表現はあんまり使い道はないと思いますが、例えば URI デコードなど、文字列のエンコードやデコードの際には役に立ちます。 ここに URI デコードの例を書いておきます。
## # URI デコードを行う # # デコード対象になるのは文字列に含まれる '%XX' 形式の全ての文字. # デコード対象の文字が妥当かどうか (16 進数表記に合致するか) やデコード後の文字列の妥当性 # (文字エンコーディングが正しいか) などのチェックはしない. # デコード後の文字列のエンコーディングは第 2 引数で指定. (デフォルトは UTF-8) def uridec( str, enc = Encoding::UTF_8 ) # 与えられた文字列を ASCII-8BIT 扱いにし、ASCII-8BIT の正規表現でマッチングする str.force_encoding(Encoding::ASCII_8BIT).gsub( /%../n ) do |s| [s[1,2].to_i(16)].pack('C') end.force_encoding(enc) # 指定のエンコーディングにして返す end