Регулярное выражение С#: отрицательный просмотр вперед не работает с опцией одной строки

Я пытаюсь понять, почему регулярное выражение с отрицательным просмотром вперед не работает, когда включена опция «одна строка».

Пример (упрощенный):

<source>Test 1</source>
<source>Test 2</source>
<target>Result 2</target>
<source>Test 3</source>

Этот:

<source>(?!.*<source>)(.*?)</source>(?!\s*<target)

завершится ошибкой, если включена однострочная опция, и будет работать, если однострочная опция отключена. Например, это работает (отключает опцию одной строки):

(?-s:<source>(?!.*<source>)(.*?)</source>(?!\s*<target))

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

Может ли кто-нибудь объяснить, что мне здесь не хватает?

::::::::::::::::::::::

РЕДАКТИРОВАТЬ: (?!.*) - это негативный прогноз, а не группа захвата.

 <source>(?!.*?<source>)(.*?)</source>(?!\s*<target)

ТАКЖЕ будет FAIL, если включен однострочный режим, поэтому не похоже, что это проблема жадности. Попробуйте это в конструкторе регулярных выражений (например, регулярное выражение Expresso или Rad):

С отключенной одной строкой это соответствует (как и ожидалось):

<source>Test 1</source>    
<source>Test 3</source>

При включенной одной линии:

<source>Test 3</source>

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


person Sylverdrag    schedule 01.06.2010    source источник
comment
Сделайте себе одолжение, проанализировав это с помощью синтаксического анализатора html вместо регулярного выражения #1732454" title="regex соответствует открытым тегам, кроме автономных тегов xhtml"> stackoverflow.com/questions/1732348/   -  person Amarghosh    schedule 01.06.2010
comment
Жду комментарий от Джона Сондерса: 3...2...1...   -  person Tim Pietzcker    schedule 01.06.2010
comment
@Амаргош. Не актуально в моем контексте. Да, есть контексты, в которых использование регулярных выражений - это то, что нужно делать.   -  person Sylverdrag    schedule 01.06.2010


Ответы (2)


Я считаю, что это то, что вы ищете:

<source>((?:(?!</?source>).)*)</source>(?!\s*<target)

Идея состоит в том, что вы сопоставляете каждый символ по одному, но только после того, как убедитесь, что это не первый символ </source>. Кроме того, с добавлением /? к опережающему просмотру вам не нужно использовать нежадный квантификатор.

person Alan Moore    schedule 01.06.2010
comment
+1; Я сделал ошибку в своем предложенном исправлении в комментарии (теперь удален). Этот работает. - person polygenelubricants; 01.06.2010
comment
Очень хорошо. Большое спасибо, Алан! - person Sylverdrag; 01.06.2010

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

<source>(?!.*<source>)(.*?)</source>(?!\s*<target)
        ^^^^^^^^^^^^^^

Теперь давайте рассмотрим, что здесь делает (?!.*<source>): это предварительный просмотр, который говорит, что НЕТ совпадений для .*<source> с этой позиции.

Ну, в однострочном режиме . соответствует всему. После сопоставления первых двух <source> на самом деле ЕСТЬ .*<source>! Таким образом, отрицательный прогноз не работает для первых двух <source>.

В последнем <source>, .*<source> больше не совпадают, поэтому отрицательный прогноз выполняется успешно. Остальная часть шаблона также успешна, и поэтому вы получаете только <source>Test 3</source> в однострочном режиме.

person polygenelubricants    schedule 01.06.2010
comment
Использование классов отрицательных символов проще и быстрее: <source>([^<]*)</source>(?!\s*<target>) - person Pent Ploompuu; 01.06.2010
comment
@pent: в этом случае я не могу использовать классы символов, потому что исходный тег может содержать другие теги (и квадратные скобки), которые также необходимо сопоставить. - person Sylverdrag; 01.06.2010