前提
文章を句点 (。) で改行して、1行に1文だけ保持されているような文字列を得たいです。
たとえば以下の文章があるとき、
叢の中からは、暫く返辞が無かった。しのび泣きかと思われる微かな声が時々洩れるばかりである。
ややあって、低い声が答えた。「如何にも自分は隴西の李徴である」と。
以下の出力を得たいです。
叢の中からは、暫く返辞が無かった。
しのび泣きかと思われる微かな声が時々洩れるばかりである。
ややあって、低い声が答えた。
「如何にも自分は隴西の李徴である」と。
方法
正規表現の先読みアサーション (Lookahead assertion, たんに先読みとも) を使います。
先読みアサーションとは、「後ろに x という表現が続くような(または続かないような) exp」にマッチさせる表現のことです。
種類 | 記述例 | マッチ例 |
---|---|---|
ポジティブアサーション | /exp(?=ress)/g | 'express explosion experiment' ではじめの exp だけマッチ |
ネガティブアサーション | /exp(?!ress)/g | 'express explosion experiment' で後ろ2つの exp だけマッチ |
当然後読みアサーション (Lookbehind assertion) というものもありますが、今回は使わないので割愛します。
環境
Bun 1.1.3
今回はJSでコードを書きますが、正規表現なのでたいていどこでも同じように実装可能です。
コード例
失敗例
const text = ' 叢の中からは、暫く返辞が無かった。しのび泣きかと思われる微かな声が時々洩れるばかりである。\nややあって、低い声が答えた。「如何にも自分は隴西の李徴である」と。';
console.log(text.replace(/。/g, '。\n'));
実行結果
叢の中からは、暫く返辞が無かった。
しのび泣きかと思われる微かな声が時々洩れるばかりである。
ややあって、低い声が答えた。
「如何にも自分は隴西の李徴である」と。
余計な改行が入ってしまいます。
先読みアサーションを使わないコード
const text = ' 叢の中からは、暫く返辞が無かった。しのび泣きかと思われる微かな声が時々洩れるばかりである。\nややあって、低い声が答えた。「如何にも自分は隴西の李徴である」と。';
console.log(text.replace(/。([^\n$])/g, '。\n$1'));
実行結果
叢の中からは、暫く返辞が無かった。
しのび泣きかと思われる微かな声が時々洩れるばかりである。
ややあって、低い声が答えた。
「如何にも自分は隴西の李徴である」と。
句点のあとが改行 \n
ないし終端 $
ではない場合だけ次の文字を含めてマッチし、その句点の次の文字を $1
で後方参照することで、文のあとに適切に改行を挿入しています。
期待した結果が得られましたが、読みづらいしなんだか冗長な感じがしてしまいます1。
先読みアサーションを使うコード
const text = ' 叢の中からは、暫く返辞が無かった。しのび泣きかと思われる微かな声が時々洩れるばかりである。\nややあって、低い声が答えた。「如何にも自分は隴西の李徴である」と。';
console.log(text.replace(/。(?!\n|$)/g, '。\n'));
実行結果
叢の中からは、暫く返辞が無かった。
しのび泣きかと思われる微かな声が時々洩れるばかりである。
ややあって、低い声が答えた。
「如何にも自分は隴西の李徴である」と。
(?!...)
を使うことで、改行 \n
ないし終端 $
に続かない句点だけをマッチし、改行を挿入することができました。
コードもスッキリと記述できています。
雑感
基本的な正規表現は一通り勉強したつもりでしたが、やはり具体的なケースに適用することで、使いどころ込みで理解できるなあと実感しました。