Список усіх спеціальних символів, які потрібно уникнути в регулярному виразі


108

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

Як я можу отримати повний перелік спеціальних символів, які потрібно уникнути, щоб мій регекс працював і відповідав максимально можливим випадкам?

Чи є універсальне рішення для втечі від усіх спеціальних символів у Java regex?

Відповіді:


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 Тому що це - дух (най, політика?) Біржі стеків, щоб відповісти у своїй відповіді, а не просто посилатися на ресурс поза сайтом. Крім того, на цій сторінці також немає чіткого списку. Зі списком можна ознайомитись тут: docs.oracle.com/javase/tutorial/essential/regex/literals.html , але в ньому зазначено "У певних ситуаціях спеціальні символи, перелічені вище, не розглядаються як метахарактеристики", не пояснюючи, що відбудеться якщо хтось намагається втекти від них. Словом, це запитання заслуговує гарної відповіді.
Олександр Дубінський,

8
"все між ними [ \Qі \E] вважається втеченим" - за винятком інших \Q\E" (що потенційно може статися в оригінальному регулярному вираженні). Отже, краще використовувати, Pattern.quoteяк тут запропоновано, а не винаходити колесо.
Сашко

92
  • Символи Java, які потрібно уникнути у регулярних виразах:
    \.[]{}()<>*+-=!?^$|
  • Дві із закриваючих дужок ( ]і }) потрібно виключити лише після відкриття однотипного кронштейна.
  • У []дужках деякі символи (як +і -) іноді працюють без уникнення.

Чи є спосіб уникнути, але дозволити цим персонажам?
Домініка

1
Уникнути символу означає дозволити символу замість інтерпретувати його як оператора.
Тобі Г.

4
Немальований -в межах []може не завжди працювати, оскільки він використовується для визначення діапазонів. Безпечніше уникати цього. Наприклад, шаблони [-]і [-)]збігаються з рядком, -але не з [(-)].
Кенстон Чой

1
Незважаючи на те, що прийнята відповідь відповідає на питання, ця відповідь мені була кориснішою, коли я просто шукав швидкий список.
Старий Нік

-=!не обов’язково уникати, це залежить від контексту. Наприклад, в якості однієї літери вони працюють як постійний регулярний вираз.
Хоук

29

Щоб уникнути, ви можете просто скористатися цим з Java 1.5 :

Pattern.quote("$test");

Ви точно збіжетеся зі словом $test


Чому це не найвища відповідь? Він вирішує проблему, не вдаючись до складних деталей перерахування всіх символів, які потребують втечі, і це частина JDK - не потрібно писати зайвого коду! Просто!
Volksman

17

Згідно з документацією на сторінку String Literals / Metacharacters , це:

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

Крім того, було б круто, щоб цей список було проведено десь у коді, але я не знаю, де це могло бути ...


11
String escaped = tnk.replaceAll("[\\<\\(\\[\\{\\\\\\^\\-\\=\\$\\!\\|\\]\\}\\)\\?\\*\\+\\.\\>]", "\\\\$0");
marbel82

1
Шаблон javadoc стверджує, що помилка використання зворотної косої риски перед будь-яким алфавітним символом, який не позначає конструкцію, що уникнув, але зворотний косий рядок може бути використаний перед символом, що не буває алфавітом, незалежно від того, чи є цей символ не конструюваною конструкцією. Тому достатньо простішого регулярного вираження: s.replaceAll("[\\W]", "\\\\$0")де \Wпозначаються несловові символи.
Джо Боубір

6

Поєднуючи те, що всі сказали, пропоную наступне, щоб список символів, спеціальних для RegExp, був чітко переліченим у їх власній строці, а також уникнути необхідності візуального розбору тисяч "\\" 's. Здається, це працює досить добре для мене:

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");
}

5

За пропозицією @ Sorin щодо документів Java Pattern, схоже, що символи для втечі є щонайменше:

\.[{(*+?^$|

4
String escaped = regexString.replaceAll("([\\\\\\.\\[\\{\\(\\*\\+\\?\\^\\$\\|])", "\\\\$1");
fracz

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

з іншого боку монети, ви повинні використовувати «нехарактерний» регулярний вираз, який виглядає приблизно так, якщо в контексті програми додаються спеціальні символи = allChars - число - ABC.

String regepx = "[^\\s\\w]*";

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


1

Якщо припустити, що ви маєте довіру (щоб бути авторитетним) переліку символів втечі, який використовує 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);
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.