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

В 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?
47
задан Nathan 1 February 2016 в 05:30
поделиться

2 ответа

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

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

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

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

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

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

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

34
ответ дан 26 November 2019 в 19:52
поделиться

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


Со стороны Perl

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

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

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

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

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

Ссылки


На стороне Java

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

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

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

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

20
ответ дан 26 November 2019 в 19:52
поделиться
Другие вопросы по тегам:

Похожие вопросы: