รายชื่ออักขระพิเศษทั้งหมดที่ต้องใช้ Escape ในนิพจน์ทั่วไป


110

ฉันกำลังพยายามสร้างแอปพลิเคชันที่ตรงกับเทมเพลตข้อความที่มีข้อความที่ผู้ใช้พยายามส่ง ฉันใช้ Java regex เพื่อจับคู่ข้อความ แม่แบบ / ข้อความอาจมีอักขระพิเศษ

ฉันจะได้รับรายการอักขระพิเศษทั้งหมดที่ต้องใช้ Escape เพื่อให้ regex ของฉันทำงานและจับคู่ในกรณีที่เป็นไปได้สูงสุดได้อย่างไร

มีวิธีแก้ปัญหาสากลสำหรับการหลีกเลี่ยงอักขระพิเศษทั้งหมดใน Java regex หรือไม่?

คำตอบ:


95

คุณสามารถดู javadoc ของคลาส Pattern: http://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html

คุณต้องหลีกเลี่ยงถ่านใด ๆ ที่ระบุไว้ในนั้นหากคุณต้องการถ่านปกติไม่ใช่ความหมายพิเศษ

ในฐานะที่เป็นวิธีแก้ปัญหาที่ง่ายกว่าคุณสามารถวางแม่แบบระหว่าง \ Q และ \ E - ทุกสิ่งที่อยู่ระหว่างนั้นจะถือว่าเป็นค่า Escape


43
หากคุณพบว่า \ Q และ \ E จำยากคุณสามารถใช้แทน Pattern.quote ("... ")
mkdev

19
ฉันหวังว่าคุณจะระบุไว้จริงๆ
Aleksandr Dubinsky

ทำไม @AleksandrDubinsky?
โซริน

55
@Sorin เนื่องจากเป็นเจตนารมณ์ของ Stack Exchange ในการระบุคำตอบในคำตอบของคุณแทนที่จะลิงก์ไปยังแหล่งข้อมูลนอกสถานที่ นอกจากนี้หน้านั้นยังไม่มีรายการที่ชัดเจนเช่นกัน ดูรายการได้ที่นี่: docs.oracle.com/javase/tutorial/essential/regex/literals.htmlแต่ระบุว่า "ในบางสถานการณ์อักขระพิเศษที่ระบุไว้ข้างต้นจะไม่ถือว่าเป็นอักขระเมตาคาแร็กเตอร์" โดยไม่ต้องอธิบายว่าจะเกิดอะไรขึ้น หากมีใครพยายามหลบหนี ในระยะสั้นคำถามนี้สมควรได้รับคำตอบที่ดี
Aleksandr Dubinsky

8
"ทุกสิ่งที่อยู่ระหว่างพวกเขา [ \Qและ\E] ถือเป็นส่วนหลีกเลี่ยง" - ยกเว้น\Qของและ\Eของอื่น ๆ(ซึ่งอาจเกิดขึ้นภายในนิพจน์ดั้งเดิม) ดังนั้นควรใช้Pattern.quoteตามที่แนะนำไว้ที่นี่จะดีกว่าไม่ใช่เพื่อสร้างล้อใหม่
Sasha

93
  • อักขระ Java ที่ต้องใช้ Escape ในนิพจน์ทั่วไป ได้แก่ :
    \.[]{}()<>*+-=!?^$|
  • ต้องใช้วงเล็บปิดสองอัน ( ]และ}) เท่านั้นหลังจากเปิดวงเล็บชนิดเดียวกัน
  • ใน[]วงเล็บอักขระบางตัว (เช่น+และ-) บางครั้งทำงานโดยไม่มีการหลบหนี

มีวิธีใดบ้างที่จะไม่หนี แต่ยอมให้ตัวละครเหล่านั้น
Dominika

1
การหลีกเลี่ยงอักขระหมายถึงการอนุญาตให้อักขระแทนที่จะตีความว่าเป็นตัวดำเนินการ
Tobi G.

4
Unescaped -within []อาจใช้งานไม่ได้เสมอไปเนื่องจากใช้เพื่อกำหนดช่วง ปลอดภัยกว่าที่จะหลบหนี ตัวอย่างเช่นรูปแบบ[-]และ[-)]ตรงกับสตริงแต่ไม่ได้มี- [(-)]
Kenston Choi

1
แม้ว่าคำตอบที่ได้รับการยอมรับจะตอบคำถามได้ แต่คำตอบนี้มีประโยชน์สำหรับฉันมากกว่าเมื่อฉันกำลังมองหารายการด่วน
Old Nick

-=!ไม่จำเป็นต้องหลีกหนี แต่ขึ้นอยู่กับบริบท ตัวอย่างเช่นเป็นตัวอักษรเดียวที่ใช้เป็นนิพจน์ทั่วไป
เหยี่ยว

29

ในการหลบหนีคุณสามารถใช้สิ่งนี้จากJava 1.5 :

Pattern.quote("$test");

คุณจะจับคู่คำทุกประการ $test


เหตุใดนี่จึงไม่ใช่คำตอบที่ได้รับคะแนนสูงสุด มันแก้ปัญหาได้โดยไม่ต้องลงรายละเอียดที่ซับซ้อนของการแสดงรายการอักขระทั้งหมดที่ต้องการหลบหนีและเป็นส่วนหนึ่งของ JDK - ไม่จำเป็นต้องเขียนโค้ดเพิ่มเติมใด ๆ ! เรียบง่าย!
Volksman

18

ตามหน้าเอกสารString Literals / Metacharactersได้แก่ :

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

มันจะดีมากที่มีการอ้างอิงรายการนั้นในรหัส แต่ฉันไม่รู้ว่าจะอยู่ที่ไหน ...


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

1
รูปแบบ javadoc กล่าวว่าเป็นข้อผิดพลาดในการใช้แบ็กสแลชก่อนอักขระตัวอักษรใด ๆ ที่ไม่ได้แสดงถึงโครงสร้างที่หลีกเลี่ยงแต่แบ็กสแลชอาจถูกใช้ก่อนอักขระที่ไม่ใช่ตัวอักษรโดยไม่คำนึงว่าอักขระนั้นเป็นส่วนหนึ่งของโครงสร้างที่ไม่ใช้ Escape หรือไม่ ดังนั้น regex ที่ง่ายกว่ามากจะเพียงพอ: s.replaceAll("[\\W]", "\\\\$0")โดยที่\Wกำหนดอักขระที่ไม่ใช่คำ
Joe Bowbeer

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

5

ตามคำแนะนำของ @ Sorin เกี่ยวกับเอกสารรูปแบบ Java ดูเหมือนว่าอักขระที่จะหลบหนีเป็นอย่างน้อย:

\.[{(*+?^$|

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

2
)ยังต้องมีการหลีกเลี่ยงและขึ้นอยู่กับว่าคุณอยู่ในหรือนอกคลาสอักขระอาจมีอักขระที่จะหลบหนีได้มากขึ้นซึ่งในกรณีนี้Pattern.quoteจะค่อนข้างดีในการหลีกเลี่ยงสตริงเพื่อใช้ทั้งในและนอกคลาสอักขระ
nhahtdh

3

Pattern.quote(String s)เรียงลำดับของการทำสิ่งที่คุณต้องการ อย่างไรก็ตามมันเหลือเพียงเล็กน้อยที่ต้องการ มันไม่จริงหนีตัวบุคคลเพียง wraps \Q...\Eสตริงกับ

ไม่มีวิธีการที่ตอบสนองสิ่งที่คุณกำลังมองหา แต่ข่าวดีก็คือมันค่อนข้างง่ายที่จะหลีกเลี่ยงอักขระพิเศษทั้งหมดในนิพจน์ทั่วไปของ Java:

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

ทำไมถึงได้ผล? เอกสารสำหรับPatternระบุเฉพาะว่าอนุญาตให้หลีกเลี่ยงอักขระที่ไม่ใช่ตัวอักษรที่ไม่จำเป็นต้องหลีกเลี่ยง:

เป็นข้อผิดพลาดในการใช้แบ็กสแลชก่อนอักขระตามตัวอักษรใด ๆ ที่ไม่ได้แสดงถึงโครงสร้างที่หลบหนี สิ่งเหล่านี้สงวนไว้สำหรับส่วนขยายในอนาคตของภาษานิพจน์ทั่วไป อาจใช้แบ็กสแลชก่อนอักขระที่ไม่ใช่ตัวอักษรไม่ว่าอักขระนั้นจะเป็นส่วนหนึ่งของโครงสร้างที่ไม่ใช้ค่า Escape หรือไม่

ตัวอย่างเช่น;ไม่ใช่อักขระพิเศษในนิพจน์ทั่วไป แต่ถ้าคุณหนีมันPatternจะยังคงตีความเป็น\; ;นี่คือตัวอย่างเพิ่มเติมบางส่วน:

  • >กลายเป็น\>ซึ่งเทียบเท่ากับ>
  • [กลาย\[เป็นรูปแบบที่หลบหนีของ[
  • 8ยัง8อยู่
  • \)กลาย\\\)เป็นรูปแบบที่หลีกหนี\และ(ต่อกัน

หมายเหตุ:ที่สำคัญคือเป็นความหมายของ "ไม่ใช่ตัวอักษร" ซึ่งในเอกสารจริงๆหมายความว่า "ไม่ใช่คำว่า " [a-zA-Z_0-9]ตัวละครหรือตัวละครนอกชุดอักขระ


3

แม้ว่าคำตอบจะเป็น 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


2

ในอีกด้านหนึ่งของเหรียญคุณควรใช้ regex "non-char" ที่มีลักษณะเช่นนี้หากอักขระพิเศษ = allChars - number - ABC - ช่องว่างในบริบทแอปของคุณ

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

1

สมมติว่าคุณมีและไว้วางใจ (เป็นเผด็จการ) รายการอักขระหลีกที่ Java regex ใช้ (จะดีถ้าอักขระเหล่านี้ถูกเปิดเผยในสมาชิกคลาส 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);
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.