Регулярное выражение Python (Perl-типа) с опережением/просмотром назад

Рассмотрим строку s = "aa,bb11,22 , 33 , 44,cc , dd ".

Я хотел бы разделить s на следующий список токенов, используя модуль регулярных выражений в < strong>Python, что похоже на функциональность, предлагаемую Perl:

  1. "aa,bb11"
  2. "22"
  3. "33"
  4. "44,cc , dd "

Примечание:

  • Я хочу токенизировать запятые, но только если у этих запятых есть числа с обеих сторон.
  • Любые (необязательные) пробелы вокруг этих «числовых запятых», на которые я ориентируюсь, должны быть удалены в результате. Необязательный пробел может состоять из нескольких пробелов.
  • Любые другие пробелы должны быть оставлены так, как они появляются в исходной строке.

Моя лучшая попытка до сих пор заключается в следующем:

import re

pattern = r'(?<=\d)(\s*),(\s*)(?=\d)'
s = 'aa,bb11,22 , 33 , 44,cc , dd '

print re.compile(pattern).split(s)

но это печатает:

['aa,bb11', '', '', '22', ' ', ' ', '33', ' ', ' ', '44,cc , dd ']

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

Любые идеи?


person JimmidyJoo    schedule 22.07.2012    source источник


Ответы (4)


Не размещайте группы захвата на \s*:

pattern = r'(?<=\d)\s*,\s*(?=\d)'
person mVChr    schedule 22.07.2012
comment
@Ωmega Посмотри еще раз. Он не делает и того, и другого, он использует разделение regex TO. Иначе на что делится? - person mVChr; 22.07.2012
comment
@Ωmega split - это операция регулярного выражения, но и findall тоже. Сплит - это то, о чем просили. Протестировано оба, и независимо от того, что оба достаточно быстры, ваш подход медленнее, чем mVChr, опубликованный здесь, даже после поправки на более медленное время компиляции. - person Phil Cooper; 22.07.2012

Не группируйте \s*, и они не будут захвачены и записаны в выводе:

>>> import re
>>> s = 'aa,bb11,22 , 33 , 44,cc , dd '
>>> re.compile(r'(?<=\d)(\s*),(\s*)(?=\d)').split(s)
['aa,bb11', '', '', '22', ' ', ' ', '33', ' ', ' ', '44,cc , dd ']
>>> re.compile(r'(?<=\d)\s*,\s*(?=\d)').split(s)
['aa,bb11', '22', '33', '44,cc , dd ']
person Justin    schedule 22.07.2012

Вам не нужно использовать регулярное выражение и разделение - это слишком сложно. Смотрите это >>

import re
s = "aa,bb11,22 , 33 , 44,cc , dd "
result = re.findall(ur"(?:^\s*|(?<=\d)\s*,\s*)(.*?)(?=\s*,\s*\d|\s*$)", s)
print(result)

Выход:

['aa,bb11', '22', '33', '44,cc , dd']

Протестируйте здесь.

person Ωmega    schedule 22.07.2012
comment
Если вы хотите сохранить завершающий символ пробела в последнем совпадении (чего, я полагаю, вы не делаете), замените \s*$ в шаблоне регулярного выражения только на $. Аналогичное изменение применяется к начальным пробельным символам в первом совпадении (в вашем примере их нет), поэтому, если вы хотите сохранить эти пробелы, замените ^\s* только на ^. - person Ωmega; 22.07.2012

Вы используете скобки для захвата, дополнительный пробел между ними - это то, что было захвачено двумя (\s*), вы можете использовать скобки без захвата, например:

r'(?<=\d)(?:\s*),(?:\s*)(?=\d)'

хотя скобки вообще не нужны

person Sophie    schedule 22.07.2012