正規表現の検索置換だけで長い行を短くする

英語のテキストを処理する際に、長い文字列の段落を処理しやすいように短い文字列に分割したいとします。 ピリオド+スペースをピリオドと改行にするとかだと、長い文字列と短い文字列がまざってしまうので、60文字とかで切りたい。 その際、できれば単語の途中に改行が入らないようにしたい。 という課題を、VSCode正規表現の検索置換だけでできないか、と思って試行錯誤してそれなりにできたのでご紹介。

検索文字列は

^(.{60}[\w.,'\)?\]]*)\n*

で、置換文字列は

$1\n

です。

$1で対象としているのは、(.{60}[\w.,'\)?\]]*) 。この両端のカッコで囲まれた部分を $1 としてそのまま置換文字列にもってくる。

冒頭の ^ は行頭という意味。

.{60} は任意の文字列 60個。

[\w.,'\)?\]]* は61文字目がなんらかの文字やたまたまピリオドやカンマやカッコ閉じ、とかならばそれも含めたいので指定している。

最後の \n* は、たまたま改行だったり改行が続いていたら、改行を増やさないための工夫。

これで全文置換をやると、1回目はたとえば 90個が置換、2回目以降はだんだん増えていって、そのうち 300個とかで増えなくなれば終了です。

プログラムで処理した方が簡単かもね!!!!

  1. 全文読み込む。
  2. 行単位に分割したリストにする。
  3. 長い文字列を短い文字列のリストにする。
  4. リストがネストしているので、フラットにする。

というのはどうでしょうか。 うむ。 また別の記事にします。

追記
Haskell のプログラムですが、ネットにあったのがちゃんと動いたので、紹介しておきます。

-- wordrap.hs

wordwrap maxlen = wrap_ 0 . words
  where
    wrap_ _ [] = ""

    wrap_ pos (w:ws)
      -- at line start: put down the word no matter what
      | pos == 0 = w ++ wrap_ (pos + lw) ws
      | pos + lw + 1 > maxlen = '\n' : wrap_ 0 (w : ws)
      | otherwise = ' ' : w ++ wrap_ (pos + lw + 1) ws
      where
        lw = length w

main =  do
    x <- getContents
    putStr $ unlines $ map (wordwrap 60) $ lines x