Kanazawa.rb meetup#132 祝11周年 LT大会での LT。 Ruby (Onigmo) ではエンコーディングによって、POSIX文字クラス名の見た目と挙動に差が出ちゃうよ、という話。
POSIX文字クラスでの躓き2023/08/26 (Sat) Kanazawa.rb meetup#132muryoimpl
View Slide
ある日の irb にて● 環境は、 ruby 3.2.2 (2023-03-30 revision e51014f9c0) [x86_64-linux]● 以下のコードを入力してみたが、イメージしていたものと違う動作をした。irb(main):001:0:> /[[:alnum:]]+/.match?(“10”): trueirb(main):002:0:> /[[:alnum:]]+/.match?(“あ0”): true[[:alnum:]] で “あ” で true ? なぜ?から調査は始まった。
ChatGPT さんに訊いてみたが……どうやら、私がおかしなことを言ってる感じですね
まず「Ruby 正規表現」でググる
正規表現のページを確認すると● [:alnum:] は POSIX 文字クラスまたはPOSIXブラケット というもの。また、[:^クラス名:]という記法でその否定を意味する。(以下、POSIXブラケットと呼ぶ)● 似たような機能を持つ記法に Unicode プロパティというものもある。○ \p{property-name}○ \p{^property-name} (否定)○ 具体例: \p{Letter}
エンコーディングによって挙動が変わるらしい。詳しくは Onigmo のドキュメントを見ろとある。
Onigmo のドキュメント(日本語版)を見るhttps://github.com/k-takata/Onigmo/blob/master/doc/RE.ja
Unicodeプロパティ● Rubyのデフォルトエンコーディングは UTF-8irb(main):001:0:> "あ".encoding: #● alnum は Unicodeプロパティの Letter | Mark | Decimal_Number に相当する。これは Ruby の正規表現のページにも同じ記載があった。● Mark と Decimal_Number はぼんやりとイメージが湧くが、Letter の範囲は?○ https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp にて、Unocodeプロパティを入力して範囲を確認してみる
ブラウザでアクセスして確認する
Unicodeプロパティ \p{Letter}● ひらがな、漢字等かなりの広範囲の”文字”が含まれていた。● Onigmo のドキュメントにあったとおり、エンコーディングが Unicode の場合において、 実はほとんどすべての文字をカバーしてしまう● つまり、alnum は alphabet と number ではない。 Letter が alphabet のみならず、広範囲の文字をカバーしてしまうため● 英数字のみならば、/ [a-zA-Z0-9]+/.match?(“あ”) みたいに、明示的に範囲を指定するか● 正規表現のページのオプションによると、/(?a:[[:alnum:]]+)/.match?("あ") のように、オプションで動作が変えられるようだ
d, u は、UnicodeではPOSIXブラケットは非ASCII文字にマッチしてしまう。a は、POSIXブラケットは非ASCII文字にマッチしないので、:alnum: は英数字にマッチする
まとめ● Rubyのデフォルトエンコーディングは UTF-8 、正規表現でPOSIXブラケットを見たままのイメージで使うと痛い目に遭う● Onigmo の POSIXブラケットはエンコーディングによって挙動が変わるため、使う場合は文字列のエンコーディングを意識する必要がある● 正規表現のみならず、処理を実装するときは失敗パターンも確認しましょう● ChatGPT さんの意見を鵜呑みにしてはいけない