rubyの正規表現を扱う際の文字セットについて


rubyの正規表現を扱う際の挙動についてよくわからないことがあります。  
HTMLのパースをしているのですが、

[code]

    Regexp.new("スタックオーバーフロー")
    => /スタックオーバーフロー/
    Regexp.new("<h1>スタックオーバーフロー</h1>")
    => /<h1>\u30B9\u30BF\u30C3\u30AF\u30AA\u30FC\u30D0\u30FC\u30D5\u30ED\u30FC<\/h1>/
    
[/code]

というような、上はUTF-8そのままで正規表現化できるのですが下の場合はunicodeがそのまま出てきてしまうという動きになっており、なぜこのようになるのかわかりません。  
理想としては

[code]

    => /<h1>スタックオーバーフロー<\/h1>/
    
[/code]

という正規表現がほしいのですが、どのようにすればよいでしょうか？  
あまり文字コードや文字セットなどに詳しくないので見当ハズレなことを聞いているのかもしれませんが、よろしくお願いします。




>
[code]

>     Regexp.new("<h1>スタックオーバーフロー</h1>")
>     =>
> /<h1>\u30B9\u30BF\u30C3\u30AF\u30AA\u30FC\u30D0\u30FC\u30D5\u30ED\u30FC<\/h1>/
>  
[/code]

これはirbやpryで上記コードを実行した結果か`p`の類いで表示した結果だと思います。表現上\uxxxx形式になっているだけで、渡した文字列に対する正しい正規表現オブジェクトが生成されてます。

[code]

    Regexp.new("<h1>スタックオーバーフロー</h1>") == /<h1>スタックオーバーフロー<\/h1>/
    => true
    
[/code]

こうなってしまう理由はよくわかりませんが、`/`を`/regexp/`形式に含めるにはエスケープが必要なので、巻き添えで非ASCII文字もエスケープされてるんではないかなと想像します。

なおHTMLを正規表現でパースするのはたいていの場合よくない方法です。Nokogiriという定番のHTML/XMLパーサライブラリがあるので、そちらを使うことも検討した方がよいとおもいます。




irbは式の評価結果に対して`inspect`メソッドを呼び出して得た文字列を出力します。つまり、そのような表示になっているのは[`Regexp#inspect`](https://docs.ruby-
lang.org/ja/latest/method/Regexp/i/inspect.html)の動作です。  
※
irbのコマンドオプションや`conf.inspect_mode`に代入することで`to_s`を使う等に変更できます。詳しくは[irbのドキュメント](https://docs.ruby-
lang.org/ja/latest/library/irb.html)を確認して下さい。  
※ `p`も同様に`inspect`を使用しますので、`p`を使った場合も同じになります。

どうしてそうなるかはソースコードを見る必要があります。`Regexp#inspect`の通常処理で文字列の生成を行っているのは[rb_reg_expr_str()](https://github.com/ruby/ruby/blob/ruby_2_5/re.c#L357)になります。この関数では、"/"が含まれていない場合はそのまま出力します。しかし、"/"が含まれている場合は、前後を"/"で囲む関係上"\/"とエスケープする必要があるため、分岐した別処理になります。問題はこの分岐した処理で、"/"以外の通常の文字は[rb_str_buf_cat_escaped_char](https://github.com/ruby/ruby/blob/ruby_2_5/string.c#L5721)で処理を行うと言うことです。rb_str_buf_cat_escaped_charではASCII文字以外は全てエスケープ(\uや\xを使う)するため、日本語の部分はエスケープされる事になります。  
※ 実際はさらに細かい処理をしています。上はだいたいの動作概要ですので、ご注意下さい。

注意して欲しいのは、Regexpの中に保存されている正規表現の文字列(`Regexp#source`で確認できる)では"/"が"\/"にエスケープされていないと言うことです。このエスケープは`/.../`というリテラル表記のみで必要なものであるため、内部処理としてはエスケープしておく必要がありません。しかし、`Regexp#inspect`ではわかりやすいリテラル表記になるようにするため、出力時のエスケープ処理が必要になっており、その副作用で、ASCII以外は全てエスケープするとなったということです。

"/"をエスケープするには、一旦文字列をバラバラに解釈していかなくてはいけないため、間違った分割をしないようにこのような処理になっているのかも知れません。ただ、エスケープせずに出す方法がないわけではないでしょうから、[Ruby
Issue](https://bugs.ruby-lang.org/)に投げるなり、自分でパッチを作って送りつけるなりすると直してくれるかも知れません。

