ログやソースコードのようなテキストファイルから、ある特定の形式の行を開始行、別の特定の形式行を終了行としてその間の範囲で特定の形式の行(複数行)を範囲ごとにグルーピングして取得したいことがたまにあります。たとえば
[START] ... ←開始行
... [ERROR] #1 ... ← 必要な行
... [INFO] ... ← 不要な行
... [ERROR] #2 ... ← 必要な行
[END] ... ←終了行
... ← 無関係な行
[START] ... ←開始行
... [ERROR] #3 ... ← 必要な行
... [INFO] ... ← 不要な行
[END] ... ←終了行
となっていたとして、必要な行(複数行)を次のようにグルーピングした(ものを他の処理で使いた)いのです。
[グループ1]
... [ERROR] #1 ... ← 必要な行
... [ERROR] #2 ... ← 必要な行
[グループ2]
... [ERROR] #3 ... ← 必要な行
...
この処理が必要になるたびに忘れてしまっていて毎回考え直しているのでメモっておこうと思ったわけで。
let re0 = new Regex(@"...") // 範囲開始行にマッチ
let re1 = new Regex(@"...") // 範囲終了行にマッチ
let re2 = new Regex(@"...") // 範囲中で取得したい行にマッチ
let trd (x, y, z) = z
use r = new StreamReader(filename, Encoding.UTF8)
seq {
while r.Peek() >= 0 do
yield r.ReadLine()
}
|> Seq.fold
(fun (isInRange, currentLines, linesList) line ->
if isInRange then
if re1.IsMatch(line) then (false, [], linesList @ [currentLines])
else if re2.IsMatch(line) then (true, currentLines @ [line], linesList)
else (true, currentLines, linesList)
else
if re0.IsMatch(line) then (true, [], linesList)
else (false, [], linesList))
(false, [], [])
|> trd