Захватите разделитель пробела, запятой и пробела с помощью регулярного выражения - PullRequest
0 голосов
/ 11 июля 2019

Совершенно новый для Python. Я использую csv ридер для разбора некоторых файлов. Я собираюсь проанализировать информацию, которая использует 3 различных разделителя. Запятая, труба и пробел (пока).

У меня есть это:

    with open(filepath, "r") as fp:
            file_lines = fp.readlines()
            delimiter = re.search("\w+([^\w])", file_lines[0]).group(1)
            reader = csv.reader(file_lines, delimiter=delimiter)
            print('Delimiter: [{}]'.format(delimiter))
            line_list = [row for row in reader]
            print(line_list)

Это работает с моим comma.txt файлом. Но при передаче в мой файл pipe.txt он захватывает пробелы, прежде чем захватить реальный канал.

пример ввода строки с трубами будет выглядеть так:

Трубы: Bouillon | Francis | G | M | Blue | 6-3-1975

Пробелы: Bouillon Francis G M Blue 6-3-1975

Запятые: Bouillon, Francis, G, M, Blue, 6-3-1975

Ребята, вы бы порекомендовали другой подход? Или я должен вместо этого просто изменить свое регулярное выражение?

Ответы [ 4 ]

2 голосов
/ 11 июля 2019

Вы можете попробовать использовать класс csv.sniffer, чтобы определить диалект csv, который вы собираетесь анализировать.

Функция sniff() принимает строку потенциальных разделителей, которые она будет использовать, чтобы попытаться определить, как анализировать файл. Это довольно умно, но тот факт, что ваши потенциальные разделители содержат пробел и ваши | файлы имеют пробелы, является проблемой для него. Если вы передадите delimiters=',| ' с пробелом, он определит пробел как разделитель для файлов, разделенных |. Один из вариантов - попробовать с разделителями без пробелов, а в случае неудачи - с пробелами:

import csv
with open('test_space.csv') as csvfile:
    try:
        dialect = csv.Sniffer().sniff(csvfile.read(1024), delimiters=',|')
    except:
        csvfile.seek(0)
        dialect = csv.Sniffer().sniff(csvfile.read(1024), delimiters=' ')
    dialect.skipinitialspace = True
    csvfile.seek(0)

   reader = csv.reader(csvfile, dialect)
    for line in reader:
        print(list(map(str.strip, line)))

Это будет правильно идентифицировать строки, подобные этой, как разделенные пробелом:

Bou|illon Francis G M Bl,ue 6-3-1975
Bouillon Francis G M Blue 6-3-1975
Bouillon Franc,is G M Blue 6-3-1975

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

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

Bou|illon Francis G M Bl,ue 6-3-1975
Bou,illon Francis G M Blue 6-3-1975
Bouillon Franc,is G M Blue 6-3-1975
1 голос
/ 11 июля 2019

Как я уже сказал в моих комментариях, регулярное выражение работает как задумано.;)

Bouillon | Francis | G | M | Blue | 6-3-1975 с \w+([^\w]) получает 'Bouillon ' как group(0) (полное совпадение), потому что пробел является первым несловарным символом.;)

Если вы хотите сохранить пробелы в своих данных или ваши данные могут содержать пробелы (например, Name Surname|Age), то вы не можете искать пробелы в том же регулярном выражении, что и поиск каналов и запятых -потому что этот отступ или пробел в первом значении будет пойман.

(Если вы не ищете более одного символа в этом регулярном выражении, но вам нужен более сложный код, и мне нравится простота и удобочитаемость.;))

То, что вы можете сделать, это:

  1. Поиск канала и запятой (при условии, что в содержимом, разделенном каналом, нет запятых, а в содержимом, разделенном запятыми, нет каналов).Допускайте пробелы, только если поиск не удался.
search = re.search(r"[|,]", file_lines[0]) # add other delimeters in square brackets
# we don't have capturing groups, our full catch (group 0) is first character that matches possible delimeters
separator = search.group(0) if search else " " # is search was empty, assume space

Другой подход основан на иерархии.

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

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

Это может быть реализовано в виде простого цикла for, а ваши возможные разделители могут быть простой строкой из наиболее важных.разделитель - "|,".Регексы плохи для таких простых вещей.;)

possible_separators = "|,"
separator = " "
for sep in possible_separators:
    if sep in file_lines[0]:
        separator = sep
        break
0 голосов
/ 11 июля 2019

2 подходов:

(вы также можете продолжить без csv.reader, просто разделите на sep с конечными пробелами)

Примеры файлов:

pipe.txt:

Bouillon | Francis | G | M | Blue | 6-3-1975
a | b | c | d | f | g

comma.txt:

Bouillon , Francis , G , M , Blue , 6-3-1975
a , b , c , d , f , g

space.txt

Bouillon   Francis   G   M   Blue   6-3-1975
a   b   c   d   f   g

import csv
from itertools import chain

with open('pipe.txt') as f:
    line = next(f).strip()   # extracting the 1st line
    sep = re.search(r'^\w+([\s\|,]+)', line).group(1)
    sep = ' ' if sep.isspace() else sep.strip()

    reader = csv.reader(chain(iter([line]), f), delimiter=sep, skipinitialspace=True)
    for row in reader:
        print(row)

Вывод (для файла comma.txt и pipe.txt):

['Bouillon ', 'Francis ', 'G ', 'M ', 'Blue ', '6-3-1975']
['a ', 'b ', 'c ', 'd ', 'f ', 'g']

with open('space.txt') as f:
...

Выход для space.txt более чистый благодаря функции skipinitialspace=True:

['Bouillon', 'Francis', 'G', 'M', 'Blue', '6-3-1975']
['a', 'b', 'c', 'd', 'f', 'g']

или без csv.reader:

with open('comma.txt') as f:
    line = next(f).strip()
    sep = re.search(r'^\w+([\s\|,]+)', line).group(1)
    pat = re.compile(sep)

    for row in chain(iter([line]), f):
        print(pat.split(row.strip()))

Выход:

['Bouillon', 'Francis', 'G', 'M', 'Blue', '6-3-1975']
['a', 'b', 'c', 'd', 'f', 'g']

Наслаждайтесь!

0 голосов
/ 11 июля 2019

От макушки головы я бы пошел к чему-то вроде

([^\w-]|[|]|[,])

Если вы урежете это, вы получите свои разделители.Взгляните на RegExr , чтобы проверить ваши файлы.Это регулярное выражение JavaSript, но я нашел его полезным и для отладки регулярного выражения Python.

EDIT

, как правильно указал @ h4z3, вы можете упростить:

([^\w-]|[|,])
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...