.NET Regex dot character соответствует возврату каретки?

Каждый единственный аромат регулярного выражения, который я когда-либо использовал, всегда имел ".". символ соответствует всем, кроме новой строки (\ r или \n)... если, конечно, вы не включите однострочный флаг.

Итак, когда я попробовал следующий код на С#, я был в шоке:

Regex rgx = new Regex(".");
if (rgx.Match("\r\n").Success)
  MessageBox.Show("There is something rotten in the state of Redmond!");

Он показал сообщение. Чтобы убедиться, что я не схожу с ума, я попробовал следующий код JavaScript:

if (/./.test("\r\n"))
  alert("Something wrong with JavaScript too.");

В JavaScript не было показано сообщение, что означает, что он работает точно так, как должен.

По-видимому, "." символ в .NET соответствует символу "\ r". Я проверил документацию, чтобы узнать, есть ли упоминание об этом:

Подстановочный знак: соответствует любому одиночному символу кроме \n.

Ух ты... с каких пор аромат регулярного выражения когда-либо совпал с точкой возврата каретки? Вы могли бы подумать, что .NET будет вести себя как все остальные варианты Regex... особенно потому, что это в среде Windows, которая использует "\ r\n" в качестве разделителей строк.

Есть ли какой-либо флаг/параметр, который я могу включить, чтобы он работал так же, как и в других вариантах Regex? Существуют ли альтернативные решения, которые не включают замену всех символов . на [^\r\n]?

Ответы

Ответ 1

Я столкнулся с этой проблемой при написании Regex Hero. Это немного странно. Я писал о проблеме здесь. И это привело к добавлению функции тестеру для включения/отключения CRLF. Во всяком случае, по какой-то причине Microsoft решила использовать \n (линейные каналы) для отметки окончаний строки.

(ОБНОВЛЕНИЕ) Причина должна быть связана с этим:

Microsoft.NET Framework выражения включают наиболее популярные функции других регулярных реализации выражений, таких как в Perl и awk. Предназначен для совместимый с Perl 5 выражения, регулярный .NET Framework выражения включают еще функции в других реализациях, таких как совпадение справа налево и на лету сборник. http://msdn.microsoft.com/en-us/library/hs600312.aspx

И как отметил Игорь, Perl имеет такое же поведение.

Теперь Singleline и Multiline RegexOptions изменяют поведение, основанное на точках и линиях. Вы можете включить Singleline RegexOption, чтобы точка соответствовала строкам строк. И вы можете включить Multiline RegexOption так, чтобы ^ и $отмечали начало и конец каждой строки (обозначается линейными фидами). Но вы не можете изменить присущее поведение оператора точки (.) Для соответствия всем, кроме\r\n.

Ответ 2

Я думаю, что точка здесь состоит в том, что точка должна соответствовать всем, что не является разделителем строк, а \r - разделителем строк. Perl уходит с распознаванием только \n, потому что он (как указывали другие) указывает на мир Unix и потому что он вдохновляет ароматы регулярных выражений, встречающиеся на большинстве других языков.

(Но я отмечаю, что в Perl 6 regexes (или Rules, для использования их формального имени) /\n/ соответствует любому, что распознано Unicode как разделитель строк, включая оба символа последовательности \r\n.)

.NET родился в эпоху Unicode; он должен распознавать все разделители строк, поддерживаемые Unicode, включая \r (старый стиль Mac) и \r\n (который используется некоторыми сетевыми протоколами, а также Windows). Рассмотрим этот пример в Java:

String s = "fee\nfie\r\nfoe\rfum";
Pattern p = Pattern.compile("(?m)^.+$");
Matcher m = p.matcher(s);
while (m.find())
{
  System.out.println(m.group().length());
}

результат:

3
3
3
3

., ^ и $ все работают правильно со всеми тремя разделителями строк. Теперь попробуйте в С#:

string s = "fee\nfie\r\nfoe\rfum";
Regex r = new Regex(@"(?m)^.+$");
foreach (Match m in r.Matches(s))
{
  Console.WriteLine(m.Value.Length);
}

результат:

3
4
7

Это похоже на кого-то другого? Здесь у нас есть аромат регулярных выражений, встроенный в платформу Microsoft.NET, и он даже не обрабатывает стандартный разделитель строк Windows. И он полностью игнорирует одиночный \r, так же как и другие разделители строк Unicode..NET появился через несколько лет после Java, и его поддержка Unicode по крайней мере так же хороша, так почему они решили придерживаться этой точки?

Ответ 3

За исключением режима SingleLine, . будет соответствовать любому символу, кроме \n.
Как вы заметили, оно соответствует \r.

Я не знаю почему.

Ответ 4

Регулярные выражения имеют практическое (в противоположность теоретическому) происхождение в среде Unix, где LF является терминатором линии, тогда он кажется полностью подходящим. чтобы соответствовать всем, кроме LF.

Это односимвольное совпадение, поэтому сопоставление CRLF было бы слишком большим, чтобы спросить, и соответствие CR или LF может вызвать проблемы с переходом на другую платформу regex. Я думаю, использование \s было бы лучшим подходом для сопоставления белого пространства и будет соответствовать как CR, так и LF.

Ответ 5

Ну, я не думаю, что "в состоянии Редмонда есть что-то гнилое!", по крайней мере, ваш сценарий не является доказательством этого. Но я думаю, что это поведение не ошибка, а скорее функция. Зачем? Просто потому, что регулярные выражения Perl имеют такое же поведение (я только что проверил его), и я считаю, что PHP PCRE (Perl Compatible Regular Expressions) ведут себя одинаково. И MS просто заставляли их методы Regex вести себя так же, как де-факто классические Perl. И теперь мой вопрос: "Что случилось в царстве JS?":)