Awk - 2つのファイルの数を比較し、新しいファイルに違いを書き込みます。

Awk - 2つのファイルの数を比較し、新しいファイルに違いを書き込みます。

アイテム番号を含む2つのリストがありますが、両方のファイルに存在しない番号を新しいファイルに作成して、このリスト間の違いを表示したいと思います。

両方のファイルには、2列に項目番号があり、3列に部品IDがあります。まず、ファイル1のエントリ番号が存在することを確認したいと思います。存在する場合は部品IDを確認し、trueの場合は次の項目番号に移動する必要があります。それ以外の条件が満たされない場合は、新しく作成されたファイルに違いを記録する必要があります。エントリ番号が2つのファイルのうちの1つにのみ存在する場合、プログラムは「Artikel [x]が見つかりません」と書く必要があります。

はい

ファイル1

Artikel[ 456]= 1,2
Artikel[ 877]= 3
Artikel[ 278]= 4
Artikel[ 453]= 13

ファイル2

Artikel[ 456]= 2, 1 
Artikel[ 877]= 3, 5 
Artikel[ 387]= 4, 9, 4 
Artikel[ 947]= 10

出力

Artikel[ 877]= 3 != Artikel[ 877]= C3, C5
Artikel[ 278]= 4 != Artikel[ 278 ]= 4, 9, 4
Artikel[ 453]= 13 cannot be found in File 2!
Artikel[ 947]= 10 cannot be found in File 1!

私はファイル1のエントリ番号を配列に書き、ファイル2の各行を確認することでこれを行うことができると思いましたが、なぜか管理に問題があります。助けてくれてありがとう。

ありがとう

答え1

POSIX awkを使用してください。

$ cat tst.awk
BEGIN {
    FS = "[]=[]+"
    f1 = ARGV[1]
    f2 = ARGV[2]
}
{
    gsub(/[[:space:]]+/,"")
    gsub(/,/,"& ")
    key = $1 "[ " $2 " ]="
    keys[key]
    vals = substr($0,index($0,"=")+1)
}
FILENAME == f1 {
    f1KeyVals[key] = vals
}
FILENAME == f2 {
    f2KeyVals[key] = vals
}
END {
    for ( key in keys ) {
        if ( (key in f1KeyVals) && (key in f2KeyVals) ) {
            if ( f1KeyVals[key] != f2KeyVals[key] ) {
                areDifferent = 0

                delete f1vals
                split(f1KeyVals[key],tmp,/, */)
                for ( i in tmp ) { f1vals[tmp[i]] }

                delete f2vals
                split(f2KeyVals[key],tmp,/, */)
                for ( i in tmp ) { f2vals[tmp[i]] }

                for ( val in f1vals ) {
                    if ( val in f2vals ) {
                        delete f2vals[val]
                    }
                    else {
                        areDifferent = 1
                        break
                    }
                }

                for ( val in f2vals ) {
                    areDifferent = 1
                    break
                }

                if ( areDifferent ) {
                    printf "%s %s != %s %s\n", key, f1KeyVals[key], key, f2KeyVals[key]
                }
            }
        }
        else if ( key in f2KeyVals ) {
            printf "%s cannot be found in %s!\n", key, f1
        }
        else {
            printf "%s cannot be found in %s!\n", key, f2
        }
    }
}
$ awk -f tst.awk file1 file2
Artikel[ 5129720100 ]= cannot be found in file2!
Artikel[ 5089100000 ]= C3 != Artikel[ 5089100000 ]= C3, C5
Artikel[ 4005530901 ]= cannot be found in file1!
Artikel[ 5091270000 ]= C4 != Artikel[ 5091270000 ]= C4, C19, C34

上記は、たとえば、値が繰り返される場合はC1, C1, C2繰り返されないのと同じ方法で処理される必要があると仮定しますC1, C2

答え2

存在するTxR不明な言葉:

(defun read-file (path)
  (let ((h (hash)))
    (with-stream (s (open-file path))
      (whilet ((line (get-line s)))
        (if-match `Artikel[ @item ]= @list` line
          (let ((idh (flow list
                       (tok #/[^ ,]+/)
                       hash-list)))
            (set [h item] idh))))
      h)))

(defun out-one (h file)
  (dohash (item ids h)
    (put-line `Artikel[ @item ] = @{(hash-values ids) ", "} cannot be found in @file!`)))

(defun out-both (h)
  (dohash (item id-pair h)
    (tree-bind (left-ids . right-ids) id-pair
      (unless (equal left-ids right-ids)
         (put-line `Artikel[ @item ] = @{(hash-values left-ids) ", "} !=\ \
                    Artikel[ @item ] = @{(hash-values right-ids) ", "}`)))))

(let* ((h0 (read-file "file1"))
       (h1 (read-file "file2")))
  (out-one (hash-diff h0 h1) "File 2")
  (out-one (hash-diff h1 h0) "File 1")
  (out-both [hash-isec h0 h1 cons]))

出力:

$ txr diff.tl
Artikel[ 5129720100 ] = C13 cannot be found in File 2!
Artikel[ 4005530901 ] = C10 cannot be found in File 1!
Artikel[ 5091270000 ] = C4 != Artikel[ 5091270000 ] = C34, C19, C4
Artikel[ 5089100000 ] = C3 != Artikel[ 5089100000 ] = C3, C5

答え3

以下は完全な解決策ではありませんが、Unixコマンドを知っておくと便利ですjoin

▷  join -v 1 -t= <(sort -k 1b,1 FILE1) <(sort -k 1b,1 FILE2) | tr '=' '\t'
Artikel[ 5129720100 ]    C13
▷  join -v 2 -t= <(sort -k 1b,1 FILE1) <(sort -k 1b,1 FILE2) | tr '=' '\t'
Artikel[ 4005530901 ]    C10
▷  join -t= <(sort -k 1b,1 FILE1) <(sort -k 1b,1 FILE2) | tr '=' '\t'
Artikel[ 4003526101 ]    C1,C2    C2,C1
Artikel[ 5089100000 ]    C3    C3,C5
Artikel[ 5091270000 ]    C4    C4,C19,C34

join入力をソートして先行スペースを無視する必要があるため、ソートオプションは次のとおりです。出力ファイル-v <i>にペアリングできない行があります。<i>これらの出力を使用すると、必要なものを計算する方がはるかに簡単になります。

答え4

使用幸せ(以前のPerl_6)

~$ raku -e 'my %hash1; for "path/to/file1.txt".IO.lines() {  
               .split("= ") andthen %hash1.append: .[0] => .[1].split(",") };  
            my %hash2; for "path/to/file2.txt".IO.lines() {  
               .split("= ") andthen %hash2.append: .[0] => .[1].split(",") };  
            for (%hash1.keys ∩ %hash2.keys).map(*.key) -> $i {  
                unless %hash1{$i} == %hash2{$i} {  
                    put ($i ~ "= " ~ %hash1{$i}.join(",") ~ " != " ~ $i ~ "= " ~ %hash2{$i}.join(","))  // next} };  
            my ($k2,$v2) = %hash2{(%hash2.keys (-) %hash1.keys)}:kv;   
            my ($k1,$v1) = %hash1{(%hash1.keys (-) %hash2.keys)}:kv;  
            put $k2 ~ "= " ~ $v2.join(",") ~ " cannot be found in File 1!" // next;  
            put $k1 ~ "= " ~ $v1.join(",") ~ " cannot be found in File 2!" // next;'

出力例:

Artikel[ 5091270000 ]= C4 != Artikel[ 5091270000 ]= C4,C19,C34
Artikel[ 5089100000 ]= C3 != Artikel[ 5089100000 ]= C3,C5
Artikel[ 4005530901 ]= C10 cannot be found in File 1!
Artikel[ 5129720100 ]= C13 cannot be found in File 2!

上記は、Perlプログラミング言語シリーズの最新メンバーであるRakuで書かれた答えです。 Rakuは組み込みのUnicodeと高度な正規表現エンジンを高度にサポートしています。この回答は、Rakuの%署名付きハッシュ(キー値)データ構造(Perl言語シリーズの機能)を利用します。

  • つまり、ファイルは1行ずつ読み取られます%hash。行には2つの部分splitが与えられ、最初の部分はキーになり、2番目の部分(コンマ)は値になります。=.[0]split.[1]

  • RakuにはSet機能が組み込まれているので、書くだけでハッシュキーの交点を得ることができます%hash1.keys ∩ %hash2.keys(Unicode中位文字または3文字ASCII中位文字を使用)。(&)

  • 交差点の結果では、コードは%hash{$k}関連する値を返す主キー検索です。これらの知識に基づいて出力文字列を設定できます(~チルダは文字列を結合するために使用されます)。unless %hash1{$i} == %hash2{$i}(AND)句のため、unless同じ値のハッシュキーは出力されませんif not

  • Rakuには、3桁のASCII中位文字で表示される差分設定機能もあります(-)。 3桁のASCII中位文字が(-)使用される理由は、実際のUnicode "SET-MINUS"記号(U + 2216)が互いに混乱しやすい可能性があるためです。 2つのハッシュキー間の差を計算して、それぞれと各出力の出力文字列を設定しますput


注1:上記のコードは、各キー値の一意性についていかなる仮定もしません。したがって、1つのファイルに重複した値がある場合(他のファイルにはない)、出力に違いとして表示されます。各ハッシュ値を一意にするには、unique各ハッシュコンストラクタに追加します(例:)%hash.push: .[0] => .[1].split(",").unique

ノート2:.[0]上記のコードは "Artikel"キーを単純化しようとはしませんが、次のように正規表現を使用して各キーを数字だけに単純化する方が良いでしょう.[0].match(/ \d+ /).Str

ノート3:この例では、入力パスはハードコーディングされていますが、1つをハードコーディングできます(証明するファイル)を受け取るテスト$*ARGFILES.IO.lines() {...};コマンドラインでファイルを使用または閉じます(STDINが正しくリダイレ​​クトされていることを$*IN.IO.lines() {...};確認)。<より多くのCLIオプション(Rakuの@*ARGSコマンドライン配列の使用など)については、以下の2番目のリンクを参照してください。


https://docs.raku.org/言語/setbagmix#Sets,_bags,_and_mixes
https://docs.raku.org/言語/create-cli
https://raku.org

関連情報