과거에도 여기에서 비슷한 질문을 여러 번 하여 큰 성공을 거두었지만 지금은 요구 사항이 약간 변경되어 원하는 정확한 결과를 얻는 데 어려움을 겪고 있습니다.
2를 비교하고 싶어요비슷한파일은 분리되지만 줄 수가 다르고 일부 중복이 발생합니다. 이러한 파일은 동일한 헤더를 갖습니다.
file1.txt
mem_id date time building
aa1 bb1 cc1 dd1
aa2 bb2 cc2 dd2
aa3 bb3 ccx3 dd3
aa4 bb4 cc4 dd4
aa5 bb5 cc5 dd5
file2.txt
mem_id date time building
aa1 bby1 cc1 ddy1
aa2 bb2 cc2 dd2
aa3 bb3 cc3 dd3
aa4 bb4 cc4 dd4
aa4 bb4a cc4a dd4a
4가지 차이점을 발견할 수 있습니다:
1- File2, mem_id aa1에는 "Date" 및 "Building" 열 모두에 "y"가 있습니다.
2- File1, mem_id aa3에는 "time" 열에 "x"가 있습니다.
3- File1에는 mem_id가 aa5입니다.
4- File2, mem_id aa4에는 2개의 항목이 있습니다.
두 파일 간의 차이점만 출력하는 스크립트를 실행하고 싶습니다(동일한 줄 건너뛰기). 내가 시도한 모든 것이 중복되거나 건너뛴 줄에서 중단되어 전체 파일의 출력이 엉망이 되었습니다. 모든 줄이 일치하면 다음 코드가 제대로 작동합니다.
current_code
awk -F ',' 'BEGIN {IGNORECASE = 1} NR==1 {for (i=1; i<=NF; i++) header[i] = $i}NR==FNR {for (i=1; i<=NF; i++) {A[i,NR] = $i} next}{ for (i=1; i<=NF; i++) if (A[i,FNR] != $i) print header[1]"#-"$1": " header[i] "- " ARGV[1] " value= ", A[i,FNR]" / " ARGV[2] " value= "$i}'
desired_output.txt
Mem_id#-aa1 : date- file1.txt value = bb1 / file2.txt value= bby1
Mem_id#-aa1 : building- file1.txt value = dd1 / file2.txt value= ddy1
Mem_id#-aa3 : time- file1.txt value = ccx3 / file2.txt value= dd3
Mem_id#-aa4 : date- file1.txt value = / file2.txt value= bb4a
Mem_id#-aa4 : time- file1.txt value = / file2.txt value= cc4a
Mem_id#-aa4 : building- file1.txt value = / file2.txt value= dd4a
Mem_id#-aa5 : date- file1.txt value = bb5 / file2.txt value=
Mem_id#-aa5 : time- file1.txt value = cc5 / file2.txt value=
Mem_id#-aa5 : building- file1.txt value = dd5 / file2.txt value=
답변1
다음 Python 프로그램은 원하는 작업을 수행하거나 이에 매우 가까운 작업을 수행해야 합니다.
세 번째 줄이
desired_output.txt
잘못된 것 같습니다.Mem_id#-aa3 : time- file1.txt value = ccx3 / file2.txt value= dd3
dd3 should probably be
cc3`그렇지 않으면 프로그램의 출력은 예제 출력에서 약간 불규칙해 보이는 공백을 제외하고 일치합니다.
입력은 키(memid)별로 정렬된 것으로 간주됩니다.
- 프로그램은 동기화를 시도할 때 기본적으로 4줄(max_diff + 1)을 버퍼링합니다. 이 버퍼의 키 중 "현재" 키와 일치하는 키가 없으면 vv는 불일치로 처리되어 인쇄되고 다음 쌍이 시도됩니다. 키가 발견되면 다른 버퍼 또는 출력과의 불일치가 먼저 발생합니다.
예제 입력에는 첫 번째 행과 두 번째 행에 동일한 memid가 두 번(또는 그 이상) 있을 때 예상되는 동작에 몇 가지 제한 사항이 있습니다.
output()
임의의 라인을 일치시키고 모든 일치 항목을 표시하려고 시도한 후 (왼쪽에서 오른쪽으로). 따라서 동일한 memid 내에서 일치하는 줄의 순서는 중요하지 않습니다. 왼쪽이나 오른쪽 또는 둘 다 비어 있으면(특히 둘 다 비어 있는 경우) 인쇄하기 쉽습니다. 나머지 부분에서는 나머지 각 행을 왼쪽에서 오른쪽으로 일치시킵니다.fmt
의 문자열에 따라line_out()
출력이 결정되며 자유롭게 변경/재주문할 수 있습니다.
#! /usr/bin/env python
# coding: utf-8
# http://unix.stackexchange.com/q/161913/33055
from __future__ import print_function
from collections import OrderedDict
from logging import debug
import sys
class RowBuffer:
def __init__(self, file_name, delim=None, max_diff=3):
"""delim is the character that is used for splitting input.
None->whitespace
"""
self._verbose = 0
self._file_name = file_name
self._fp = open(self._file_name)
self._delim = delim
self._max_diff = max_diff
self._head = self._fp.readline().split(delim)
# the buffer consists of a maximum of max_diff entries
# the keys are the first items of a row, the value a list
# of all other items on that row
self._buffer = OrderedDict()
self.fill_buffer()
def compare(self, rb):
"""check if self._buffer"""
if self._head != rb._head:
print('headings differ:\n {}\n {}'.format(
self._head, rb._head))
while self._buffer:
l = self.get()
try:
r = rb.get()
except KeyError:
debug('only left %s', l[0])
self.output(l, None, rb)
break
if l[0] == r[0]:
debug('compare vals %s', l[0])
self.output(l, r, rb)
continue
if l[0] in rb:
# left key in right, but not at top
# output right until top keys are same
while l[0] != r[0]:
debug('only right %s', r[0])
self.output(None, r, rb)
r = rb.get()
self.output(l, r, rb)
continue
if r[0] in self:
# right key in left, but not at top
# output left until top keys are same
while l[0] != r[0]:
debug('only left %s', l[0])
self.output(l, None, rb)
l = self.get()
self.output(l, r, rb)
continue
# neither found: output both
debug('neither left in right nor vv %s %s', l[0], r[0])
self.output(l, None, rb)
self.output(None, r, rb)
while rb._buffer: # remaining in right file
r = rb.get()
debug('only right %s', r[0])
self.output(None, r, rb)
def output(self, l, r, right):
fmt1 = '{col0_header}#-{col0_value} : {col_header}- ' \
'{left_file_name} value = {left_value} / ' \
'{right_file_name} value= {right_value}'
d = dict(
col0_header=self._head[0],
left_file_name=self._file_name,
right_file_name=right._file_name,
)
if l is not None and r is not None:
# one or more values on both sides, compare all lines on the
# left with all on the right remove any matching pairs
match = {} # left index to right index
for lidx, lv in enumerate(l[1]):
for ridx, rv in enumerate(r[1]):
if lv == rv:
if lidx not in match:
match[lidx] = ridx
# pop from back of list, not invalidate index
for lidx in sorted(match, reverse=True):
l[1].pop(lidx)
for ridx in sorted(match.values(), reverse=True):
r[1].pop(lidx)
if r is None or not r[1]:
for lv in l[1]:
for idx, k in enumerate(self._head[1:]):
self.line_out(d, col0_value=l[0], col_header=k,
left_value=lv[idx], right_value=' ')
return
if l is None or not l[1]:
for rv in r[1]:
for idx, k in enumerate(self._head[1:]):
self.line_out(d, col0_value=l[0], col_header=k,
left_value=' ', right_value=rv[idx])
return
# print non matching
for lv in l[1]:
for rv in r[1]:
for idx, k in enumerate(self._head[1:]):
if lv[idx] == rv[idx]:
continue # same value
self.line_out(d, col0_value=l[0], col_header=k,
left_value=lv[idx], right_value=rv[idx])
def line_out(self, d, **kw):
# manipulate and print output
# the fields of the format string can be arbitrarily arranged
# as long as the field names (between {} match)
fmt = '{col0_header}#-{col0_value} : {col_header}- ' \
'{left_file_name} value = {left_value} / ' \
'{right_file_name} value= {right_value}'
d1 = d.copy()
d1.update(kw)
s = fmt.format(**d1)
# s = s.rstrip()
s = s[0].upper() + s[1:] # sample output doesn't match input
print(s)
def get(self):
item = self._buffer.popitem(last=False)
self.fill_buffer()
return item
def fill_buffer(self):
if self._fp is None:
return
while len(self._buffer) < self._max_diff:
row = self._fp.readline().split(self._delim)
if not row:
self._fp.close()
self._fp = None
return
entry = self._buffer.setdefault(row[0], [])
entry.append(row[1:])
def __contains__(self, key):
self.fill_buffer()
return key in self._buffer
rb1 = RowBuffer(sys.argv[1])
rb2 = RowBuffer(sys.argv[2])
rb1.compare(rb2)
답변2
이것은 귀하의 문제에 대한 (우아함과는 거리가 먼) 부분적인 해결책입니다. 첫 번째 열을 id 열로 사용하고(첫 번째 열일 필요는 없지만 반드시 있어야 함) suffix
동일한 키의 여러 항목을 저장하기 위해 세 번째 차원을 도입합니다. 마지막으로 파일 1에서 찾을 수 없는 키를 파일 2에서 찾으려고 시도합니다.
BEGIN {
IGNORECASE = 1
}
NR==1 {
for (i = 1; i <= NF; i++)
header[i] = $i
suffix = 0
previous_key=""
}
NR==FNR {
if ($1 == previous_key) {
suffix = suffix + 1
max_suffix[$1] = suffix
} else
suffix = 0
for (i = 1; i <= NF; i++) {
A[$1,suffix,i] = $i
}
key_count[$1] = key_count[$1] + 1
previous_key = $1
next
}
{
if ($1 == previous_key)
suffix = suffix + 1
else
suffix = 0
previous_key = $1
if (A[$1,suffix,1] != "") {
for (i = 2; i <= NF; i++)
if (A[$1,suffix,i] != $i) {
print header[1]"#-"$1": " header[i] "- " ARGV[1] " value= ", A[$1,suffix,i]" / " ARGV[2] " value= "$i
}
key_count[$1] = key_count[$1] - 1
}
else
for (i = 2; i <= NF; i++)
print header[1]"#-"$1": " header[i] "- " ARGV[1] " value= ", " / " ARGV[2] " value= "$i
}
END {
for (missing_key in key_count)
if (key_count[missing_key] > 0) {
for (suffix = max_suffix[missing_key] - key_count[missing_key] + 1; suffix <= C[missing_key]; suffix++)
for (i = 2; i <= NF; i++)
print header[1]"#-"missing_key": " header[i] "- " ARGV[1] " value= ", A[missing_key,suffix,i] " / " ARGV[2] " value= "
}
}
주의할 점이 있습니다. 파일 2의 일치하지 않는 항목은 항상 끝에 인쇄되며 파일의 위치에 따라 정렬되지 않습니다. 또한 행의 순서는 임의적입니다. 나는 이것이 명령에 결과를 전달함으로써 달성될 수 있다고 생각합니다 sort
.