항목 번호가 포함된 두 개의 목록이 있는데 두 파일 모두에 존재하지 않는 번호를 새 파일에 작성하여 이 목록 간의 차이점을 표시하고 싶습니다.
두 파일 모두 2열에 항목 번호가 있고 3열에 부품 ID가 있습니다. 먼저 파일 1의 항목 번호가 존재하는지 확인하고 싶습니다. 존재하는 경우 부품 ID를 확인하고 true이면 다음 항목 번호로 이동해야 합니다. 그렇지 않고 조건 중 하나가 충족되지 않으면 새로 생성된 파일에 차이점을 기록해야 합니다. 항목 번호가 두 파일 중 하나에만 있는 경우 프로그램은 "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는 내장된 유니코드와 고급 정규식 엔진을 고급 지원합니다. 이 답변은 Raku의 %
서명된 해시(키-값) 데이터 구조(Perl 언어 계열의 기능)를 활용합니다 .
즉, 파일은 한 줄씩 읽혀집니다
%hash
. 줄에는 두 부분이split
주어지며 첫 번째 부분은 키가 되고 두 번째 부분( 쉼표 )은 값이 됩니다.=
.[0]
split
.[1]
Raku에는 Set 기능이 내장되어 있으므로 쓰기만 하면 해시 키의 교차점을 얻을 수 있습니다
%hash1.keys ∩ %hash2.keys
(유니코드 중위∩
문자 또는 3자 ASCII 중위 문자 사용).(&)
교차점 결과에서 코드는
%hash{$k}
관련 값을 반환하는 기본 키 조회입니다. 이러한 지식을 바탕으로 출력 문자열을 구성할 수 있습니다(~
물결표는 문자열을 함께 결합하는 데 사용됩니다).unless %hash1{$i} == %hash2{$i}
( AND) 절로 인해unless
동일한 값을 갖는 해시 키가 출력되지 않습니다if not
.Raku에는 3자리 ASCII 중위 문자로 표시되는 차이 설정 기능도 있습니다
(-)
. 3자리 ASCII 중위 문자가(-)
사용되는 이유는 실제 유니코드 "SET-MINUS" 기호∖
(U+2216)가 서로 쉽게 혼동될 수 있기 때문입니다. 두 해시 키 간의 차이를 계산하여 각각 및 각 out 에 대한 출력 문자열을 구성합니다put
.
참고 1:위의 코드는 각 키 값의 고유성에 대해 어떠한 가정도 하지 않습니다. 따라서 한 파일에 중복된 값이 있으면(다른 파일에는 없음) 출력에 차이로 표시됩니다. 각 해시 값을 고유하게 만들려면 unique
각 해시 생성자에 추가하세요(예: ) %hash.push: .[0] => .[1].split(",").unique
.
노트 2:.[0]
위의 코드는 "Artikel" 키를 단순화하려고 시도하지 않지만 다음과 같이 정규식을 사용하여 각 키를 숫자로만 단순화하는 것이 더 나을 것입니다 .[0].match(/ \d+ /).Str
.
노트 3:이 예에서는 입력 경로가 하드코딩되어 있지만 하나를 하드코딩할 수 있습니다(입증하다파일)을 받아시험$*ARGFILES.IO.lines() {...};
명령줄에서 파일을 사용하거나 닫습니다 ( STDIN이 적절하게 리디렉션되는지 $*IN.IO.lines() {...};
확인 ). <
더 많은 CLI 옵션(예: Raku의 @*ARGS
명령줄 배열 사용 등)은 아래 두 번째 링크를 참조하세요.
https://docs.raku.org/언어/setbagmix#Sets,_bags,_and_mixes
https://docs.raku.org/언어/create-cli
https://raku.org