регулярное выражение Python: захватывать части нескольких строк, которые содержат пробелы - PullRequest
0 голосов
/ 02 марта 2011

Я пытаюсь захватить подстроки из строки, которая похожа на

'some string, another string, '

Я хочу, чтобы группа соответствия результатов была

('some string', 'another string')

мое текущее решение

>>> from re import match
>>> match(2 * '(.*?), ', 'some string, another string, ').groups()
('some string', 'another string')

работает, но практически неосуществимо - то, что я показываю здесь, конечно, значительно сокращено с точки зрения сложности по сравнению с тем, что я делаю в реальном проекте; Я хочу использовать только один «прямой» (не вычисленный) шаблон регулярного выражения. К сожалению, мои попытки пока не увенчались успехом:

Это не соответствует (нет как результат), потому что {2} применяется только к пробелу, а не ко всей строке:

>>> match('.*?, {2}', 'some string, another string, ')

добавление скобок вокруг повторяющейся строки приводит к запятой и пробелу в результате

>>> match('(.*?, ){2}', 'some string, another string, ').groups()
('another string, ',)

добавление еще одного набора паратезов исправляет это, но слишком много для меня:

>>> match('((.*?), ){2}', 'some string, another string, ').groups()
('another string, ', 'another string')

Добавление модификатора без захвата улучшает результат, но по-прежнему пропускает первую строку

>>> match('(?:(.*?), ){2}', 'some string, another string, ').groups()
('another string',)

Я чувствую, что я рядом, но я не могу найти правильный путь.

Кто-нибудь может мне помочь? Других подходов я не вижу?


Обновление после первых нескольких ответов:

Прежде всего, большое спасибо всем, ваша помощь очень ценится! : -)

Как я сказал в оригинальном посте, я опустил много вопросов в моем вопросе, чтобы изобразить реальную основную проблему. Для начала, в проекте, над которым я работаю, я анализирую большое количество файлов (в настоящее время десятки тысяч в день) в ряде (в настоящее время 5, скоро ~ 25, возможно, сотнями позже) различных форматов на основе строк. Существуют также XML, JSON, двоичные и некоторые другие форматы файлов данных, но давайте сосредоточимся.

Чтобы справиться с множеством форматов файлов и использовать тот факт, что многие из них основаны на строках, я создал несколько универсальный модуль Python, который загружает один файл за другим, применяет регулярное выражение к каждой строке и возвращает большую структуру данных с совпадениями. Этот модуль является прототипом, рабочая версия потребует версию C ++ по соображениям производительности, которая будет подключена через Boost :: Python и, возможно, добавит тему диалектов регулярных выражений в список сложностей.

Кроме того, существует не 2 повторения, а количество, варьирующееся в настоящее время от нуля до 70 (или около того), запятая не всегда является запятой, и, несмотря на то, что я сказал изначально, некоторые части шаблона регулярного выражения придется вычислять во время выполнения; скажем так, у меня есть причина попытаться уменьшить «динамическое» количество и иметь как можно больше «фиксированных» паттернов.

Итак, одним словом: Я должен использовать регулярные выражения.


Попытка перефразировать: Я думаю, что суть проблемы сводится к следующему: существует ли нотация Python RegEx, например, включает повторения фигурных скобок и позволяет мне захватить

'some string, another string, '

в * * тысяча пятьдесят-одна

('some string', 'another string')

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


Вторая попытка перефразировать: Почему я не вижу первую строку («некоторую строку») в результате? Почему регулярное выражение выдает совпадение (указывая, что должно быть 2 чего-то), но возвращает только 1 строку (вторую)?

Проблема остается той же, даже если я использую нечисловое повторение, т.е. используя + вместо {2}:

>>> match('(?:(.*?), )+', 'some string, another string, ').groups()
('another string',)

Кроме того, возвращается не вторая строка, а последняя:

>>> match('(?:(.*?), )+', 'some string, another string, third string, ').groups()
('third string',)

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

Ответы [ 6 ]

5 голосов
/ 02 марта 2011

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

[s.strip() for s in mys.split(',') if s.strip()]

Или, если это должен быть кортеж:

tuple(s.strip() for s in mys.split(',') if s.strip())

Код более читабелентоже.Пожалуйста, скажите мне, если это не может быть применено.


РЕДАКТИРОВАТЬ: Хорошо, эта проблема действительно больше, чем казалось на первый взгляд.Оставляя это в исторических целях.(Думаю, я не "дисциплинированный" :))

4 голосов
/ 02 марта 2011

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

import re
thepattern = re.compile("(.+?)(?:,|$)") # lazy non-empty match 
thepattern.findall("a, b, asdf, d")     # until comma or end of line
# Result:
Out[19]: ['a', ' b', ' asdf', ' d']

Ключом здесь является использование findall вместо match.Формулировка вашего вопроса предполагает, что вы предпочитаете match, но это не тот инструмент, который подходит для этой работы - он предназначен для возврата ровно одной строки для каждой соответствующей группы ( ) в регулярном выражении.Поскольку ваше «число строк» ​​является переменным, правильный подход заключается в том, чтобы использовать либо findall, либо split.

Если это не то, что вам нужно, то уточните вопрос.

Редактировать: И если вы должны использовать кортежи, а не списки:

tuple(Out[19])
# Result
Out[20]: ('a', ' b', ' asdf', ' d')
2 голосов
/ 02 марта 2011
import re

regex = " *((?:[^, ]| +[^, ])+) *, *((?:[^, ]| +[^, ])+) *, *"

print re.match(regex, 'some string, another string, ').groups()
# ('some string', 'another string')
print re.match(regex, ' some string, another string, ').groups()
# ('some string', 'another string')
print re.match(regex, ' some string , another string, ').groups()
# ('some string', 'another string')
1 голос
/ 02 марта 2011

Без обид, но вам, очевидно, есть, что узнать о регулярных выражениях, и в конечном итоге вы узнаете, что регулярные выражения не справляются с этой работой.Я уверен, что это конкретное задание выполнимо с регулярными выражениями, но что тогда?Вы говорите, что у вас есть потенциально сотни файлов различных форматов для анализа!Вы даже упомянули JSON и XML, которые в корне несовместимы с регулярными выражениями.

Сделайте себе одолжение: забудьте о регулярных выражениях и изучите pyparsing .Или полностью пропустите Python и используйте автономный генератор синтаксического анализатора, такой как ANTLR .В любом случае вы, вероятно, обнаружите, что грамматики для большинства ваших форматов файлов уже написаны.

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

Я думаю, что суть проблемы кипит вниз: есть ли Python RegEx нотация, например, включает в себя вьющиеся брекеты повторений и позволяет мне захватить некоторую строку, другую строку, '?

Я не думаю, что есть такая запись.

Но регулярные выражения - это вопрос не только NOTATION, то есть строки RE, используемой для определения регулярного выражения. Это также вопрос ИНСТРУМЕНТОВ, то есть функций.

К сожалению, я не могу использовать findall как строка из исходного вопроса является лишь частью проблемы, реальная строка намного длиннее, поэтому findall работает только если я делаю несколько регулярные выражения findalls / совпадений / поисков.

Вы должны дать больше информации без задержек: мы могли бы быстрее понять, каковы ограничения. Потому что, на мой взгляд, чтобы ответить на вашу проблему, как она была раскрыта, findall () действительно в порядке:

import re

for line in ('string one, string two, ',
             'some string, another string, third string, ',
             # the following two lines are only one string
             'Topaz, Turquoise, Moss Agate, Obsidian, '
             'Tigers-Eye, Tourmaline, Lapis Lazuli, '):

    print re.findall('(.+?), *',line)

Результат

['string one', 'string two']
['some string', 'another string', 'third string']
['Topaz', 'Turquoise', 'Moss Agate', 'Obsidian', 'Tigers-Eye', 'Tourmaline', 'Lapis Lazuli']

Теперь, так как вы "опустили много сложности" в своем вопросе, findall () может оказаться недостаточным для удержания этой сложности. Тогда будет использоваться finditer () , поскольку это обеспечивает большую гибкость при выборе групп совпадений

import re

for line in ('string one, string two, ',
             'some string, another string, third string, ',
             # the following two lines are only one string
             'Topaz, Turquoise, Moss Agate, Obsidian, '
             'Tigers-Eye, Tourmaline, Lapis Lazuli, '):

    print [ mat.group(1) for mat in re.finditer('(.+?), *',line) ]

дает тот же результат и может быть сложнее, если вместо mat.group (1)

ввести другое выражение
0 голосов
/ 10 марта 2011

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

>>> from re import match
>>> match(2 * '(.*?), ', 'some string, another string, ').groups()
('some string', 'another string')

2 * '(.*?)

это то, что я подразумеваю под динамикой. Альтернативный подход

>>> match('(?:(.*?), ){2}', 'some string, another string, ').groups()
('another string',)

не может вернуть желаемый результат из-за того, что (как любезно объяснили Гленн и Алан)

при совпадении захваченный контент перезаписывается с каждым повторением захвата группа

Спасибо за вашу помощь всем! : -)

...