Преобразование текстового файла с разделителями пробелами с использованием Python? - PullRequest
0 голосов
/ 27 августа 2018

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

У меня проблемы с форматом файла, это очень простой пример

col1 col2                 col3 text                      col4

1403 bash                 2014-07-28 22:32:53 UTC+0000   sudo bash
1464 bash                 2014-07-28 22:32:28 UTC+0000   sudo root

когда я анализирую файл, я получаю это для данных ниже тире:

['1403', 'bash', '2014-07-28', '22:32:53', 'UTC+0000', 'sudo', 'bash']

Я хочу, чтобы это выглядело так:

['1403', 'bash', '2014-07-28 22:32:53 UTC+0000', 'sudo bash']

Это очень простой пример. но в основном я сопоставляю заголовки с данными под тире. дайте мне знать, если вы можете помочь с чем-либо.

ПРИМЕЧАНИЕ: вывод не должен быть точно таким, как показано, я просто хочу иметь возможность разделить данные cols.

Вот то, что у меня есть в коде, пока он разделяет заголовки на отдельные столбцы:

colNames = testFile.readline()
#tempList = re.split('(\s )', colNames)
headerList = []
for i in tempList:
    j = i.replace(' ','')
    if j != '':
        headerList.append(j)

Затем у меня есть цикл для просмотра данных в зависимости от их местоположения (именно здесь я считаю, что мне нужно найти способ лучше разделить данные):

   for line in testFile.readlines():
        if rowCounter > 0:
            row = line.split()
            print row
            for i in range(len(headerList)):
                colNameDic.update({headerList[i]:row[i]})
            rowDic = dict(colNameDic)
            fileList.append(rowDic)
            rowDic = {}
        rowCounter +=1

Ответы [ 3 ]

0 голосов
/ 27 августа 2018

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

In [20]: h = 'col1 col2                 col3_text                      col4\n'
In [21]: r = '1403 bash                 2014-07-28 22:32:53 UTC+0000   sudo bash\n'
In [22]: fields = h.split()
    ...: ifields = [h.index(f) for f in fields]+[None]
In [23]: slices = [slice(i,j) for i, j in zip(ifields, ifields[1:])]
In [24]: tokens = [r[s] for s in slices]
In [25]: tokens
Out[25]: 
['1403 ',
 'bash                 ',
 '2014-07-28 22:32:53 UTC+0000   ',
 'sudo bash\n']
In [26]: 

PS Вы можете rstrip отдельные предметы, [r[s].rstrip() for s in slices].


Добавление

Если можно найти различение между допустимыми именами столбцов и мусором, требование об отсутствии посторонних пробелов можно ослабить ... ваш конкретный формат данных (без подчеркиваний ...)

In [35]: h = 'col1 col2                 col3 text                      col4\n'
In [36]: r = '1403 bash                 2014-07-28 22:32:53 UTC+0000   sudo bash\n'
In [37]: fields = [f for f in h.split() if f[:3]=='col']
In [38]: ifields = [h.index(f) for f in fields]+[None]
In [39]: slices = [slice(i,j) for i, j in zip(ifields, ifields[1:])]
In [40]: tokens = [r[s].rstrip() for s in slices]
In [41]: tokens
Out[41]: ['1403', 'bash', '2014-07-28 22:32:53 UTC+0000', 'sudo bash']
In [42]: 
0 голосов
/ 28 августа 2018

Вот альтернативный подход: выполнить двухпроходную обработку над строками текста. В первом проходе мы угадаем начальные индексы: индексы, где непробельный символ предшествовал пробел. Для каждой строки начальные индексы могут немного отличаться, но если мы посмотрим на те индексы, у которых они все общие, то эти индексы могут быть началами столбцов. Гадание стартовых индексов не идеально. Требуется, чтобы ни одна ячейка в строке отсутствовала

Во втором проходе мы будем использовать эти индексы, чтобы разбить строку на столбцы, обрезать завершающие пробелы.

import itertools

def split_columns(row, start_indices):
    start_indices = start_indices + [None]
    a, b = iter(start_indices), iter(start_indices)
    next(b)

    columns = []
    for istart, istop in zip(a, b):
        columns.append(row[slice(istart, istop)].strip())

    return columns

def guess_start_indices(row):
    row = ' ' + row
    prev_seq, cur_seq = iter(row), iter(row)
    next(cur_seq)

    start_indices = []
    for i, (prev, cur) in enumerate(zip(prev_seq, cur_seq)):
        if prev == ' ' and cur != ' ':
            start_indices.append(i)
    return set(start_indices)

def find_common_start_indices(rows):
    start_indices = set.intersection(*(guess_start_indices(row) for row in rows))
    start_indices = sorted(start_indices)
    return start_indices

if __name__ == '__main__':
    with open('columnize.txt') as rows:
        first_pass, second_pass = itertools.tee(rows)

        start_indices = find_common_start_indices(first_pass)
        print(start_indices)

        for row in second_pass:
            print(split_columns(row, start_indices))

Примечания

  • В коде я создал два итератора first_pass и second_pass, чтобы помочь перебирать строки текста. Эти итераторы важны, потому что они позволяют перебирать строки дважды, не перематывая файл.
  • Тема этого подхода - угадывание, поэтому будет текст, который обманывает код, и он будет делать неправильные догадки
  • Поэтому используйте это решение в качестве отправной точки и проверьте вывод
0 голосов
/ 27 августа 2018

Пока ширина столбцов фиксирована, вы можете использовать синтаксический анализатор фиксированной ширины:

pd.read_fwf(io.StringIO(data), skiprows=[1], 
            parse_dates=["col3 text"], # Optional
            colspecs=[(0,4),(5,25),(26,54),(57,66)])
#   col1  col2            col3 text       col4
#0  1403  bash  2014-07-28 22:32:53  sudo bash
#1  1464  bash  2014-07-28 22:32:28  sudo root
...