Список всех специальных символов, которые нужно экранировать в регулярном выражении

108

Я пытаюсь создать приложение, которое сопоставляет шаблон сообщения с сообщением, которое пытается отправить пользователь. Я использую регулярное выражение Java для сопоставления сообщения. Шаблон / сообщение могут содержать специальные символы.

Как мне получить полный список специальных символов, которые нужно экранировать, чтобы мое регулярное выражение работало и совпадало в максимально возможных случаях?

Есть ли универсальное решение для экранирования всех специальных символов в регулярном выражении Java?

Авинаш Наир
источник

Ответы:

94

Вы можете посмотреть javadoc класса Pattern: http://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html

Вам нужно экранировать любой перечисленный там символ, если вам нужен обычный символ, а не особое значение.

Как, возможно, более простое решение, вы можете поместить шаблон между \ Q и \ E - все между ними считается экранированным.

Сорин
источник
43
Если вам трудно запомнить \ Q и \ E, вы можете использовать вместо него Pattern.quote ("...")
mkdev
19
Я бы хотел, чтобы вы их на самом деле заявили
Александр Дубинский
Почему, @AleksandrDubinsky?
Сорин
55
@Sorin Потому что дух (нет, политика?) Stack Exchange - указывать ответ в вашем ответе, а не просто ссылаться на сторонний ресурс. Кроме того, на этой странице также нет четкого списка. Список можно найти здесь: docs.oracle.com/javase/tutorial/essential/regex/literals.html , но в нем говорится: «В определенных ситуациях перечисленные выше специальные символы не будут рассматриваться как метасимволы», без объяснения того, что произойдет. если кто-то пытается убежать от них. Короче говоря, этот вопрос заслуживает хорошего ответа.
Александр Дубинский
8
«все, что находится между ними [ \Qи \E], считается экранированным» - кроме других \Qи \E(которые потенциально могут встречаться в исходном регулярном выражении). Так что лучше использовать то, Pattern.quoteчто предлагается здесь, а не изобретать велосипед.
Саша
92
  • В регулярных выражениях необходимо использовать следующие символы Java:
    \.[]{}()<>*+-=!?^$|
  • Две закрывающие скобки ( ]и }) нужно экранировать только после открытия скобок того же типа.
  • В []скобках некоторые символы (например, +и -) иногда работают без выхода.
Тоби Г.
источник
Есть ли способ не убежать, а позволить этим персонажам?
Доминика
1
Экранирование символа означает разрешение символа вместо его интерпретации как оператора.
Тоби Г.
4
Неэкранирование -внутри []может не всегда работать, поскольку оно используется для определения диапазонов. От него безопаснее сбежать. Например, шаблоны [-]и [-)]совпадают со строкой, -но не с [(-)].
Kenston Choi
1
Несмотря на то, что принятый ответ действительно отвечает на вопрос, этот ответ был для меня более полезным, когда я просто искал быстрый список.
Старый Ник
-=!не обязательно экранировать, это зависит от контекста. Например, как отдельная буква, они работают как постоянное регулярное выражение.
Ястреб,
29

Чтобы сбежать, вы можете просто использовать это из Java 1.5 :

Pattern.quote("$test");

Вы точно подберете слово $test

Madx
источник
Почему это не самый высоко оцененный ответ? Он решает проблему, не вдаваясь в сложные детали перечисления всех символов, которые нужно экранировать, и это часть JDK - нет необходимости писать дополнительный код! Просто!
Volksman
17

Согласно странице документации String Literals / Metacharacters , они:

<([{\^-=$!|]})?*+.>

Также было бы круто, если бы этот список где-то в коде ссылался, но я не знаю, где это может быть ...

Богдан
источник
11
String escaped = tnk.replaceAll("[\\<\\(\\[\\{\\\\\\^\\-\\=\\$\\!\\|\\]\\}\\)\\?\\*\\+\\.\\>]", "\\\\$0");
marbel82
1
В шаблоне javadoc говорится, что использование обратной косой черты перед любым буквенным символом, не обозначающим экранированную конструкцию, является ошибкой, но обратная косая черта может использоваться перед неалфавитным символом независимо от того, является ли этот символ частью неэкранированной конструкции. Поэтому будет достаточно более простого регулярного выражения: s.replaceAll("[\\W]", "\\\\$0")где \Wобозначает символы, не являющиеся словами.
Джо
6

Объединив то, что все сказали, я предлагаю следующее: чтобы список символов, специальных для RegExp, был четко указан в их собственной строке, и избежать необходимости пытаться визуально анализировать тысячи "\\". Мне кажется, это очень хорошо работает:

final String regExSpecialChars = "<([{\\^-=$!|]})?*+.>";
final String regExSpecialCharsRE = regExSpecialChars.replaceAll( ".", "\\\\$0");
final Pattern reCharsREP = Pattern.compile( "[" + regExSpecialCharsRE + "]");

String quoteRegExSpecialChars( String s)
{
    Matcher m = reCharsREP.matcher( s);
    return m.replaceAll( "\\\\$0");
}
NeuroDuck
источник
5

По предложению @Sorin в документах Java Pattern, похоже, что символы, которые нужно избежать, по крайней мере:

\.[{(*+?^$|
Пит
источник
4
String escaped = regexString.replaceAll("([\\\\\\.\\[\\{\\(\\*\\+\\?\\^\\$\\|])", "\\\\$1");
fracz 01
2
)также должно быть экранировано, и в зависимости от того, находитесь ли вы внутри или вне класса символов, может быть больше символов, которые нужно экранировать, и в этом случае Pattern.quoteнеплохо справляется с экранированием строки для использования как внутри, так и вне класса символов.
nhahtdh
3

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

Не существует метода, который бы делал именно то, что вы ищете, но хорошая новость заключается в том, что на самом деле довольно просто избежать всех специальных символов в регулярном выражении Java:

regex.replaceAll("[\\W]", "\\\\$0")

Почему это работает? Что ж, документация дляPattern специально сказано, что разрешено экранировать неалфавитные символы, которые не обязательно экранировать:

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

Например, ;это не специальный символ в регулярном выражении. Однако, если вы его избежите, Patternвсе равно будет интерпретироваться \;как; . Вот еще несколько примеров:

  • > становится \> что эквивалентно>
  • [ становится \[ в ускользнувшую форму[
  • 8 все еще 8 .
  • \)превращается \\\)в ускользнувшие формы \и (сцепленные.

Примечание: Ключ является определением «неалфавитным», что в документации на самом деле означает «не- слово » символы или символы вне набора символов [a-zA-Z_0-9].

Уиллер
источник
2

с другой стороны, вы должны использовать регулярное выражение «non-char», которое выглядит так, если специальные символы = allChars - number - ABC - space в контексте вашего приложения.

String regepx = "[^\\s\\w]*";
Bo6Медведь
источник
2

хотя ответ для Java, но код можно легко адаптировать из этого расширения Kotlin String, которое я придумал (адаптировано из предоставленного @brcolow):

private val escapeChars = charArrayOf(
    '<',
    '(',
    '[',
    '{',
    '\\',
    '^',
    '-',
    '=',
    '$',
    '!',
    '|',
    ']',
    '}',
    ')',
    '?',
    '*',
    '+',
    '.',
    '>'
)

fun String.escapePattern(): String {
    return this.fold("") {
      acc, chr ->
        acc + if (escapeChars.contains(chr)) "\\$chr" else "$chr"
    }
}

fun main() {
    println("(.*)".escapePattern())
}

печатает \(\.\*\)

проверьте это в действии здесь https://pl.kotl.in/h-3mXZkNE

Pocesar
источник
1

Предполагая, что у вас есть и доверяете (чтобы быть авторитетным) список escape-символов, которые использует регулярное выражение Java (было бы неплохо, если бы эти символы были представлены в каком-либо члене класса Pattern), вы можете использовать следующий метод для экранирования символа, если это действительно необходимо:

private static final char[] escapeChars = { '<', '(', '[', '{', '\\', '^', '-', '=', '$', '!', '|', ']', '}', ')', '?', '*', '+', '.', '>' };

private static String regexEscape(char character) {
    for (char escapeChar : escapeChars) {
        if (character == escapeChar) {
            return "\\" + character;
        }
    }
    return String.valueOf(character);
}
Brcolow
источник