как сопоставить только одно вхождение двойного пробела в строке?

#python-3.x #regex

#python-3.x #регулярное выражение

Вопрос:

строка A

 foo bar  bar foo  bar foo
  

строка B

 foo bar  bar foo
  

В строке A двойной пробел встречается многократно.

Я хочу сопоставлять только такие строки, как строка B, в которой только один раз встречается двойной пробел.

Я пытался

 ^.*s{2}.*$
  

но это будет соответствовать обоим.

Как я могу получить желаемый результат? Спасибо.

Комментарии:

1. Зачем вообще использовать regex для этого? Не кажется лучшим вариантом. Просто найдите количество вхождений двух последовательных пробелов в строке. Или, если вы хотите предотвратить подсчет более двух пробелов, вы могли бы использовать регулярное выражение и подсчитать количество (?<![ ]{2})[ ]{2}(?![ ]{2}) совпадений.

2. @41686d6564 у меня был поток строковых данных с этим шаблоном, строка с только одним появлением двойного пробела означает, что это двойной столбец, но с более чем однократным появлением двойного пробела не классифицируется. итак, мне нужно различать эти два случая.

3. Используйте шаблон в моем предыдущем комментарии. Он будет сопоставлять все два последовательных символа пробела, которым непосредственно не предшествуют другие символы пробела (т. Е. ровно два). Теперь подсчитайте количество совпадений в каждой строке, и вы получите свое решение (т. Е. вы ожидаете только одно совпадение, не больше и не меньше). Удачи!

4. @41686d6564, есть много вопросов, опубликованных с тегом regex, где могут быть решения без регулярных выражений, которые, возможно, в некотором смысле лучше. Некоторые из этих вопросов просто отражают желание задающего улучшить свое понимание некоторых функций регулярных выражений, без утверждения, что использование регулярного выражения было бы предпочтительным подходом к данной проблеме. Любыми способами предлагайте решения без регулярных выражений для вопросов, имеющих теги регулярных выражений (за исключением случаев, когда в вопросе указано, что необходимо использовать регулярное выражение), но признайте, что у задающего могут быть веские причины для желания решения в регулярных выражениях.

Ответ №1:

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

 r'^(?!(?:.*(?<! ) {2,}(?! )){2})'
  

Запустите свой движок!

Обратите внимание, что это выражение соответствует

 abc    de fgh
  

где между 'c' и 'd' четыре пробела.

Механизм регулярных выражений Python выполняет следующие операции.

 ^
(?!           : begin negative lookahead
  (?:         : begin non-capture group
    .*        : match 0  characters other than line terminators
    (?<!      : begin negative lookbehind
      [ ]{2,} : match 2  spaces
      (?! )   : negative lookahead asserts match is not followed by a space
    )         : end negative lookbehind
  )           : end non-capture group
  {2}         : execute non-capture group twice
)             : end negative lookahead
  

Ответ №2:

Вы можете сделать:

 ^(?!.*[ t]{2,}.*[ t]{2,})

# Negative look ahead assertion that states 'only start the match 
# on this line IF there are NOT 2 (or potentially more) breaks with
# two (or potentially more) of tabs or spaces'.
  

Демонстрация 1

Если вы хотите использовать ОДИН двойной пробел в строке, но не более:

 ^(?=.*[ t]{2,})(?!.*[ t]{2,}.*[ t]{2,})

# Positive look ahead that states 'only start this match if there is 
# at least one break with two tabs or spaces'

# BUT

# Negative look ahead assertion that states 'only start the match 
# on this line IF there are NOT 2 (or potentially more) breaks with
# two (or potentially more) of tabs or spaces'.
  

Демонстрация 2

Если вы хотите ограничить только двумя пробелами (не табуляциями и не более чем 2 пробелами):

 ^(?=.*[ ]{2})(?!.*[ ]{2}.*[ ]{2})
# Same as above but remove the tabs as part of the assertion
  

Демонстрация 3

Примечание: В вашем регулярном выражении у вас есть s в качестве класса для пробела. Это также соответствует [rntfv ] , поэтому как горизонтальные, так и вертикальные символы пробела.

Примечание 2: Вы можете сделать это и без регулярного выражения (предполагая, что вам нужны только строки, в которых есть 1 и только 1 двойной пробел):

 txt='''
line A

foo bar  bar foo  bar foo
line B

foo bar  bar foo'''

>>> [line for line in txt.splitlines() if len(line.split('  '))==2]
['foo bar  bar foo']
  

Комментарии:

1. спасибо, это работает. не могли бы вы, пожалуйста, объяснить, зачем ему нужно дополнительное [ ]{2} в конце? я не могу понять логику здесь..

Ответ №3:

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

Затем необязательно повторите одиночный символ пробела, за которым следуют символы без пробела до и после сопоставления символа с двойным пробелом.

Класс отрицаемых символов [^Srn] будет сопоставлять любые символы пробела, за исключением новой строки или возврата каретки. Если вы также хотите разрешить сопоставление новых строк, вы могли бы использовать s

 ^S (?:[^Srn]S )*[^Srn]{2}(?:S [^Srn])*S $
  

Объяснение

  • ^ Начало строки
  • S Сопоставьте 1 символы без пробелов
  • (?: Группа без захвата
    • [^Srn]S Сопоставьте символ пробела без перевода строки
  • )* Закройте группу и повторите 0 раз
  • [^Srn]{2} Сопоставьте 2 пробельных символа без перевода строки
  • (?: Группа без захвата
    • S [^Srn] Сопоставьте 1 символы без пробелов, за которыми следует символ пробела без перевода строки
  • )* Закройте группу a и повторите 1 раз
  • S Сопоставьте 1 символы без пробелов
  • $ Конец строки

Демонстрация регулярных выражений