Есть ли случай, когда "[^ xy]" не равно "(?! X | y)."?
Я работаю над своей собственной библиотекой JavaScript для поддержки новых метасимволов и функций для регулярных выражений, и я хотел бы найти случай, когда [^xy]
не эквивалентен (?!x).
(или, более конкретно, (?:(?!x|y).)
).
Возьмем пример текста: "abc\n"
Скажем, я хочу эмулировать регулярное выражение Perl: /\A.{3}\Z/s
С флагом singleline регулярное выражение JavaScript должно быть эквивалентно: /^[\s\S]{3}\n*$(?!\s)/
(\A
становится ^
, .
становится [\s\S]
, \Z
становится \n*$(?!\s)
)
Теперь /^.{3}$/
завершится с ошибкой, но /^[\s\S]{3}\n*$(?!\s)/
будет захватывать "abcabc" (так же, как и регулярное выражение Perl)
Так как \Z
содержит больше, чем просто метасимвол, эмуляция [^\Z]
представляется сложнее.
Возьмем пример текста: "abcabc\n"
Предложенное регулярное выражение JavaScript для регулярного выражения Perl /.{3}[^\Za]/g
будет .{3}(?:(?!\n*$(?!\s)|a).)/g
Оба будут соответствовать "bcab"
Итак, наконец, я снова задаю вопрос. Есть ли случай, когда [^xy]
не эквивалентен (?:(?!x|y).)
с таким сценарием, возможно, в более сложном регулярном выражении, где lookahead изменит сценарий?
Ответы
Ответ 1
Есть ли случай, когда [^xy]
не равно (?!x|y).
?
Только тот, который вы уже описали: Точка JS не соответствует символам новой строки и должна быть заменена на [\s\S]
.
\Z
становится \n$(?!\s)
Это выглядит неправильно. После окончания строки (\Z
/$
) никогда не будет ничего, независимо от того, пробелы или нет. Afaik, \Z
- это утверждение с нулевой шириной (оно не потребляет новую строку (строки)) и должно быть эквивалентно
(?=\n*$)
// ^ not sure whether ? or *
Так как \Z
содержит больше, чем просто метасимвол, эмуляция [^\Z]
представляется более сложной.
Что вы подразумеваете под "метасимволом"? Это утверждение с нулевой шириной и не имеет особого смысла в классе символов. Я бы предположил, что это либо синтаксическая ошибка, либо будет интерпретирована буквально (неэкранированная) как [^Z]
.
Ответ 2
Для строки ввода "x\na"
2 регулярных выражения выдают разные выходы, потому что .
не соответствует символам новой строки.
console.log("x\na".match(/(?:(?!x|y).)/))
["a", index: 2, input: "x↵a"]
console.log("x\na".match(/[^xy]/))
["↵", index: 1, input: "x↵a"]
Если вы меняете .
на [\s\S]
, в этом случае вывод идентичен:
console.log("x\na".match(/(?:(?!x|y)[\s\S])/))
["↵", index: 1, input: "x↵a"]
Я не могу сейчас думать ни о каком другом случае.
Ответ 3
[^xy]
будет соответствовать \n
. (?!x|y).
по умолчанию не соответствует \n
(поскольку .
не соответствует \n
)
Я не верю, что у javascript есть модификатор "dotall" или "single-line", но с новыми версиями каждого браузера, который ударяет каждые пару месяцев, я потерял трек.
Ответ 4
Как говорили другие, вы должны использовать [\s\S]
вместо .
в замене. В противном случае, если вы делаете это преобразование только через литеральные строки, вам нужно еще кое-что позаботиться. В частности, метасимволы и escape-последовательности:
[^*)] => (?!\*|\))[\s\S]
Но я думаю, вам все равно нужно позаботиться о разборе и написании мета-персонажей.
Самый сложный из них, вероятно, \b
, хотя, потому что это символ (обратное пространство) в классах символов и граница слова снаружи. Поэтому при замене вам придется идти с восьмеричным или шестнадцатеричным побегом:
[^a\b] => (?!a|\10)[\s\S]
or => (?!a|\x08)[\s\S]
Кроме этого, они должны быть всегда эквивалентными.
Ответ 5
Случай, когда формат [^xy]
не совпадает с (?:(?!x|y).)
, где x было утверждением с нулевой шириной, а не фактическим символом, например:
Учитывая этот образец текста: ab-yz
Regex: [^\by]
Пример: http://www.rubular.com/r/ERKrqyeAs9
Возвращает:
[0] => a
[1] => b
[2] => -
[3] => z
В то время как
Regex: (?:(?!\b|y).)
example: http://www.rubular.com/r/V5RdyQEQo5
Возвращает:
[0] => b
[1] => z
Другие неэквивалентные выражения, они в основном фокусируются на том факте, что тот же синтаксис имеет разные значения внутри или вне класса символов:
-
[^^y]
дает a, b, -, z не равно (?:(?!^|y).)
, дает b, -, z
-
[^.y]
дает a, b, -, z не равно (?:(?!.|y).)
ничего не дает
Или вы можете попробовать это в юникодном саморождении в Perl: http://ideone.com/2xMfkQ
print "\ncapture\n";
@m = ("ss" =~ m/^(?:(?!\xDF|y).)+$/ui );
print for @m;
print "\nclass\n";
@m = ("ss" =~ m/^[^\xDFy]+$/ui) ;
print for @m;
Урожайность:
capture
class
1