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

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

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
336
0
222 803
8
Перейти к ответу Данный вопрос помечен как решенный

Ответы 8

Ответ принят как подходящий

Поскольку Java 1.5, да:

Pattern.quote("");

Обратите внимание, что это не экранирование самой строки, а оболочка с использованием \Q и \E. Это может привести к неожиданным результатам, например, Pattern.quote("*.wav").replaceAll("*",".*") приведет к \Q.*.wav\E, а не .*\.wav, как вы могли ожидать.

Matthias Ronge 16.01.2013 17:27

Я просто хочу указать, что этот способ экранирования применяется также к выражениям, которые вы вводите после. Это может быть удивительно. Если вы сделаете "mouse".toUpperCase().replaceAll("OUS","ic"), он вернет MicE. Вы не ожидали, что он вернет MICE, потому что вы не применили toUpperCase() к ic. В моем примере quote() также применяется на вставке .* посредством replaceAll(). Вы должны сделать что-то еще, возможно, .replaceAll("*","\E.*\Q") подойдет, но это нелогично.

Matthias Ronge 12.11.2013 18:53

@Parameleon: Лучшее решение соответствующей проблемы - использовать метод split-map-mkString. ".wav" .split ("\\."). map (Pattern.quote) .mkString ("."). r

Mikaël Mayer 20.12.2013 19:28

@Paramaeleon Приводит к ошибке для меня, так как первый аргумент в replaceAll является регулярным выражением.

Adam Jensen 20.08.2014 08:56

@Paramaleon Если бы это сработало путем добавления отдельных экранирований, ваш первоначальный пример все равно не сделал бы то, что вы хотели ... если бы он экранировал символы по отдельности, он превратил бы *.wav в шаблон регулярного выражения \*\.wav, а replaceAll превратил бы его в \.*\.wav, это означает, что он будет соответствовать файлам, имя которых состоит из произвольного количества точек, за которыми следует .wav. Скорее всего, вам понадобился бы replaceAll("\*", ".*"), если бы они выбрали более хрупкую реализацию, которая полагается на распознавание всех возможных активных символов регулярных выражений и их экранирование по отдельности ... Было бы это намного проще?

Theodore Murdock 13.06.2015 00:19

Или, скорее, в сочетании с комментарием Адама Дженсена, вам пришлось бы использовать replaceAll("\\\*", ".*")

Theodore Murdock 13.06.2015 00:24

@Paramaeleon: вариант использования - "*.wav".replaceAll(Pattern.quote("*"), ".*").

sferencik 19.10.2015 19:23

На самом деле ни один из ответов мне не помог, но потом я подумал: я попытался сделать возможным простой ввод данных пользователем, где * просто означает, например, .*. Пользователь, конечно, ничего не знает о регулярных выражениях, поэтому теоретически он может использовать строки регулярных выражений, такие как .Hello*you?. Невозможно использовать Pattern.quote(string), а затем заменить * на .* и наоборот, потому что это будет цитировать всю строку. Просто split, строка для *, затем заключите в кавычки каждую часть результирующего массива и снова соедините все вместе с .* между ними. Надеюсь, это помогло тому, кто искал то же самое :)

Panther 11.08.2017 01:10

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

Подробнее см. В документации Шаблон javadoc.

Мне любопытно, есть ли разница между этим и использованием флага LITERAL, поскольку javadoc говорит, что нет встроенного флага для включения и выключения LITERAL: java.sun.com/j2se/1.5.0/docs/api/java/util/regex/…

Chris Mazzola 07.08.2009 00:51

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

Jeremy Huiskamp 14.02.2011 05:03

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

s.replaceFirst(Pattern.quote("text to replace"), 
               Matcher.quoteReplacement("replacement text"));

В частности, Pattern.quote заменяет специальные символы в строках поиска регулярных выражений, например. | + () И т. Д., А Matcher.quoteReplacement заменяет специальные символы в строках замены, например \ 1 для обратных ссылок.

Steven 18.11.2011 22:12

Я не согласен. В Pattern.quote аргумент заключен в \ Q и \ E. Не экранирует специальные символы.

David Medinets 07.02.2015 02:28

Matcher.quoteReplacement ("4 $ &% $") производит "4 \ $ &% \ $". Он экранирует специальные символы.

David Medinets 07.02.2015 02:31

Другими словами: quoteReplacement заботится только о двух символах $ и \ , которые могут, например, использоваться в замещающих строках как обратные ссылки или . Поэтому его нельзя использовать для экранирования / кавычки регулярного выражения.

SebastianH 14.02.2016 13:13

Потрясающие. Вот пример, в котором мы хотим заменить $Group$ на T$UYO$HI. Символ $ особенный как в шаблоне, так и в замене: "$Group$ Members".replaceFirst(Pattern.quote("$Group$"), Matcher.quoteReplacement("T$UYO$HI"))

arun 02.03.2016 00:10

Во-первых, если

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

в конце не будет 1. Он будет смотреть на регулярное выражение поиска для первой совпадающей группы и вложенной ТО в. Это то, что $ 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's input

замена

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

с

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

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

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

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("\\");
        return str.replaceAll("([^a-zA-Z0-9])", "\\");
    }
}

Вам не нужно покидать пробелы. Таким образом, вы можете изменить свой шаблон на «([^ a-zA-z0-9])».

Erel Segal-Halevi 07.04.2013 12:21

Маленькая опечатка, серьезные последствия: «([^ a-zA-z0-9])» также не соответствует (т.е. не экранируется) [, \,], ^ которого вы, безусловно, хотите избежать! Опечатка - это вторая буква «z», которая должна быть буквой «Z», в противном случае включается все от ASCII 65 до ASCII 122.

Zefiro 29.05.2015 19:04

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

Pattern.compile(textToFormat, Pattern.LITERAL);

Это особенно приятно, потому что его можно комбинировать с Pattern.CASE_INSENSITIVE.

mjjaniec 04.08.2017 10:04

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

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

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

Этот метод возвращает: Некоторые / \ 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"));

+1 Это очень хорошо работает для преобразования указанной пользователем строки нестандартных символов в шаблон, совместимый с регулярными выражениями. Я использую его для обеспечения соблюдения этих символов в пароле. Спасибо.

JackLeEmmerdeur 17.11.2020 22:07

Символ ^ (отрицание) используется для сопоставления того, что не входит в группу символов.

Это ссылка на Обычные выражения

Вот информация об отрицании:

Info about negation

Я вообще не понимаю, как это решает вопрос.

VLAZ 10.11.2020 12:16

Другие вопросы по теме