Danh sách tất cả các ký tự đặc biệt cần được thoát trong regex


108

Tôi đang cố gắng tạo một ứng dụng phù hợp với mẫu tin nhắn với tin nhắn mà người dùng đang cố gắng gửi. Tôi đang sử dụng Java regex để khớp thông báo. Mẫu / tin nhắn có thể chứa các ký tự đặc biệt.

Làm cách nào để tôi có được danh sách đầy đủ các ký tự đặc biệt cần phải thoát để regex của tôi hoạt động và khớp trong các trường hợp tối đa có thể?

Có giải pháp chung nào để thoát tất cả các ký tự đặc biệt trong Java regex không?

Câu trả lời:


94

Bạn có thể xem javadoc của lớp Pattern: http://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html

Bạn cần phải thoát khỏi bất kỳ ký tự nào được liệt kê ở đó nếu bạn muốn ký tự thông thường và không có ý nghĩa đặc biệt.

Như một giải pháp có thể đơn giản hơn, bạn có thể đặt mẫu giữa \ Q và \ E - mọi thứ giữa chúng được coi là thoát.


43
Nếu bạn tìm thấy \ Q và \ E khó nhớ, bạn có thể sử dụng thay vì Pattern.quote ( "...")
mkdev

19
Tôi muốn anh thực sự tuyên bố họ
Aleksandr Dubinsky

Tại sao, @AleksandrDubinsky?
Sorin

55
@Sorin Bởi vì tinh thần (nay, chính sách?) Của Stack Exchange là nêu câu trả lời trong câu trả lời của bạn thay vì chỉ liên kết đến một tài nguyên ngoài trang web. Ngoài ra, trang đó cũng không có danh sách rõ ràng. Bạn có thể tìm thấy danh sách tại đây: docs.oracle.com/javase/tutorial/essential/regex/literals.html , nhưng nó nói rằng "Trong một số trường hợp nhất định, các ký tự đặc biệt được liệt kê ở trên sẽ không được coi là siêu ký tự" mà không giải thích điều gì sẽ xảy ra nếu một người cố gắng thoát khỏi chúng. Tóm lại, câu hỏi này xứng đáng có một câu trả lời tốt.
Aleksandr Dubinsky

8
"mọi thứ giữa chúng [ \Q\E] được coi là đã thoát" - ngoại trừ các \Qcủa và \E'khác (có thể xảy ra trong regex ban đầu). Vì vậy, tốt hơn là sử dụng Pattern.quotetheo đề xuất ở đây và không phải phát minh lại bánh xe.
Sasha

92
  • Các ký tự Java phải được thoát trong biểu thức chính quy là:
    \.[]{}()<>*+-=!?^$|
  • Hai trong số các dấu ngoặc đóng ( ]}) chỉ cần được thoát sau khi mở cùng một loại dấu ngoặc.
  • Trong []-brackets một số ký tự (như +-) đôi khi hoạt động mà không có lối thoát.

Có cách nào để không thoát mà cho phép những ký tự đó không?
Dominika

1
Thoát một ký tự có nghĩa là cho phép ký tự thay vì diễn giải nó như một toán tử.
Tobi G.

4
Unescaped -trong vòng []có thể không luôn luôn làm việc kể từ khi nó được sử dụng để xác định phạm vi. Sẽ an toàn hơn nếu thoát khỏi nó. Ví dụ, các mẫu [-][-)]khớp với chuỗi -nhưng không khớp với [(-)].
Kenston Choi

1
Mặc dù câu trả lời được chấp nhận không trả lời câu hỏi, câu trả lời này hữu ích hơn cho tôi khi tôi chỉ đang tìm kiếm một danh sách nhanh.
Nick cũ

-=!không nhất thiết phải được thoát ra, nó phụ thuộc vào ngữ cảnh. Ví dụ như một chữ cái duy nhất, chúng hoạt động như một regex không đổi.
Hawk

29

Để thoát, bạn chỉ cần sử dụng điều này từ Java 1.5 :

Pattern.quote("$test");

Bạn sẽ khớp chính xác từ $test


Tại sao đây không phải là câu trả lời được đánh giá cao nhất? Nó giải quyết vấn đề mà không cần đi sâu vào các chi tiết phức tạp của việc liệt kê tất cả các ký tự cần thoát và nó là một phần của JDK - không cần viết thêm bất kỳ mã nào! Đơn giản!
Volksman

17

Theo trang tài liệu String Literals / Metacharacters , chúng là:

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

Ngoài ra, sẽ rất tuyệt nếu danh sách đó được tham chiếu ở đâu đó trong mã, nhưng tôi không biết đó có thể là ...


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

1
Mẫu javadoc cho biết thật là lỗi khi sử dụng dấu gạch chéo ngược trước bất kỳ ký tự chữ cái nào không biểu thị cấu trúc thoát, nhưng dấu gạch chéo ngược có thể được sử dụng trước ký tự không phải chữ cái bất kể ký tự đó có phải là một phần của cấu trúc không thoát. Do đó, một regex đơn giản hơn nhiều sẽ đủ: s.replaceAll("[\\W]", "\\\\$0")nơi \Wchỉ định các ký tự không phải từ.
Joe Bowbeer

6

Kết hợp những gì mọi người đã nói, tôi đề xuất như sau, để giữ cho danh sách các ký tự đặc biệt đối với RegExp được liệt kê rõ ràng trong Chuỗi của riêng họ và để tránh phải cố gắng phân tích cú pháp trực quan hàng nghìn "\\". Điều này dường như hoạt động khá tốt đối với tôi:

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

Theo đề xuất của @ Sorin về tài liệu Java Pattern, có vẻ như các ký tự cần thoát ít nhất là:

\.[{(*+?^$|

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

2
)cũng phải được thoát, và tùy thuộc vào việc bạn ở trong hay ngoài lớp ký tự, có thể có nhiều ký tự hơn để thoát, trong trường hợp đó, Pattern.quotethoát một chuỗi khá tốt để sử dụng cả bên trong và bên ngoài lớp ký tự.
nhahtdh

3

Các Pattern.quote(String s)loại làm những gì bạn muốn. Tuy nhiên, nó để lại một chút mong muốn; nó không thực sự thoát khỏi các ký tự riêng lẻ, chỉ quấn chuỗi bằng\Q...\E .

Không có một phương pháp nào thực hiện chính xác những gì bạn đang tìm kiếm, nhưng tin tốt là nó thực sự khá đơn giản để thoát tất cả các ký tự đặc biệt trong một biểu thức chính quy Java:

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

Tại sao điều này hoạt động? Vâng, tài liệu cho Patternbiết cụ thể rằng nó được phép thoát các ký tự không phải chữ cái mà không nhất thiết phải thoát:

Đó là một lỗi khi sử dụng dấu gạch chéo ngược trước bất kỳ ký tự chữ cái nào không biểu thị một cấu trúc thoát; chúng được dành riêng cho các phần mở rộng trong tương lai cho ngôn ngữ biểu thức chính quy. Dấu gạch chéo ngược có thể được sử dụng trước một ký tự không phải bảng chữ cái bất kể ký tự đó có phải là một phần của cấu trúc không thoát.

Ví dụ, ;không phải là một ký tự đặc biệt trong một biểu thức chính quy. Tuy nhiên, nếu bạn thoát khỏi nó, Patternvẫn sẽ diễn giải\;;. Dưới đây là một số ví dụ khác:

  • >trở nên \>tương đương với>
  • [ trở thành \[ mà là dạng thoát của[
  • 8 vẫn còn 8 .
  • \)trở thành \\\)mà là các dạng thoát của \( nối.

Lưu ý: Điều quan trọng là định nghĩa của "non-alphabetic", trong tài liệu này thực sự có nghĩa là các ký tự "không phải từ ", hoặc các ký tự nằm ngoài bộ ký tự [a-zA-Z_0-9].


2

ở phía bên kia của đồng tiền, bạn nên sử dụng regex "non-char" trông giống như thế này nếu các ký tự đặc biệt = allChars - number - ABC - space trong ngữ cảnh ứng dụng của bạn.

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

2

mặc dù câu trả lời là dành cho Java, nhưng mã có thể dễ dàng điều chỉnh từ phần mở rộng Chuỗi Kotlin mà tôi đã nghĩ ra (được điều chỉnh từ @brcolow đó được cung cấp):

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())
}

bản in \(\.\*\)

kiểm tra hoạt động tại đây https://pl.kotl.in/h-3mXZkNE


1

Giả sử rằng bạn có và tin tưởng (có thẩm quyền) danh sách các ký tự thoát mà Java regex sử dụng (sẽ rất tốt nếu các ký tự này được hiển thị trong một số thành viên lớp Mẫu), bạn có thể sử dụng phương pháp sau để thoát ký tự nếu nó thực sự cần thiết:

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

private static String regexEscape(char character) {
    for (char escapeChar : escapeChars) {
        if (character == escapeChar) {
            return "\\" + character;
        }
    }
    return String.valueOf(character);
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.