ファイル内の 2 つの行が両方ともパターンに適合する場合、どうすれば 2 つの行を結合できますか?

ファイル内の 2 つの行が両方ともパターンに適合する場合、どうすれば 2 つの行を結合できますか?

複数の行を含むファイルがあり、両方の行が特定のパターンに適合する場合は行を結合したいと考えています。

パターンに適合する行を見つけて、次の行を取得できることはわかっています。

grep -E -A1 'Pattern' filename

しかし、次の行もパターンに適合するかどうかをどのように確認し、2 つを結合するにはどうすればよいでしょうか?

たとえば、次のようなファイルがあります。

Hello
i
am
John
Smith

パターンの例は次のようになります。

'^[A-Z][a-z]+'

したがって、この場合、両方の行が大文字で始まる場合は、行を結合したいと思います。

達成したい出力は次のようになります。

Hello
i
am 
John Smith

答え1

/^[A-Z][a-z]+/{
  :a
  N
  /\n[A-Z][a-z]+/{
    s/\n/ /
    b a
  }
}

として保存しjoin.sed、実行します: sed -Ef join.sed file

行がパターンに一致する場合、次の行をパターン スペースに追加し、その行もパターンに一致する限り、改行文字をスペースに置き換えるループを開始します。

GNU Sed の場合は、これを 1 行にまとめることができます。

sed -E '/^[A-Z][a-z]+/{:a;N;/\n[A-Z][a-z]+/{s/\n/ /;b a}}' file

あるいは、Awk スクリプト の場合join.awk、パターンは次のように指定する必要がありますp

{
    if($0~p)c+=1
    else c=0
    printf "%s%s", (c>1 ? " " : ors), $0
    ors=ORS
}
END{print ""}

実行するには: awk -f join.awk p='^[A-Z][a-z]+' file

答え2

sed区切り文字としてヌル文字を使用する( -z):

$ sed -z 's/\([A-Z][a-z]\+\)\n\([A-Z][a-z]\+\)/\1 \2/'
Hello
i
am
John Smith

答え3

Raku (旧称 Perl_6) の使用

raku -e 'given lines.join("\n") { S/ $<first>=[<upper><lower>+] \n $<last>=[<upper><lower>+] /$<first> $<last>/.put};'

サンプル入力:

Hello
i
am
John
Smith
goodbye

サンプル出力:

Hello
i
am
John Smith
goodbye

上記は、Perl系言語の1つであるRakuでコーディングされたソリューションです。データはgivenRakuでは の形式で提供されますlinesが、Rakuのlinesルーチンは入力を自動的に処理するため、データはjoin改行で区切られます。これは少し複雑に思えるかもしれませんが、Rakuのルーチンはデータを遅延して読み取るという利点がありますlines。つまり、上記のコードはすべきであるメモリ効率が高い。

RakuはS///「非破壊的」演算子を実装しており、これはよく知られているs///演算子(Rakuにもそれがあります)と似ています(同一ではないかもしれませんが)。大文字の演算子Sには、「元の文字列をそのまま残し、$/ (一致変数) の代わりに結果の文字列を返します。」

演算子の対応する(左)半分内ではS///名前付きキャプチャが採用されています。正規表現エンジンは最初に を検索して[<upper><lower>+]名前付きキャプチャに割り当て$<first>、次に (改行)を検索し\n、最後に別の を検索して[<upper><lower>+]、今度は名前付きキャプチャに割り当てます$<last>。最後に、演算子の置換(右半分)内でS///、2つの名前付きキャプチャを$<first> $<last>使用して左側の一致を置き換えますが、スペースとそれなし間に改行\nを入れます。

同じことを実現する別の方法を以下に示します。コードでは、名前付きキャプチャを省略し、代わりに を使用して、キャプチャ マーカー<(\n)>内にあるものを除くすべてのものを一致オブジェクトから削除します<(…)>。次に、置換で、がスペース\n に置き換えられます。

raku -e 'put S/ [<upper><lower>+] <(\n)> [<upper><lower>+] / / given lines.join("\n");'  

[注: 上記のコードは、 のような部分をGeorge\nHerbert\nWalker\nBush4 行から 3 行に折りたたむだけです ( George Herbert\nWalker\nBush)。 の行ごとに連続する出現をすべて[<upper><lower>+]1 行で返したい場合は、遠慮なくその質問を投稿してください。]

https://docs.raku.org/language/regexes#S///_非破壊的置換
https://docs.raku.org/language/regexes#index-entry-regex__Named_captures-Named_captures
https://raku.org

関連情報