Как убрать переменные пробелы в каждой строке текстового файла на основе специального условия - однострочный в Python?

У меня есть некоторые данные (текстовые файлы), которые отформатированы самым неровным образом. Я пытаюсь свести к минимуму объем ручной работы по анализу этих данных.

Пример данных:

Name        Degree      CLASS       CODE        EDU     Scores
--------------------------------------------------------------------------------------
John Marshall       CSC   78659944   89989        BE   900
Think Code DB I10   MSC  87782  1231  MS            878
Mary 200 Jones    CIVIL      98993483  32985        BE       898
John G. S  Mech 7653 54 MS 65
Silent Ghost  Python Ninja 788505  88448  MS Comp  887

Условия:

  • Несколько разделителей должны быть сжаты до разделителя (лучше конвейер? Конечная цель - сохранить эти файлы в базе данных).
  • За исключением первого столбца, в других столбцах не будет пробелов, поэтому все эти пробелы могут быть сжаты в канал.
  • Только в первом столбце может быть несколько слов с пробелами (Мэри К Джонс). Остальные столбцы в основном цифры и некоторые алфавиты.
  • Первый и второй столбцы являются строками. Они почти всегда имеют более одного пробела между ними, поэтому мы можем различать эти 2 столбца. (Если есть один пробел, это риск, на который я готов пойти, учитывая ужасное форматирование!).
  • Количество столбцов варьируется, поэтому нам не нужно беспокоиться об именах столбцов. Все, что мы хотим, это извлечь данные каждого столбца.

Надеюсь, у меня есть смысл! У меня есть ощущение, что эту задачу можно выполнить в режиме oneliner. Я не хочу зацикливаться, зацикливаться, зацикливаться:(

Muchos gracias "Pythonistas" за то, что прочитали все до конца и не выходили перед этим предложением!

3 ответа

Решение

До сих пор кажется, что в ваших файлах есть какой-то формат:

>>> regex = r'^(.+)\b\s{2,}\b(.+)\s+(\d+)\s+(\d+)\s+(.+)\s+(\d+)'
>>> for line in s.splitlines():
    lst = [i.strip() for j in re.findall(regex, line) for i in j if j]
    print(lst)


[]
[]
['John Marshall', 'CSC', '78659944', '89989', 'BE', '900']
['Think Code DB I10', 'MSC', '87782', '1231', 'MS', '878']
['Mary 200 Jones', 'CIVIL', '98993483', '32985', 'BE', '898']
['John G. S', 'Mech', '7653', '54', 'MS', '65']
['Silent Ghost', 'Python Ninja', '788505', '88448', 'MS Comp', '887']

Regex довольно прост, единственное, на что нужно обратить внимание - это разделители (\s) и слово ломается (\b) в случае первого разделителя. Обратите внимание, что когда строка не совпадает, вы получаете пустой список lst, Это был бы флаг чтения, чтобы вызвать взаимодействие с пользователем, описанное ниже. Также вы можете пропустить строки заголовка, выполнив:

>>> file = open(fname)
>>> [next(file) for _ in range(2)]
>>> for line in file:
    ...  # here empty lst indicates issues with regex

Предыдущие варианты:

>>> import re
>>> for line in open(fname):
    lst = re.split(r'\s{2,}', line)
    l = len(lst)
    if l in (2,3):
        lst[l-1:] = lst[l-1].split()
    print(lst)

['Name', 'Degree', 'CLASS', 'CODE', 'EDU', 'Scores']
['--------------------------------------------------------------------------------------']
['John Marshall', 'CSC', '78659944', '89989', 'BE', '900']
['Think Code DB I10', 'MSC', '87782', '1231', 'MS', '878']
['Mary 200 Jones', 'CIVIL', '98993483', '32985', 'BE', '898']
['John G. S', 'Mech', '7653', '54', 'MS', '65']

другое, что нужно сделать, это просто позволить пользователю решить, что делать с сомнительными записями:

if l < 3:
    lst = line.split()
    print(lst)
    iname = input('enter indexes that for elements of name: ')     # use raw_input in py2k
    idegr = input('enter indexes that for elements of degree: ')

Хм, у меня все время было впечатление, что второй элемент может содержать пробелы, поскольку это не тот случай, который вы могли бы просто сделать:

>>> for line in open(fname):
    name, _, rest = line.partition('  ')
    lst = [name] + rest.split()
    print(lst)

Изменение ответа SilentGhost, на этот раз сначала разделяя имя от остальных (разделенных двумя или более пробелами), затем просто разделяя остальные, и, наконец, составляя один список.

import re

for line in open(fname):
    name, rest = re.split('\s{2,}', line, maxsplit=1)
    print [name] + rest.split()

Этот ответ был написан после того, как ОП признался, что изменил каждую вкладку ("\t") в своих данных на 3 пробела (и не упомянул об этом в своем вопросе).

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

Вместо того чтобы делать line.replace('\t', ' ' * 3) пытаться line.expandtabs(),

Документы для расширенных таблиц здесь.

Если результат выглядит разумным (столбцы данных выстраиваются в линию), вам нужно будет определить, как вы можете программно определить ширину столбцов (если это возможно) - возможно, из строки заголовка.

Вы уверены, что во второй строке все "-", или между столбцами есть пробелы? Причина для того, чтобы спросить, состоит в том, что мне когда-то нужно было проанализировать много разных файлов из механизма отчетов о запросах к базе данных, который представил результаты, подобные следующим:

RecordType  ID1                  ID2         Description           
----------- -------------------- ----------- ----------------------
1           12345678             123456      Widget                
4           87654321             654321      Gizmoid

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

sizes = map(len, dash_line.split())

Если expandtabs() не работает, отредактируйте свой вопрос, чтобы показать, что именно у вас есть, то есть показать результат print repr(line) для первых 5 или около того строк (включая строку заголовка). Также было бы полезно, если бы вы могли сказать, какое программное обеспечение производит эти файлы.

Другие вопросы по тегам