วิธียกเว้นข้อความสำหรับนิพจน์ทั่วไปใน Java


320

Java มีวิธีในตัวเพื่อหลีกเลี่ยงข้อความที่กำหนดเองเพื่อให้สามารถรวมไว้ในนิพจน์ทั่วไปได้หรือไม่? ตัวอย่างเช่นหากผู้ใช้ของฉันป้อน "$ 5" ฉันต้องการจับคู่นั้นแทนที่จะเป็น "5" หลังจากสิ้นสุดการป้อนข้อมูล

คำตอบ:


450

ตั้งแต่Java 1.5 ใช่ :

Pattern.quote("$5");

88
โปรดทราบว่านี้ไม่ได้หลบหนีสตริงตัวเอง แต่ wraps โดยใช้และ\Q \Eสิ่งนี้อาจนำไปสู่ผลลัพธ์ที่ไม่คาดคิดเช่นPattern.quote("*.wav").replaceAll("*",".*")จะส่งผล\Q.*.wav\Eและไม่.*\.wavเป็นไปตามที่คุณคาดหวัง
Matthias Ronge

11
@Paramaeleon ทำไมคุณถึงคิดว่า foo (x) .bar () == x.bar ()
Michael

7
@Paramaeleon ฉันคิดว่าคุณเข้าใจผิดเกี่ยวกับกรณีการใช้งาน
vikingsteve

18
ฉันเพียงแค่ wantet ชี้ให้เห็นว่าวิธีการนี้จะใช้หลบหนีหลบหนีนอกจากนี้ยังมีการแสดงออกที่คุณแนะนำหลังจากนั้น นี่อาจจะแปลกใจ ถ้าคุณทำมันจะกลับมา"mouse".toUpperCase().replaceAll("OUS","ic") MicEคุณ would't คาดหวังว่ามันจะกลับมาMICEเพราะคุณไม่ได้นำไปใช้ในtoUpperCase() icในตัวอย่างของฉันquote()ถูกนำไปใช้กับตัว.*แทรกด้วยreplaceAll()เช่นกัน คุณต้องทำอย่างอื่นบางทีอาจใช้.replaceAll("*","\\E.*\\Q")งานได้
Matthias Ronge

2
@Paramaleon ถ้ามันทำงานได้โดยการเพิ่มการหลบหนีส่วนบุคคลตัวอย่างเริ่มต้นของคุณยังคงไม่ทำในสิ่งที่คุณต้องการ ... ถ้ามันหลบหนีตัวละครทีละคนก็จะกลาย*.wavเป็นรูปแบบ regex \*\.wavและ replaceAll จะกลายเป็น\.*\.wavหมายความว่ามันจะ .wavไฟล์การแข่งขันที่มีชื่อประกอบด้วยจำนวนโดยพลการของงวดตามมาด้วย คุณน่าจะต้องการreplaceAll("\\*", ".*")ถ้าพวกเขาไปด้วยการใช้งานที่เปราะบางมากขึ้นซึ่งขึ้นอยู่กับการจดจำ charachters ที่ใช้งานได้ทั้งหมดที่เป็นไปได้และหนีพวกเขาทีละคน ... นั่นจะง่ายกว่านี้ไหม?
Theodore Murdock

112

ความแตกต่างระหว่างPattern.quoteและMatcher.quoteReplacementไม่ชัดเจนกับฉันก่อนที่ฉันจะเห็นตัวอย่างต่อไปนี้

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

29
โดยเฉพาะPattern.quoteแทนที่อักขระพิเศษในสตริงการค้นหาของ regex เช่น | + () ฯลฯ และMatcher.quoteReplacementแทนที่อักขระพิเศษในสตริงการแทนที่เช่น \ 1 สำหรับการอ้างอิงย้อนกลับ
สตีเวน

9
ฉันไม่เห็นด้วย Pattern.quote ล้อมอาร์กิวเมนต์ด้วย \ Q และ \ E มันไม่หนีตัวละครพิเศษ
David Medinets

5
Matcher.quoteReplacement ("4 $ &% $") สร้าง "4 \ $ &% \ $" มันหนีออกมาจากตัวละครพิเศษ
David Medinets

4
ในคำอื่น ๆ : quoteReplacementใส่ใจเพียงประมาณสองสัญลักษณ์$และ\ ที่สามารถยกตัวอย่างเช่นจะใช้ในสตริงทดแทน backreferences หรือ$1 \1ดังนั้นจึงต้องไม่ใช้เพื่อหลบหนี / อ้างถึง regex
SebastianH

1
น่ากลัว นี่คือตัวอย่างที่เราต้องการที่จะแทนที่ด้วย$Group$ สัญลักษณ์เป็นพิเศษทั้งในรูปแบบและในการเปลี่ยน:T$UYO$HI$"$Group$ Members".replaceFirst(Pattern.quote("$Group$"), Matcher.quoteReplacement("T$UYO$HI"))
อรุณ

29

อาจตอบสนองช้าเกินไป แต่คุณสามารถใช้Pattern.LITERALซึ่งจะละเว้นอักขระพิเศษทั้งหมดในขณะที่จัดรูปแบบ:

Pattern.compile(textToFormat, Pattern.LITERAL);

เป็นเรื่องที่ดีโดยเฉพาะอย่างยิ่งเพราะคุณสามารถรวมเข้ากับPattern.CASE_INSENSITIVE
mjjaniec

13

\Q$5\Eผมคิดว่าสิ่งที่คุณหลังจากเป็น ดูเพิ่มเติมที่Pattern.quote(s)แนะนำใน Java5

ดูรายละเอียดรูปแบบ javadoc


ฉันสงสัยว่ามีความแตกต่างระหว่างสิ่งนี้กับการใช้ธง LITERAL หรือไม่เนื่องจาก javadoc แจ้งว่าไม่มีการตั้งค่าสถานะแบบฝังเพื่อสลับเปิดและปิด LITERAL: java.sun.com/j2se/1.5.0/docs/api/java/ util / regex / …
Chris Mazzola

15
โปรดทราบว่าการใช้ \ Q และ \ E นั้นใช้ได้ผลก็ต่อเมื่อคุณทราบอินพุตของคุณ Pattern.quote จะจัดการกรณีและปัญหาที่ข้อความของคุณมีลำดับเหล่านี้
Jeremy Huiskamp

10

ก่อนอื่นถ้า

  • คุณใช้ replaceAll ()
  • คุณไม่ได้ใช้ Matcher.quoteReplacement ()
  • ข้อความที่จะถูกแทนที่ด้วยรวมถึง $ 1

มันจะไม่ใส่ 1 ในตอนท้าย มันจะดูที่ regex การค้นหาสำหรับกลุ่มการจับคู่แรกและย่อยที่นั่นนั่นคือสิ่งที่ $ 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 () ไปดูใน regex การค้นหาสำหรับกลุ่มการจับคู่ที่สามไม่พบหนึ่งและ puked

ได้รับ:

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

แก้ไขปัญหา ผู้ใช้สามารถใส่อักขระทุกชนิดรวมถึงเครื่องหมายดอลลาร์โดยไม่มีปัญหา มันทำงานอย่างที่คุณคาดหวัง


6

มีรูปแบบการป้องกันคุณสามารถแทนที่สัญลักษณ์ทั้งหมดด้วย "\\\\" ยกเว้นตัวเลขและตัวอักษร และหลังจากนั้นคุณสามารถใส่รูปแบบที่มีการป้องกันสัญลักษณ์พิเศษของคุณเพื่อทำให้รูปแบบนี้ทำงานได้ไม่เหมือนกับข้อความที่ยกมาโง่ แต่ชอบเสื้อคลุม แต่เป็นของคุณเอง ไม่มีสัญลักษณ์พิเศษของผู้ใช้

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

คุณไม่ต้องหลบหนีจากช่องว่าง ดังนั้นคุณสามารถทำให้รูปแบบของคุณเป็น "([^ a-zA-z0-9])"
Erel Segal-Halevi

5
พิมพ์เล็กผลที่ตามมามาก: "([^ a-zA-z0-9])" ไม่ตรงกัน (เช่นไม่หนี) [, \,], ^ ซึ่งคุณต้องการหนีแน่นอน! ตัวพิมพ์คือ 'z' ตัวที่สองซึ่งควรเป็น 'Z' ไม่เช่นนั้นทุกอย่างจาก ASCII 65 ถึง ASCII 122 จะถูกรวมอยู่ด้วย
Zefiro

3

Pattern.quote ("blabla") ทำงานได้ดี

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

-2

ใช้สัญลักษณ์ ^ (การปฏิเสธ) เพื่อจับคู่สิ่งที่ไม่ได้อยู่ในกลุ่มอักขระ

นี่คือลิงค์ไปยังนิพจน์ปกติ

นี่คือข้อมูลภาพเกี่ยวกับการปฏิเสธ:

ข้อมูลเกี่ยวกับการปฏิเสธ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.