Синтаксис обратных ссылок в замещающих строках (почему знак доллара?) - PullRequest
45 голосов
/ 23 мая 2010

В Java, и, похоже, в некоторых других языках обратным ссылкам в шаблоне предшествует обратный слеш (например, \1, \2, \3 и т. Д.), Но в строке замены им предшествует символ знак доллара (например, $1, $2, $3, а также $0).

Вот фрагмент для иллюстрации:

System.out.println(
    "left-right".replaceAll("(.*)-(.*)", "\\2-\\1") // WRONG!!!
); // prints "2-1"

System.out.println(
    "left-right".replaceAll("(.*)-(.*)", "$2-$1")   // CORRECT!
); // prints "right-left"

System.out.println(
    "You want million dollar?!?".replaceAll("(\\w*) dollar", "US\\$ $1")
); // prints "You want US$ million?!?"

System.out.println(
    "You want million dollar?!?".replaceAll("(\\w*) dollar", "US$ \\1")
); // throws IllegalArgumentException: Illegal group reference

Вопросы:

  • Является ли использование $ для обратных ссылок в замещающих строках уникальным для Java? Если нет, то на каком языке это началось? Какие ароматы используют, а какие нет?
  • Почему это хорошая идея? Почему бы не придерживаться того же синтаксиса шаблона? Не приведет ли это к более сплоченному и более легкому изучению языка?
    • Не станет ли синтаксис более упорядоченным, если бы операторы 1 и 4 в вышеприведенном случае были «правильными» вместо 2 и 3?

Ответы [ 2 ]

33 голосов
/ 23 мая 2010

Является ли использование $ для обратных ссылок в замещающих строках уникальным для Java?

Нет.Perl использует его, и Perl определенно предшествует классу Java Pattern.Поддержка регулярных выражений в Java явно описана в терминах регулярных выражений Perl.

Например: http://perldoc.perl.org/perlrequick.html#Search-and-replace

Почему это хорошая идея?

Хорошоочевидно, вы не думаете, что это хорошая идея!Но одна из причин, по которой это хорошая идея, - сделать поддержку поиска / замены Java (более) совместимой с Perl.

Существует еще одна возможная причина, по которой $ можно было бы рассматривать каклучший выбор, чем \.То есть \ должно быть записано как \\ в литерале Java String.

Но все это чисто спекуляция.Никто из нас не был в комнате, когда были приняты дизайнерские решения.И в конечном итоге не имеет значения, почему они разработали заменяющий синтаксис String таким образом.Решения были приняты и изложены конкретно, и любые дальнейшие обсуждения носят чисто академический характер ... если только у вас не случается, что вы проектируете новый язык или новую библиотеку регулярных выражений для Java.

18 голосов
/ 23 мая 2010

Проведя некоторое исследование, я понял проблемы: Perl имел , чтобы использовать другой символ для обратных ссылок на шаблоны и обратных ссылок, а java.util.regex.* не имеет следовать примеру, по своему усмотрению, не по технической, а по традиционной причине.


На стороне Perl

(Пожалуйста, имейте в виду, что все, что я знаю о Perl на данный момент, получено из чтения статей Википедии, так что не стесняйтесь исправлять любые ошибки, которые я мог сделать)

Причина, по которой пришлось сделать 1011 * таким образом в Perl, заключается в следующем:

  • Perl использует $ в качестве сигилы (то есть символ, прикрепленный к имени переменной).
  • Строковые литералы Perl интерполируются переменными.
  • Регулярное выражение Perl фактически захватывает группы как переменные $1, $2 и т. Д.

Таким образом, из-за того, как Perl интерпретируется и как работает его механизм регулярных выражений, должен использоваться предшествующий слеш для обратных ссылок (например, \1) в шаблоне, потому что если вместо него используется символ $ (например, $1), это приведет к непреднамеренной интерполяции переменных в шаблон.

Строка замены, из-за того, как она работает в Perl, оценивается в контексте каждого совпадения. Для Perl наиболее естественно использовать здесь интерполяцию переменных, поэтому механизм регулярных выражений собирает группы в переменные $1, $2 и т. Д., Чтобы это работало без проблем с остальным языком.

Ссылки


На стороне Java

Java - это совсем другой язык, чем Perl, но самое важное здесь то, что здесь нет интерполяции переменных. Более того, replaceAll является вызовом метода, и, как и при всех вызовах методов в Java, аргументы оцениваются один раз, до вызова метода.

Таким образом, одной лишь функции интерполяции переменных недостаточно, поскольку по сути строка замены должна переоцениваться при каждом совпадении, и это просто не семантика вызовов методов в Java. Переменная с интерполяцией, которая вычисляется до , даже когда вызывается * 1059, практически бесполезна; интерполяция должна происходить во время метода, при каждом совпадении.

Поскольку это не семантика языка Java, replaceAll должен выполнить эту «своевременную» интерполяцию вручную . Таким образом, абсолютно никаких технических причин , почему $ является escape-символом для обратных ссылок в замещающих строках. Это вполне могло быть \. И наоборот, обратные ссылки в шаблоне также можно было бы экранировать с помощью $ вместо \, и технически все равно работало бы так же хорошо.

Причина, по которой Java выполняет регулярные выражения, является чисто традиционной: она просто следует прецеденту, установленному Perl.

...