Подтвердить что ты не робот

Синтаксис ссылок на ссылки в строках замены (почему знак доллара?)

В 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?
4b9b3361

Ответ 1

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

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

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

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

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

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

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

Ответ 2

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


На стороне Perl

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

Причиной, по которой это было, сделано в Perl, является следующее:

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

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

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

Ссылки


На стороне Java

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

Таким образом, функция переменной интерполяции сама по себе недостаточно, так как по существу заменяющая строка должна быть переоценена для каждого совпадения, а это просто не семантика вызовов методов в Java. Строка замены с переменным интерполированием, которая оценивается до replaceAll, даже вызвана, практически бесполезна; интерполяция должна выполняться во время метода в каждом матче.

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

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