どうやってそこに行きましたか?

どうやってそこに行きましたか?

次のようなファイルがあるとしましょう。

1,2,3-5,6
1,2,3-5,6,
1
1-3
1,2,3-,4,5-7
1,2,3-,4,5-7,
1,2,-3,4,5
1,2,-,3,4
1,2,,,3,4
,1,2,3

次の規則の組み合わせのみが有効と見なされます。

  1. 範囲 [0-9]+-[0-9]+
  2. グループ[0-9]+,[0-9]+
  3. 注文番号[0-9]+

カンマで終わる行も有効と見なす必要があります。

ただ抽出したいのですが

1,2,3-5,6
1,2,3-5,6,
1
1-3

以下に示す他の行はルールに準拠していないため

1,2,3-,4,5-7
1,2,3-,4,5-7,
1,2,-3,4,5
1,2,-,3,4
1,2,,,3,4
,1,2,3

これは、一部の行の範囲が不完全であり、一部の行のグループに数字が欠落しているためです。


PS:PCRE互換性のあるgrepソリューションをお勧めしますが、他のソリューションも大歓迎です。

答え1

リストされた文字列(およびその文字列で始まる文字列)と,一致する完全なPCREは次のとおりです。

grep -P '^([0-9]+(-[0-9]+)?(,|$))+$'

どうやってそこに行きましたか?

一致の最も基本的な要素は数値です。[0-9]PCREのより単純な要素は、\d英語(ASCII)番号の正しい正規表現です。どのまたはそうでないかもしれません。一致することができるデバナガリ数字、例えば。その後、[0123456789]正確に書く必要があります。

それから連続した数字と一致します[0-9]+

数字(1、3、または26)の後にダッシュ「-」が続き、その後に1つ以上の数字(やはり数字)が続くことがあります。

[0-9]+(-[0-9]+)?

ここで、?ダッシュ番号の順序はオプションです。

その後、各数字:(3または数字の範囲4-9:)の後にはカンマ,(複数回)が続く必要があります。

([0-9]+(-[0-9]+)?,)+

最後のコンマが欠落している可能性があることを除いて:

([0-9]+(-[0-9]+)?(,|$))+

そして、必要に応じて前にカンマが表示されることがあります。

(^|,)([0-9]+(-[0-9]+)?(,|$))+

正規表現をテストテキストの始めと終わりに固定するのはとても良い考えです。

^((^|,)([0-9]+(-[0-9]+)?(,|$))+)$

テストして編集できます。このウェブサイトのPCRE正規表現

先行するカンマを拒否する必要がある場合は、次を使用してください。

^(([0-9]+(-[0-9]+)?(,|$))+)$

このため、正規表現マシンには代替解釈は不要です。すべて一致する必要があり、一致しない項目は拒否されます。

(GNU)拡張正規表現で書くことができます。

grep -E '^(([0-9]+(-[0-9]+)?(,|$))+)$'

基本正規表現(BRE):

grep '^\(\([0-9]\{1,\}\(-[0-9]\{1,\}\)\{0,1\},\{0,1\}\)\{1,\}\)$'

カンマがオプション,の場合、{0,1}正規表現エンジンは一致に対していくつかの決定を下すことができます。


説明的な正規表現?

(?x)スペースとコメントを含むより説明的な正規表現は、inで始まります。pcregrep

pcregrep '(?x)                  # tell the regex engine to allow
                                 # white space and comments.
           (?(DEFINE)            # subroutines that will be used. 
             (?<nrun> [0-9]+)    # run of digits (n-run).

              # define a range pair. A number run followed by
              # an optional ( dash and another number run )
             (?<range> (?&nrun)  (-(?&nrun))? )    # range pair.
             
             (?<sep> ,)          # separator used.
           )                     # end of definitions.

         # Actual regex to use:
         # (range) that ends in a (sep) 
         # or is at the end of the line,
         # several times (+).

         ^(  (?&range)  ((?&sep)|$)  )+$

        ' file

この正規表現(一度コンパイルされた場合)は元の正規表現とまったく同じで、すばやく実行されます。もちろん、正規表現をコンパイルするには(無視するのに十分な)追加時間がかかります。

テスト例はここ

答え2

awk不要なフィールドまたはサブフィールドを含む行を削除しながら、各行をカンマ区切りフィールドに分割し、ダッシュの対応するフィールドをサブフィールドに分割するために使用されます。

BEGIN { FS = "," }

{
    for (i = 1; i <= NF; ++i) {
        # Only the 1st field is allowed to be
        # empty, but only if there are further
        # fields (avoids empty lines).

        if ($i == "" && (i != 1 || NF == 1)) next

        # If the field is split on dashes, it
        # should split into no more than two
        # elements.

        if ((n = split($i, a, "-")) > 2) next

        # Each split-up element needs to be made
        # up of decimal digits only.

        for (j = 1; j <= n; ++j)
            if (a[j] !~ "^[[:digit:]]+$") next
    }

    # The current line is ok to print.

    print
}

これは次のように使用されます

awk -f script file

scriptプログラムの場所ですawk

または「ワンタイム」として:

awk -F, '{for(i=1;i<=NF;++i){if(($i==""&&(i!=1||NF==1))||((n=split($i,a,"-"))>2))next;for(j=1;j<=n;++j)if(a[j]!~"^[[:digit:]]+$")next}};1' file

5-2ループの後に「リバースレンジ」(例:)のチェックを簡単に追加できますj

if (n == 2 && a[1] > a[2]) next

答え3

$ perl -n -e 'print if /^((\d+(-\d+)?)(,|$))+$/g' input.txt 
1,2,3-5,6
1,2,3-5,6,
1
1-3

またはGNU grepと同じです。

$ grep -P '^((\d+(-\d+)?)(,|$))+$' input.txt 
1,2,3-5,6
1,2,3-5,6,
1
1-3

答え4

Raku(以前のPerl_6)の使用

raku -ne '.put if m:g/^^ [ [\d+ [\-\d+]?] [\,|$$] ]+ $$/;'

入力例:

1,2,3-5,6
1,2,3-5,6,
1
1-3
1,2,3-,4,5-7
1,2,3-,4,5-7,
1,2,-3,4,5
1,2,-,3,4
1,2,,,3,4
,1,2,3

出力例:

1,2,3-5,6
1,2,3-5,6,
1
1-3

Rakuを使用する1つの利点は、一致者内の空白許容誤差です。これにより、コードを読みやすくなります。第二に、基本正規表現エンジンの修飾子(:global先行コロンのインポートなど)が一致構造の先頭に表示されますm/.../。これにより、正規表現も読みやすくなります。

上記の正規表現を文字通りとすると、次のようになります。'1つ以上の数字を探し、その後にオプション(0または1)ダッシュ、1つ以上の数字、カンマ、または行末($$)が続き、前のパターン全体が1回以上繰り返されます。

今、自分でこのように質問することができます。「だから何ですか?Perl5のように見えました。」上記のコードはPerl5/PCREをほぼ直接翻訳したものだからです。実際、Raku(Perl6など)には、一般的な正規表現の問題を解決するために使用できる新しい「セパレータ」イディオム(「修正数量子」など)があります。気をつけて:

raku -ne '.put if m:g/^^ [\d+ [\-\d+]? ]+ % "," $$/;'

または

raku -ne '.put if m:g/^^ [\d+ [\-\d+]? ]+ %% "," $$/;'

最初の行では、%左側のパターンの間にカンマ区切り文字が挿入される検出マッチングを使用します。 2行目のusingは%%同じことを行いますが、末尾のコンマも受け入れます。あなたの選択。

Perlの専門家Damian Conwayは、Raku(別名Perl6)がまったく新しいスタイルの正規表現を表すと指摘しています。試してみると同意することもできます。

https://docs.raku.org/言語/regexes#Modified_Quantifier:_%,_%%
https://youtu.be/ubvSjW6Nyqk
https://raku.org/

関連情報