「長い文字列を配列に切り出す」処理はよく必要となりますが、ときには手こずったりもします。
今回は、'foo_bar_baz_quz'
のような文字列を、アンダーバーの直後で区切って['foo_', 'bar_', 'baz_', 'quz']
のようにしようとして、きれいな方法はないかと探し回った話です。
String.prototype.split
まず、「文字列を配列に切り分ける」といって真っ先に出てくるであろうものが、String.prototype.split
(MDN)です。
いちばん平凡な挙動
.split
の引数に文字列を書くと、そこで切れた上で切れ目の文字は削除されます。とりあえず、str.split('_')
のようにすれば切り分けはできますが、削除された_
を各要素に戻す必要が出てきます。
0文字にヒットさせる
.split
の引数としては正規表現も指定できますが、正規表現に言明と呼ばれる機能があります。^
や$
は使う人も多いかと思いますが、文字ではなく文字と文字の間にマッチさせることが可能なのです。
ただ、JavaScriptの正規表現には先読みはあっても戻り読みがないので、str.split(/(?=_)/)
のようにしてアンダーバーの「直前の位置」を切れ目にすることはできますが、直後はうまく行かなさそうです。
切れ目も取得する
.split
に与える正規表現としてキャプチャのカッコが入ったものを与えると、キャプチャされた中身が結果の配列に追加されるようになります。ただ、素直にstr.split(/(_)/)
のようにしても、foo, _, bar,
のように_
だけで1つの要素になってしまいます。
ここで気分を変えて、foo_
全体が切れ目になるように、/([^_]*_)/
を切れ目にしてみれば、一体として取ることができるようになります…が、「切れ目」どうしが連続するので、'foo_', '', 'bar_'
のようにから文字列が入ってしまいます。ただ、空文字列はfalsyなので、.filter(str => str)
のようにして比較的容易に取り除けはします。
別なメソッドを使ってみる
「連続して正規表現にヒットさせる」という戦略を取るのであれば、別にsplit
である必要性はなくなります。別な正規表現のメソッドを使ってみましょう。
RegExp.prototype.exec
ふつうに正規表現をマッチさせるメソッドです。/([^_]+_)*/
のようなものをマッチングさせてみたのですが、カッコを*
などで複数回適用してもキャプチャされるものの数は増えず、最後の1つしかキャプチャできませんでした。これでは切り分けには使えません。
String.prototype.match
.match
の場合、/.../g
となっている正規表現を使えば、一度に複数個をマッチさせることができます。結果、'foo_bar_baz_quz'.match(/([^_]*_|[^_]+$)/g)
のような形で、目的の配列を一発で得られるようになりました。
結論
正規表現は、書き方だけでなく、どのような形で使うかでもできることは違ってくる