Сравните два CSV-файла и найдите похожие предметы - PullRequest
13 голосов
/ 11 марта 2011

Итак, у меня есть два 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 с новым столбцом РЕЗУЛЬТАТЫ):

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

Ответы [ 5 ]

21 голосов
/ 15 апреля 2014

Ответ 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]: i for i, r in enumerate(csv.reader(master))}).

11 голосов
/ 11 марта 2011

Редактировать: Пока мое решение работает правильно, ознакомьтесь с ответом Мартина ниже для более эффективного решения.

С документацией по 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()
4 голосов
/ 11 марта 2011

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)

Прошу прощения за несоответствие, они разделены табуляцией:)

0 голосов
/ 08 октября 2018

Я просто исправил небольшую вещь в коде Martijn Pieters, чтобы он работал в Python 3, и в этом коде я пытаюсь сопоставить первые элементы столбца в файле1 row[0] с первыми элементами столбца в файле2row[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()
0 голосов
/ 11 марта 2011

Модуль 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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...