Как экранировать текст для регулярного выражения в Java

251

Есть ли у Java встроенный способ избежать произвольного текста, чтобы он мог быть включен в регулярное выражение? Например, если мои пользователи вводят "5 долларов США", я бы хотел совместить это, а не "5" после окончания ввода.

Теги:
escaping

7 ответов

369
Лучший ответ

Так как Java 1.5, да:

Pattern.quote("$5");
  • 75
    Обратите внимание, что это не экранирует саму строку, а переносит ее с помощью \Q и \E Это может привести к неожиданным результатам, например, Pattern.quote("*.wav").replaceAll("*",".*") к \Q.*.wav\E а не .*\.wav , как и следовало ожидать.
  • 11
    @Paramaeleon Почему вы ожидаете, что foo (x) .bar () == x.bar ()?
Показать ещё 8 комментариев
94

Разница между Pattern.quote и Matcher.quoteReplacement мне не было ясно, прежде чем я увидел следующий пример

s.replaceFirst(Pattern.quote("text to replace"), 
               Matcher.quoteReplacement("replacement text"));
  • 29
    В частности, Pattern.quote заменяет специальные символы в строках поиска регулярных выражений, например. | + () И т. Д., А Matcher.quoteReplacement заменяет специальные символы в строках замены, например \ 1 для обратных ссылок.
  • 8
    Я не согласна Pattern.quote переносит свой аргумент с помощью \ Q и \ E. Это не ускользает от специальных символов.
Показать ещё 3 комментария
18

Возможно, слишком поздно ответить, но вы также можете использовать Pattern.LITERAL, который будет игнорировать все специальные символы при форматировании:

Pattern.compile(textToFormat, Pattern.LITERAL);
  • 0
    Это особенно приятно, потому что вы можете комбинировать его с Pattern.CASE_INSENSITIVE
13

Я думаю, что вам нужно \Q$5\E. Также см. Pattern.quote(s), введенный в Java5.

Подробнее см. Pattern javadoc.

  • 0
    Мне интересно, есть ли какая-либо разница между этим и использованием флага LITERAL, поскольку javadoc говорит, что нет встроенного флага для включения и выключения LITERAL : java.sun.com/j2se/1.5.0/docs/api/java/ Util / регулярное выражение / ...
  • 15
    Обратите внимание, что буквально использовать \ Q и \ E хорошо, только если вы знаете свой ввод. Pattern.quote (s) также будет обрабатывать случай, когда ваш текст на самом деле содержит эти последовательности.
10

Прежде всего, если

  • вы используете replaceAll()
  • НЕ используйте Matcher.quoteReplacement()
  • текст, подлежащий замене, включает в себя $1

он не поместит 1 в конец. Он будет искать регулярное выражение поиска для первой сопоставимой группы и суб THAT. Что означает $1, $2 или $3 в заменяющем тексте: сопоставление групп с шаблоном поиска.

Я часто подключаю длинные строки текста к файлам .properties, а затем создаю объекты электронной почты и тела из них. В самом деле, это, по-видимому, является способом по умолчанию для i18n в Spring Framework. Я помещал теги XML в качестве заполнителей в строки, и я использую replaceAll() для замены тегов XML значениями во время выполнения.

Я столкнулся с проблемой, когда пользователь вводил цифру доллара и центов со знаком доллара. replaceAll() захлебнулся, при этом в stracktrace отображается следующее:

java.lang.IndexOutOfBoundsException: No group 3
at java.util.regex.Matcher.start(Matcher.java:374)
at java.util.regex.Matcher.appendReplacement(Matcher.java:748)
at java.util.regex.Matcher.replaceAll(Matcher.java:823)
at java.lang.String.replaceAll(String.java:2201)

В этом случае пользователь ввел "$ 3" где-то на своем входе, а replaceAll() пошел в поисковое регулярное выражение для третьей сопоставимой группы, не нашел ее и не потерял.

Дано:

// "msg" is a string from a .properties file, containing "<userInput />" among other tags
// "userInput" is a String containing the user input

вместо

msg = msg.replaceAll("<userInput \\/>", userInput);

с

msg = msg.replaceAll("<userInput \\/>", Matcher.quoteReplacement(userInput));

решил проблему. Пользователь может вводить любые символы, включая знаки доллара, без проблем. Он вел себя так, как вы ожидали.

4

Чтобы иметь защищенный шаблон, вы можете заменить все символы на "\\\\", кроме цифр и букв. И после этого вы можете поместить в этот защищенный шаблон свои специальные символы, чтобы заставить этот шаблон работать не как глупый цитируемый текст, а действительно как паттен, но ваш собственный. Без специальных символов пользователя.

public class Test {
    public static void main(String[] args) {
        String str = "y z (111)";
        String p1 = "x x (111)";
        String p2 = ".* .* \\(111\\)";

        p1 = escapeRE(p1);

        p1 = p1.replace("x", ".*");

        System.out.println( p1 + "-->" + str.matches(p1) ); 
            //.*\ .*\ \(111\)-->true
        System.out.println( p2 + "-->" + str.matches(p2) ); 
            //.* .* \(111\)-->true
    }

    public static String escapeRE(String str) {
        //Pattern escaper = Pattern.compile("([^a-zA-z0-9])");
        //return escaper.matcher(str).replaceAll("\\\\$1");
        return str.replaceAll("([^a-zA-Z0-9])", "\\\\$1");
    }
}
  • 0
    Вам не нужно избегать пробелов. Таким образом, вы можете поменять свой шаблон на «([^ a-zA-z0-9])».
  • 5
    Маленькая опечатка, большие последствия: "([^ a-zA-z0-9])" также не совпадает (т.е. не сбежит) [, \,], ^, от которой вы наверняка хотите сбежать! Опечатка - это вторая буква «z», которая должна быть буквой «Z», иначе все от ASCII 65 до ASCII 122 включено
0

Pattern.quote( "blabla" ) прекрасно работает.

Образец Pattern.quote() работает красиво. Он заключает предложение с символами "\Q" и "\E", и если он убегает "\ Q" и "\ E". Однако, если вам нужно выполнить экстренное выполнение обычного выражения (или пользовательское экранирование), вы можете использовать этот код:

String someText = "Some/s/wText*/,**";
System.out.println(someText.replaceAll("[-\\[\\]{}()*+?.,\\\\\\\\^$|#\\\\s]", "\\\\$0"));

Этот метод возвращает: Some/\ s/wText */\, **

Код, например, и тесты:

String someText = "Some\\E/s/wText*/,**";
System.out.println("Pattern.quote: "+ Pattern.quote(someText));
System.out.println("Full escape: "+someText.replaceAll("[-\\[\\]{}()*+?.,\\\\\\\\^$|#\\\\s]", "\\\\$0"));

Ещё вопросы

Сообщество Overcoder
Наверх
Меню