Python: парсинг нескольких файлов .txt в один файл .csv? - PullRequest
2 голосов
/ 27 марта 2012

Я не очень разбираюсь в сложном крупномасштабном парсинге в Python, есть ли у вас какие-либо советы или рекомендации о том, как легко анализировать несколько текстовых файлов в разных форматах, объединять их в один файл .csv и, в конечном итоге, вводить их в базу данных?

Пример текстовых файлов:

general.txt (Имя - Отдел (DEPT) Комната № [Возраст]

John Doe -- Management (MANG) 205 [Age: 40]
Equipment: Laptop, Desktop, Printer, Stapler
Experience: Python, Java, HTML
Description: Hardworking, awesome

Mary Smith -- Public Relations (PR) 605 [Age: 24] 
Equipment: Mac, PC
Experience: Social Skills
Description: fun to be around

Scott Lee -- Programmer (PG) 403 [Age: 25]
Equipment: Personal Computer
Experience: HTML, CSS, JS
Description: super-hacker

Susan Kim -- Programmer (PG) 504 [Age: 21]
Equipment: Desktop
Experience: Social Skills
Descriptions: fun to be around

Bob Simon  -- Programmer (PG) 101 [Age: 29]
Equipment: Pure Brain Power
Experience: C++, C, Java 
Description: never comes out of his room

cars.txt (список людей, которым принадлежат машины по их отделу / комнате #)

Programmer: PG 403, PG 101
Management: MANG 205

house.txt

Programmer: PG 504

Конечный csv предпочтительно табулировать примерно так:

Name     | Division    | Division Abbrevation | Equipment | Room | Age | Car? | House? |
Scott Lee  Programming          PG                 PC        403   25     YES     NO 
Mary Smith Public Rel.          PR               Mac, PC     605   24      NO     NO

Конечная цель - создать базу данных, в которой при поиске "PR" будет возвращаться каждая строка, в которой для отдела указан "PR" и т. Д. Всего может быть 30 текстовых файлов, каждый из которых представляет один или несколько столбцов в базе данных. Некоторые столбцы представляют собой короткие абзацы, которые включают запятые. Всего около 10000 строк. Я знаю, что Python встроил CSV, но я не уверен, с чего начать, и как закончить только с 1 CSV. Любая помощь?

Ответы [ 4 ]

1 голос
/ 27 марта 2012

Похоже, вы ищете кого-то, кто решит всю проблему для вас. Вот и я :) 1001 *

Общая идея состоит в том, чтобы анализировать общую информацию для диктовки (с использованием регулярных выражений), затем добавлять к ней дополнительные поля и, наконец, записывать в CSV. Вот решение Python 3.x (я думаю, что Python 2.7+ должно быть достаточно):

import csv
import re


def read_general(fname):
    # Read general info to dict with 'PR 123'-like keys

    # Gerexp that will split row into ready-to-use dict
    re_name = re.compile(r'''
        (?P<Name>.+)
        \ --\  # Separator + space
        (?P<Division>.+)
        \  # Space
        \(
            (?P<Division_Abbreviation>.*)
        \)
        \  # Space
        (?P<Id>\d+)
        \  # Space
        \[Age:\  # Space at the end
            (?P<Age>\d+)
        \]
        ''', re.X)

    general = {}

    with open(fname, 'rt') as f:
        for line in f:
            line = line.strip()
            m = re_name.match(line)

            if m:
                # Name line, start new man
                man = m.groupdict()
                key = '%s %s' % (m.group('Division_Abbreviation'), m.group('Id'))
                general[key] = man

            elif line:
                # Non empty lines
                # Add values to dict
                key, value = line.split(': ', 1)
                man[key] = value

    return general


def add_bool_criteria(fname, field, general):
    # Append a field with YES/NO value

    with open(fname, 'rt') as f:
        yes_keys = set()

        # Phase one, gather all keys
        for line in f:
            line = line.strip()
            _, keys = line.split(': ', 1)

            yes_keys.update(keys.split(', '))

        # Fill data
        for key, man in general.items():  # iteritems() will be faster in Python 2.x
            man[field] = 'YES' if key in yes_keys else 'NO'


def save_csv(fname, general):
    with open(fname, 'wt') as f:
        # Gather field names
        all_fields = set()
        for value in general.values():
            all_fields.update(value.keys())

        # Write to csv
        w = csv.DictWriter(f, all_fields)
        w.writeheader()
        w.writerows(general.values())


def main():
    general = read_general('general.txt')
    add_bool_criteria('cars.txt', 'Car?', general)
    add_bool_criteria('house.txt', 'House?', general)
    from pprint import pprint
    pprint(general)
    save_csv('result.csv', general)


if __name__ == '__main__':
    main()

Я желаю вам много $$$ для этого;)

Примечание к сведению

CSV - это история, вы можете использовать JSON для хранения и дальнейшего использования, потому что он проще в использовании, более гибок и удобочитаем.

0 голосов
/ 27 марта 2012

Лучший совет: не делай этого.

Твои отношения с машинами и домом, ну, интересно.Владение домом или автомобилем является атрибутом лица или другой организации (компании, партнерства, совместной аренды, общей аренды и т. Д. И т. Д.).Это НЕ атрибут комбинации ("деление", комната).Первый факт в файле вашего автомобиля: «Программист в комнате 403 владеет автомобилем».Что происходит в том маловероятном случае, когда в одной комнате находятся 2 или более программистов?

Оборудование не должно быть в списке.

Не записывать возраст, дату или год записирождения.

Вам нужно несколько таблиц в базе данных, а не 1 CSV-файл.Вам нужно изучить книгу по разработке элементарных баз данных.

0 голосов
/ 27 марта 2012

Я сделаю это задом наперед, начну с загрузки всех этих house.txt и cars.txt, каждый из которых в dict, который может выглядеть следующим образом:

cars = {'MANG': [205], 'PG': [403, 101]}

Поскольку вы сказали, что их около 30, вы можете легко использовать вложенный дикт, не усложняя ситуацию:

data = {'house': {'PG': 504}, 'cars': {...}}

После того, как диктат data будет завершен, загрузите файл general.txt и при построении диктанта для каждого сотрудника (или кем бы он ни был) выполните поиск диктов, чтобы увидеть, есть ли у них дом или нет, или машина, и т.д ..

Например, для Джона Доу вам нужно проверить:

if data['house']['PG'].get(205):
    # ...

и обновите его dict соответственно. Очевидно, вам не нужно жестко кодировать все возможные поиски, просто создайте пару списков ['house', 'cars', ...] или что-то в этом роде и выполняйте итерации по нему.

В конце у вас должен быть большой список dict со всей объединенной информацией, поэтому просто запишите каждый из них в файл csv.

0 голосов
/ 27 марта 2012

У вас просто есть функция, которая анализирует один файл и возвращает список словарей, содержащих {'name': 'Bob Simon', 'age': 29, ...} и т. Д. Затем вызывайте это для каждого из ваших файлов, расширяя основной список.Затем запишите этот основной список диктов в виде файла CSV.

Более подробно:

Сначала вам нужно проанализировать входные файлы, у вас будет функция, которая принимает файл и возвращаетсписок "вещей".

def parse_txt(fname):
    f = open(fname)

    people = []

    # Here, parse f. Maybe using a while loop, and calling
    # f.readline() until there is an empty line Construct a
    # dictionary from each person's block, and append it to allpeople

    return people

Возвращает что-то вроде:

people = [
    {'name': 'Bob Simon', 'age': 29},
    {'name': 'Susan Kim', 'age': 21},
]

Затем переберите каждый из ваших входных файлов (возможно, используя os.listdir или optparse для получения списка аргументов):

allpeople = []
for curfile in args:
     people = parse_txt(fname = curfile)
     allpeople.extend(people)

Итак, allpeople - это длинный список всех людей из всех файлов.

Наконец, вы можете написать это вCSV-файл с использованием CSV-модуля (этот бит обычно включает в себя другую функцию для реорганизации данных в формат, более совместимый с модулем записи)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...