Как улучшить мой синтаксис регулярных выражений Python? - PullRequest
3 голосов
/ 29 марта 2010

Я очень плохо знаком с Python и довольно плохо знаком с regex. (У меня нет опыта работы с Perl.)

Я могу использовать регулярные выражения таким образом, чтобы он работал, но я не уверен, что мой код особенно 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-подобную нечитаемость. Я просто хочу выполнить эту общую задачу самым простым и простым способом.

Ответы [ 4 ]

3 голосов
/ 29 марта 2010

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

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

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

Кстати, как 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)
1 голос
/ 29 марта 2010

Есть два трюка: первый - это регулярное выражение 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 голосов
/ 29 марта 2010

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

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

вам не нужно регулярное выражение. разбейте вашу строку на «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']
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...