2010-03-29 6 views
3

Я очень новичок в Python и довольно новичок в регулярном выражении. (У меня нет опыта Perl.)Как улучшить синтаксис регулярного выражения Python?

Я могу использовать регулярные выражения таким образом, чтобы это работало, но я не уверен, что мой код особенно Pythonic или consise.

Например, если бы я хотел прочитать в текстовом файле и распечатать текст, который отображается непосредственно между словами «foo» и «bar» в каждой строке (предполагая, что это произошло один или нулевой раз в строке), я бы написал следующие:

fileList = open(inFile, 'r') 
pattern = re.compile(r'(foo)(.*)(bar)') 
for line in fileList: 
    result = pattern.search(line) 
    if (result != None): 
     print result.groups()[1] 

Есть ли лучший способ? if необходимо избегать вызова groups() на None. Но я подозреваю, что существует более сжатый способ получить совпадающую строку, если она есть, без ошибок при отсутствии.

Я не надеюсь на нечитаемость Perl. Я просто хочу выполнить эту общую задачу самым простым и простым способом.

ответ

3

Я думаю, что все в порядке.

Некоторые незначительные моменты: -

  • Вы можете заменить result.groups()[x] с result.group(x+1).
  • Если вам не нужно снимать foo и bar, просто используйте r'foo(.*)bar'.
  • Если вы используете Python 2.5+, попробуйте использовать the with statement, поэтому даже если есть исключение, файл может быть закрыт должным образом.

BTW, как 5-вкладышем (не то, что я рекомендую это):

import re 
pattern = re.compile(r'foo(.*)bar') 
with open(inFile, 'r') as fileList: 
    searchResults = (pattern.search(line) for line in fileList) 
    groups = (result.group(1) for result in searchResults if result is not None) 
    print '\n'.join(groups) 
+0

По какой-то причине 'result.group (1)' захватывает 'foo' для меня, но' result.group (2) 'работает. –

+0

@FarmBoy: Потому что вы согласны с '(foo) (. *) (Bar)' вместо 'foo (. *) Bar'. – kennytm

+0

Не будет ли индекс кортежа 0-основанным? Я ожидал, что 'result.group (0)' вернет 'foo' в мой код. –

0

вам не нужно регулярное выражение. разделите строку на «bar», повторите их, найдите «foo», разделите на «foo» и получите результаты вправо. Конечно, вы можете использовать другие манипуляции с строками, например, получать индекс и прочее.

>>> s="w1 w2 foo what i want bar w3 w4 foowhatiwantbar w5" 
>>> for item in s.split("bar"): 
...  if "foo" in item: 
...   print item.split("foo")[1:] 
... 
[' what i want '] 
['whatiwant'] 
1

Есть две уловки, чтобы быть было: во-первых, re.finditer регулярная функция выражения (метод). Второй - использование модуля mmap.

Из документации по re.DOTALL, можно отметить, что . не соответствует новой строки:

без этого флага ". будет соответствовать чему угодно, кроме новой строки.

Так что, если вы посмотрите на все матчи в любом месте файла (например, при считывании в строку, используя f.read()), вы можете делать вид, каждая строка представляет собой изолированную подстроку (примечание:., Это не совсем верно, хотя, если вы хотите, чтобы^и $ утверждения работали таким образом, используйте re.MULTILINE). Теперь, поскольку вы отметили, что мы предполагаем, что в строке есть только ноль или один случай, нам не нужно беспокоиться о том, что re.finditer() соответствует больше, чем нужно (потому что это будет!).Так сразу же, вы могли бы заменить все, что с Перебор finditer() вместо того, чтобы:

fileList = open(inFile, 'r') 
pattern = re.compile(r'foo(.*)bar') 
for result in pattern.finditer(fileList.read()): 
    print result.groups(1) 

Это не действительно приятно, хотя. Проблема в том, что весь файл читается в памяти для вашего удобства. Было бы неплохо, если бы был удобный способ сделать это, не разбирая большие файлы. И, ну, есть! Введите модуль mmap.

mmap позволяет обрабатывать файл, как если бы это была строка (изменяемая строка, не менее!), И она не загружает все это в память. Длинный и короткое это, вы можете использовать следующий код вместо:

fileList = open(inFile, 'r+b') 
fileS = mmap.mmap(fileList.fileno(), 0) 
pattern = re.compile(r'foo(.*)bar') 
for result in pattern.finditer(fileS): 
    print result.groups(1) 

и он будет работать точно так же, но не потребляя весь файл сразу (надеюсь).

0

У меня есть несколько незначительных предложений:

  • Если вы не уверены, что foo и bar не может произойти не более одного раза в каждой строке, то лучше использовать .*? вместо .*
  • Если вам нужно убедитесь, что foo и bar должны соответствовать только целых слов (в отличие от foonly и rebar), вы должны добавить \b якоря вокруг них (\bfoo\b и т.д.)
  • Вы можете использовать lookaround для соответствия только самому совпадению ((?<=\bfoo\b).*?(?=\bbar\b)), так что теперь result.group(0) будет содержать матч. Но это не более читаемо :)
Смежные вопросы