В 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? В противном случае, какой язык запустил его? Какие разновидности используют его и что не делает?Является ли использование $ для обратных ссылок в замещающих строках уникальным для Java?
Нет. Perl использует его, и Perl определенно предшествует классу Java Pattern
. Поддержка регулярных выражений Java явно описана в терминах регулярных выражений Perl.
Например: http://perldoc.perl.org/perlrequick.html#Search-and-replace
Почему это хорошая идея?
Очевидно, вы так не думаете хорошая идея! Но одна из причин, по которой это хорошая идея, - сделать поддержку поиска / замены Java (больше) совместимой с Perl.
Существует еще одна возможная причина, по которой $
могло рассматриваться как лучший выбор, чем \
. То есть \
должен быть записан как \\
в строковом литерале Java.
Но все это чистые предположения. Когда принимались дизайнерские решения, никого из нас в комнате не было. И, в конечном счете, не имеет значения, почему они так разработали синтаксис замены String. Решения были приняты и конкретизированы, и любое дальнейшее обсуждение носит чисто академический характер ... если только вы не разрабатываете новый язык или новую библиотеку регулярных выражений для Java.
Проведя некоторое исследование, я понял, в чем дело: Perl должен использовать разные символы для обратных ссылок шаблона и обратных ссылок замены, и хотя java.util.regex.*
не должен следовать этому примеру, он выбирает его, но не по технической, а скорее по традиционной причине.
(Пожалуйста, имейте в виду, что все, что я знаю о Perl на данный момент, получено из чтения статей Википедии, поэтому не стесняйтесь исправлять любые ошибки, которые я мог сделать)
Причина, по которой это должно быть сделано таким образом в Perl, следующая:
$
как сигил (т.е. символ, прикрепленный к имени переменной). $1
, $2
и т.д. Таким образом, из-за того, как интерпретируется Perl и как работает его механизм regex, предшествующая косая черта для обратных ссылок (например, \1
) в шаблоне должна быть использована, потому что если вместо нее использовать сигил $
(например, $1
), это приведет к непреднамеренной интерполяции переменных в шаблон.
Строка замены, в силу того, как она работает в Perl, оценивается в контексте каждого совпадения. Для Perl наиболее естественно использовать здесь интерполяцию переменных, поэтому механизм regex записывает группы в переменные $1
, $2
и т.д., чтобы это работало без проблем с остальным языком.
Java - это совсем другой язык, чем Perl, но самое важное здесь то, что здесь нет интерполяции переменных. Более того, replaceAll
- это вызов метода, а как и во всех вызовах методов в Java, аргументы оцениваются один раз, до вызова метода.
Таким образом, функция интерполяции переменных сама по себе недостаточна, поскольку, по сути, строка замены должна заново оцениваться при каждом совпадении, а это просто не соответствует семантике вызовов методов в Java. Интерполированная переменной строка замены, которая оценивается до вызова replaceAll
, практически бесполезна; интерполяция должна происходить во время метода, при каждом совпадении.
Поскольку это не является семантикой языка Java, replaceAll
должен выполнять эту интерполяцию "точно в срок" вручную. Таким образом, нет абсолютно никакой технической причины, почему $
является управляющим символом для обратных ссылок в строках замены. Это вполне мог быть \
. И наоборот, обратные ссылки в шаблоне можно было бы экранировать с помощью $
вместо \
, и технически это работало бы так же хорошо.
Причина, по которой Java делает regex так, как она это делает, чисто традиционная: она просто следует прецеденту, созданному Perl.