C # Regex Заменить странное поведение с несколькими захватами и сопоставления в конце строки? - PullRequest
2 голосов
/ 07 августа 2010

Я пытаюсь написать что-то, что форматирует бразильские телефонные номера, но я хочу, чтобы оно совпадало с концом строки, а не с начала, поэтому входные строки были бы повернуты по следующей схеме:

"5135554444" -> "(51) 3555-4444"
"35554444" -> "3555-4444"
"5554444" -> "555-4444"

Поскольку начальная часть - это то, что обычно изменяется, я подумал о построении соответствия с использованием знака $, чтобы оно начиналось в конце, а затем перехватывало назад (как я думал), заменяя затем желаемым конечным форматом и после , просто избавившись от парентези "()" впереди, если они были пусты.

Это код C #:

s = "5135554444";
string str = Regex.Replace(s, @"\D", ""); //Get rid of non digits, if any
str = Regex.Replace(str, @"(\d{0,2})(\d{0,4})(\d{1,4})$", "($1) $2-$3");
return Regex.Replace(str, @"^\(\) ", ""); //Get rid of empty () at the beginning

Возвращаемое значение было ожидаемым для 10-значного числа. Но для чего-то меньшего, это закончилось странным поведением. Это были мои результаты:

"5135554444" -> "(51) 3555-4444"
"35554444" -> "(35) 5544-44"
"5554444" -> "(55) 5444-4"

Кажется, что он игнорирует $ в конце для сопоставления, за исключением того, что если я проверяю что-то менее чем из 7 цифр, это выглядит так:

"554444" -> "(55) 444-4"
"54444" -> "(54) 44-4"
"4444" -> "(44) 4-4"

Обратите внимание, что он сохраняет «минимальное» число {n} раз, когда третья группа захвата всегда захватывает его с конца, но затем первые две группы захватывают с самого начала, как если бы последняя группа не была жадной из конец, просто получение минимума ... странно или это я?

Теперь, если я изменю шаблон, поэтому вместо {1,4} на третьем снимке я использую {4}, это результаты:

str = Regex.Replace(str, @"(\d{0,2})(\d{0,4})(\d{4})$", "($1) $2-$3");

"5135554444" -> "(51) 3555-4444" //As expected
"35554444" -> "(35) 55-4444" //The last four are as expected, but "35" as $1?
"54444" -> "(5) -4444" //Again "4444" in $3, why nothing in $2 and "5" in $1?

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

Я бы подумал, что "54444" превратится в "5-4444" в этом последнем примере ... тогда это не ...

Как можно это сделать?

(Я знаю, может быть, есть лучший способ сделать то же самое, используя разные подходы ... но мне действительно интересно узнать, почему это специфическое поведение регулярного выражения кажется странным. Итак, ответ на этот вопрос Вопрос должен быть сосредоточен на объяснении, почему последний захват фиксируется в конце строки, а почему нет, как продемонстрировано в этом примере. Поэтому меня не особо интересует реальная проблема с форматированием # телефона, но чтобы понять Регулярное выражение синтаксис) ...

Спасибо ...

Ответы [ 2 ]

1 голос
/ 07 августа 2010

Итак, вы хотите, чтобы третья часть всегда имела четыре цифры, вторая часть - от нуля до четырех цифр, а первая часть - от нуля до двух цифр, но только если вторая часть содержит четыре цифры?

Использовать

^(\d{0,2}?)(\d{0,4})(\d{4})$

Как фрагмент кода C #, прокомментировал:

resultString = Regex.Replace(subjectString, 
  @"^             # anchor the search at the start of the string
    (\d{0,2}?)    # match as few digits as possible, maximum 2
    (\d{0,4})     # match up to four digits, as many as possible
    (\d{4})       # match exactly four digits
    $             # anchor the search at the end of the string", 
   "($1) $2-$3", RegexOptions.IgnorePatternWhitespace);

Путем добавления ? к квантификатору (??, *?, +?, {a,b}?)Вы делаете его ленивым, то есть говорите, чтобы оно совпадало с как можно меньшим количеством символов, в то же время позволяя найти общее совпадение.

Без ? в первой группе, что произойдет при попытке сопоставления 123456?

Сначала \d{0,2} соответствует 12.

Затем \d{0,4} соответствует 3456.

Затем \d{4} не изменяетсяУ нас ничего не осталось, поэтому двигатель regex возвращается, пока это снова не станет возможным.После четырех шагов \d{4} может соответствовать 3456.\d{0,4} отдает все, что ему соответствует, жадно для этого.

Теперь найдено общее совпадение - не нужно больше пробовать комбинации.Поэтому первая и третья группы будут содержать части матча.

0 голосов
/ 07 августа 2010

Вы должны сказать, что все в порядке, если первых подходящих групп нет, но нет последней:

(\d{0,2}?)(\d{0,4}?)(\d{1,4})$

Соответствует вашим примерам в моем тестировании должным образом.

...