Сравните два файла CSV и выполните поиск похожих элементов
Итак, у меня есть два файла CSV, которые я пытаюсь сравнить и получить результаты аналогичных элементов.
Первый файл, hosts.csv показан ниже:
Path Filename Size Signature
C:\ a.txt 14kb 012345
D:\ b.txt 99kb 678910
C:\ c.txt 44kb 111213
Второй файл, masterlist.csv показан ниже:
Filename Signature
b.txt 678910
x.txt 111213
b.txt 777777
c.txt 999999
Как вы можете видеть, строки не совпадают, а masterlist.csv всегда больше, чем файл hosts.csv. Единственной частью, которую я хотел бы найти, является часть подписи. Я знаю, что это будет выглядеть примерно так:
hosts[3] == masterlist[1]
Я ищу решение, которое даст мне что-то вроде следующего (в основном файл hosts.csv с новым столбцом RESULTS):
Path Filename Size Signature RESULTS
C:\ a.txt 14kb 012345 NOT FOUND in masterlist
D:\ b.txt 99kb 678910 FOUND in masterlist (row 1)
C:\ c.txt 44kb 111213 FOUND in masterlist (row 2)
Я искал сообщения и нашел что-то похожее на это здесь, но я не совсем понимаю это, поскольку я все еще изучаю python.
Изменить Использование Python 2.6
Ответы
Ответ 1
Изменить: В то время как мое решение работает правильно, ознакомьтесь с приведенным ниже ответом Martijn для более эффективного решения.
Вы можете найти документацию для модуля CSV python здесь.
То, что вы ищете, выглядит примерно так:
import csv
f1 = file('hosts.csv', 'r')
f2 = file('masterlist.csv', 'r')
f3 = file('results.csv', 'w')
c1 = csv.reader(f1)
c2 = csv.reader(f2)
c3 = csv.writer(f3)
masterlist = list(c2)
for hosts_row in c1:
row = 1
found = False
for master_row in masterlist:
results_row = hosts_row
if hosts_row[3] == master_row[1]:
results_row.append('FOUND in master list (row ' + str(row) + ')')
found = True
break
row = row + 1
if not found:
results_row.append('NOT FOUND in master list')
c3.writerow(results_row)
f1.close()
f2.close()
f3.close()
Ответ 2
Ответ srgerg ужасно неэффективен, так как он работает в квадратичном времени; здесь вместо этого используется линейное временное решение, использующее Python 2.6-совместимый синтаксис:
import csv
with open('masterlist.csv', 'rb') as master:
master_indices = dict((r[1], i) for i, r in enumerate(csv.reader(master)))
with open('hosts.csv', 'rb') as hosts:
with open('results.csv', 'wb') as results:
reader = csv.reader(hosts)
writer = csv.writer(results)
writer.writerow(next(reader, []) + ['RESULTS'])
for row in reader:
index = master_indices.get(row[3])
if index is not None:
message = 'FOUND in master list (row {})'.format(index)
else:
message = 'NOT FOUND in master list'
writer.writerow(row + [message])
Это создает словарь, сопоставляя подписи из masterlist.csv
с номером строки. Поиск в словаре занимает постоянное время, что делает второй цикл по строкам hosts.csv
независимым от количества строк в masterlist.csv
. Не говоря уже о коде, который намного проще.
Для тех, кто использует Python 3, нужно только настроить вызовы open()
для открытия в текстовом режиме (убрать b
из файлового режима), и вы хотите добавить new line=''
чтобы читатель CSV мог взять на себя управление разделителей строк. Возможно, вы захотите указать кодировку, чтобы использовать явно, а не полагаться на вашу систему по умолчанию (используйте encoding=...
). Отображение master_indices
может быть построено с использованием словаря ({r[1]: я for i, r in enumerate(csv.reader(master))}
).
Ответ 3
Python CSV и модуль коллекций, в частности OrderedDict, действительно полезны здесь. Вы хотите использовать OrderedDict для сохранения порядка ключей и т.д. Вам не обязательно, но это полезно!
import csv
from collections import OrderedDict
signature_row_map = OrderedDict()
with open('hosts.csv') as file_object:
for line in csv.DictReader(file_object, delimiter='\t'):
signature_row_map[line['Signature']] = {'line': line, 'found_at': None}
with open('masterlist.csv') as file_object:
for i, line in enumerate(csv.DictReader(file_object, delimiter='\t'), 1):
if line['Signature'] in signature_row_map:
signature_row_map[line['Signature']]['found_at'] = i
with open('newhosts.csv', 'w') as file_object:
fieldnames = ['Path', 'Filename', 'Size', 'Signature', 'RESULTS']
writer = csv.DictWriter(file_object, fieldnames, delimiter='\t')
writer.writer.writerow(fieldnames)
for signature_info in signature_row_map.itervalues():
result = '{0} FOUND in masterlist {1}'
# explicit check for sentinel
if signature_info['found_at'] is not None:
result = result.format('', '(row %s)' % signature_info['found_at'])
else:
result = result.format('NOT', '')
payload = signature_info['line']
payload['RESULTS'] = result
writer.writerow(payload)
Здесь вывод с использованием ваших тестовых CSV файлов:
Path Filename Size Signature RESULTS
C:\ a.txt 14kb 012345 NOT FOUND in masterlist
D:\ b.txt 99kb 678910 FOUND in masterlist (row 1)
C:\ c.txt 44kb 111213 FOUND in masterlist (row 2)
Пожалуйста, извините несоосность, они разделены на вкладку:)
Ответ 4
Модуль csv
пригодится при разборе файлов csv. Но просто для удовольствия я просто разбиваю входные данные на пробелы, чтобы получить данные.
Просто проанализируйте данные, постройте dict
для данных в masterlist.csv с ключом подписи и номером строки как значением. Теперь для каждой строки hosts.csv мы можем просто запросить dict
и выяснить, существует ли соответствующая запись в masterlist.csv, и если да, то в какой строке.
#! /usr/bin/env python
def read_data(filename):
input_source=open(filename,'r')
input_source.readline()
return [line.split() for line in input_source]
if __name__=='__main__':
hosts=read_data('hosts.csv')
masterlist=read_data('masterlist.csv')
master=dict()
for index,data in enumerate(masterlist):
master[data[-1]]=index+1
for row in hosts:
try:
found="FOUND in masterlist (row %s)"%master[row[-1]]
except KeyError:
found="NOT FOUND in masterlist"
line=row+[found]
print "%s %s %s %s %s"%tuple(line)
Ответ 5
Я просто исправил небольшую вещь в коде Martijn Pieters, чтобы он работал в Python 3, и в этом коде я пытаюсь сопоставить первые элементы столбца в row[0]
file1 row[0]
с первыми элементами столбца в row[0]
file2 row[0]
.
import csv
with open('file1.csv', 'rt', encoding='utf-8') as master:
master_indices = dict((r[0], i) for i, r in enumerate(csv.reader(master)))
with open('file2.csv', 'rt', encoding='utf-8') as hosts:
with open('result.csv', 'w') as results:
reader = csv.reader(hosts)
writer = csv.writer(results)
writer.writerow(next(reader, []) + ['RESULTS'])
for row in reader:
index = master_indices.get(row[0])
if index is not None:
message = 'FOUND in master list (row {})'.format(index)
writer.writerow(row + [message])
else:
message = 'NOT FOUND in master list'
writer.writerow(row + [message])
results.close()