String#splitの引数に正規表現を使うと、組み込み変数の
- \$~(最後に成功したマッチのMatchData)
- \$&(最後にマッチに成功した文字列全体)
- \$1、\$2、\$3...(最後に成功したマッチのn番目の括弧に対応する文字列)
等のデータがすべてnilになってしまいます。
irb(main):001:0> "abc" =~ /(ab)c/
=> 0
irb(main):002:0> [$~, $&, $1]
=> [#<MatchData "abc" 1:"ab">, "abc", "ab"]
irb(main):003:0> "123".split("2")
=> ["1", "3"]
irb(main):004:0> [$~, $&, $1]
=> [#<MatchData "abc" 1:"ab">, "abc", "ab"]
irb(main):005:0> "123".split(/2/)
=> ["1", "3"]
irb(main):006:0> [$~, $&, $1]
=> [nil, nil, nil]
文字列で分割する場合は基本的に大丈夫ですが、空文字列(1文字ごとに分割)だと正規表現の場合と同じになってしまいます。
irb(main):007:0> "abc" =~ /(ab)c/
=> 0
irb(main):008:0> [$~, $&, $1]
=> [#<MatchData "abc" 1:"ab">, "abc", "ab"]
irb(main):009:0> "123".split("")
=> ["1", "2", "3"]
irb(main):010:0> [$~, $&, $1]
=> [nil, nil, nil]
Cはよくわからないんですが、ソースをちらっと覗いた感じでは引数が""
の場合のみ//
に変換して処理しているっぽいです。
これってバグなんですかね?仕様?
いずれにしても、組み込み変数とかわかりにくいしMatchDataオブジェクトを使えってことですね!
<追記>
コメントで、「〈区切り〉を探すので,最後は必ず失敗する」のではないかと指摘をいただきました。
確かに正規表現マッチに失敗すればこれらの変数はnilになります。
ということはもしかして……と思って実験してみました
irb(main):007:0> "abc" =~ /(ab)c/
=> 0
irb(main):008:0> [$~, $&, $1]
=> [#<MatchData "abc" 1:"ab">, "abc", "ab"]
irb(main):009:0> "12345".split(/[24]/, 3)
=> ["1", "3", "5"]
irb(main):010:0> [$~, $&, $1]
=> [#<MatchData "4">, "4", nil]
split
の第2引数には分割の最大個数を指定できます。
これを指定しなければ文字列の最後までマッチして失敗するが、整数nを指定すればn-1回目のマッチが成功すればそこで終了するのでマッチに失敗しない、という理解で良さそうです。