正規表現:最も深いリストレベルのみ一致

正規表現:最も深いリストレベルのみ一致

ゲームに必要なマテリアルのリストを、一番上から最も原始的なマテリアルまでまとめました。しかし、今は数字を集計する簡単な方法を探しています。

21 reinforced alloy
    21 damascus steel
        21 steel
            21 iron dust
            21 carbon
            21 iron
        21 iron dust
        21 carbon
        21 iron
    21 hardened metal
        21 damascus steel
            21 steel
                21 iron dust
                21 carbon
                21 iron
            21 iron dust
            21 carbon
            21 iron
        21 duralmin
            21 aluminum dust
            21 copper dust
            21 aluminum
                21 aluminum dust
        21 compressed carbon
            84 carbon
        21 aluminum bronze
            21 aluminum dust
            21 bronze
                21 copper dust
                21 tin dust
                21 copper
            21 aluminum
                21 aluminum dust
    21 corinthian bronze
        21 silver dust
        21 gold dust
        21 copper dust
        21 bronze
            21 copper dust
            21 tin dust
            21 copper
    21 solder
        21 lead dust
        21 tin dust
        21 lead
            21 lead dust
    21 billon
        21 silver dust
        21 copper dust
        21 silver
            21 silver dust
    21 gold 24 carat

収集すべき原材料を探しているので、トップレベルは重要ではありません。たとえば、21 hardened metalおよびは21 damascus steel合計を探しているため重要ではありません42 damascus steel42 iron dust42 carbon42 iron

これまで私正規表現テストウェブサイトgrepしかし、最終的には計算のためにウェブサイトを開く必要がないように使用できるようになることを願っています。私は「炭素が5回現れ、これが一致する線です」のようなものを手に入れたいです。炭素が5回現れ、そのうち4番が現れて21 carbon1番があることが分かれば、84 carbon総必要量を簡単に計算できるので、より簡単に計算できます21*4 + 84 = 168 carbon

私は他の行がなく、その後に多数のタブがある行の数を数えようとしています。もしあれば、それは生ではないからです。

/(\t+)\d+ aluminum\n(?!\1)/g(「アルミニウム」を私が探している原材料に置き換えます)

しかし、これは何も判明していません。正規表現を使用して達成したい目標を達成する方法はありますか?それでは、どうすればいいですか?

時間をいただきありがとうございます。


これをSOに置くかSEに置くかはよく分からないが、結局は使えるようになることを望むという点を考慮すれば、そこがgrepより適切な場所ではないかと思います。

答え1

Perlに似た正規表現を使用するには、実際の正規表現を使用することをお勧めします。

<your-file perl -l -0777 -ne '
  while (m{^(\s*+)(\d+) (.*)$(?!\n\1\s)}mg) {
    $count{$3} += $2
  }
  END {
    printf "%4d %s\n", $count{$_}, $_ for sort keys %count
  }'

これは作る:

  84 aluminum dust
 168 carbon
  42 copper
 105 copper dust
  21 gold 24 carat
  21 gold dust
  84 iron
  84 iron dust
  42 lead dust
  63 silver dust
  63 tin dust

-0777 -n入力全体が吸収されることを意味します$_。演算子の最終フラグは、始まりと終わりだけでなく、m各行の始まりと終わりにも一致を生成します。フラグがない場合は改行に一致するものはありませんが、入力に空白行がある場合はここで問題が発生する可能性があることに注意してください。m{...}^$$_$_s.\s

\s*+はい、遡及適用されていないバージョンです\s*\d+( ) 後の内容は空白と一致できないため必ずしも必要ではありません。

Standardは、grep使用中のようなperlに似た正規表現と\dperl RE演算子をサポートしていませんが、複数行モードもサポートするものを使用できます。(?!\1)pcregrep-o-M

<your-file pcregrep -Mo '^(\s*+)\K.*$(?!\n\1\s)'

perl合計計算などの他の作業にはまだパイプが必要なため、すべてのタスクにパイプを使用するよりも利点はawkほとんどperlありません。

インデントをタブとスペースと混在させることができる場合は、入力をこれらのいずれかを渡すか、expand最初にunexpandスペースまたはタブにマージできます。基本的に、彼らはタブストップをほとんどの端末やブラウザのように8列と見なしますが(Stackexchangeを除いて面倒なことに4列になります)、-tこれを変更するオプションを参照してください。

答え2

行のレベル<=次の要素のレベルの場合、行は「プレミア」です。これは次のとおりです。

前の行は、そのレベル<=現在のレベルの場合(または最後の行の場合)の最初の行です。

NF最後のフィールドとして、フィールド区切り文字「\ t」、level、およびコンポーネントでawkを使用します$NF

awk -F '\t' 'prevlev>=NF  {print primi}; 
                          {prevlev = NF; primi=$NF } 
             END          {print $NF}'

要約すると、次の内容を実行できます。

... | sed 's/ /\t/' | datamash -g 2 -s sum 1

答え3

LookbehindとLookaheadを使用する必要があります。また、1行ずつ処理するのではなく、入力全体を一緒に処理する必要があります。次のコマンドは必要な操作を実行する必要があります。

grep -Pzo '(?<=\n)(\s+)(\S[^\n]*)(?!\n\1\s)' input_file
  • -PPerl構文を有効にします。

  • -z改行文字の代わりにヌル終端を使用してください。

  • -o一致する項目のみが出力されます。

  • (?<=\n)改行を見つけてください。代わりに^、通常は各行の先頭に一致します。後続の否定的な見解を使用してください(?<!...)。おそらく常により深いレベルがあるので、最初の行を無視します。そうでない場合は、送信する前に入力の先頭に新しい行を追加できますgrep。これを行うより良い方法があるかもしれませんが、以下は一つです。

    ( echo ; cat input_file ) | grep ...
    
  • (\s+)インデントレベルをキャプチャします。これは後で呼び出されます\1\sスペースと一致します。これに対する潜在的な問題の1つは、改行文字がインデントの一部と見なすことができることです。たとえば、二重改行は段落区切り文字としてよく使用されます。\sインデントに使用したい特定のスペース()に置き換えることができます[\ \t]

  • (\S[^\n]*)興味のあるテキストをキャプチャします。 \S空白でないものと一致します。[^\n]改行文字以外のすべての文字と一致します。

  • (?!\n\1\s)否定的なプレビューは、次の行が現在の行より深くインデントされないようにします。肯定的な見通しが必要な場合はを使用してください(?=...)

関連情報