Cara menghindari teks untuk ekspresi reguler di Jawa

320

Apakah Java memiliki cara bawaan untuk keluar dari teks arbitrer sehingga dapat dimasukkan dalam ekspresi reguler? Misalnya, jika pengguna saya memasukkan "$ 5", saya ingin mencocokkannya dengan tepat daripada "5" setelah akhir input.

Mat
sumber

Jawaban:

450

Sejak Java 1.5, ya :

Pattern.quote("$5");
Mike Stone
sumber
88
Tolong bukan berarti ini tidak lepas dari string itu sendiri, tetapi bungkus dengan menggunakan \Qdan \E. Ini dapat menyebabkan hasil yang tidak terduga, misalnya Pattern.quote("*.wav").replaceAll("*",".*")akan menghasilkan \Q.*.wav\Edan tidak .*\.wav, seperti yang Anda harapkan.
Matthias Ronge
11
@Paramaeleon Mengapa Anda mengharapkan foo (x) .bar () == x.bar ()?
Michael
7
@Paramaeleon Saya pikir Anda salah paham tentang use case.
vikingsteve
18
Saya hanya ingin menunjukkan bahwa cara melarikan diri ini berlaku juga untuk melarikan diri pada ekspresi yang Anda perkenalkan sesudahnya . Ini mungkin mengejutkan. Jika Anda melakukannya "mouse".toUpperCase().replaceAll("OUS","ic")akan kembali MicE. Anda would't berharap untuk kembali MICEkarena Anda tidak berlaku toUpperCase()pada ic. Dalam contoh saya quote()diterapkan pada .*insertet replaceAll()juga. Anda harus melakukan sesuatu yang lain, mungkin .replaceAll("*","\\E.*\\Q")akan berhasil, tetapi itu berlawanan dengan intuisi.
Matthias Ronge
2
@Paramaleon Jika itu berhasil dengan menambahkan lolos individu, contoh awal Anda masih tidak akan melakukan apa yang Anda inginkan ... jika lolos karakter secara individual, itu akan berubah *.wavmenjadi pola regex \*\.wav, dan ganti Semua akan mengubahnya menjadi \.*\.wav, berarti itu akan mencocokkan file yang namanya terdiri dari jumlah periode acak diikuti oleh .wav. Anda kemungkinan besar harus melakukannya replaceAll("\\*", ".*")jika mereka pergi dengan implementasi yang lebih rapuh yang bergantung pada mengenali semua charachters regex aktif yang mungkin dan melarikan diri secara individual ... apakah itu jauh lebih mudah?
Theodore Murdock
112

Perbedaan antara Pattern.quotedan Matcher.quoteReplacementtidak jelas bagi saya sebelum saya melihat contoh berikut

s.replaceFirst(Pattern.quote("text to replace"), 
               Matcher.quoteReplacement("replacement text"));
Pavel Feldman
sumber
29
Secara khusus, Pattern.quotemengganti karakter khusus dalam string pencarian regex, seperti. | + () Dll, dan Matcher.quoteReplacementmenggantikan karakter khusus dalam string pengganti, seperti \ 1 untuk referensi-ulang.
Steven
9
Saya tidak setuju. Pattern.quote membungkus argumennya dengan \ Q dan \ E. Itu tidak luput dari karakter khusus.
David Medinets
5
Matcher.quoteReplacement ("4 $ &% $") menghasilkan "4 \ $ &% \ $". Itu lolos dari karakter khusus.
David Medinets
4
Dengan kata lain: quoteReplacementhanya peduli pada dua simbol $dan \ yang misalnya dapat digunakan dalam string pengganti sebagai backreferences $1atau \1. Karena itu tidak boleh digunakan untuk melarikan diri / mengutip suatu regex.
SebastianH
1
Luar biasa. Berikut adalah contoh di mana kita ingin mengganti $Group$dengan T$UYO$HI. The $simbol khusus baik dalam pola dan penggantian:"$Group$ Members".replaceFirst(Pattern.quote("$Group$"), Matcher.quoteReplacement("T$UYO$HI"))
arun
29

Mungkin sudah terlambat untuk merespons, tetapi Anda juga dapat menggunakan Pattern.LITERAL, yang akan mengabaikan semua karakter khusus saat memformat:

Pattern.compile(textToFormat, Pattern.LITERAL);
Androidme
sumber
Ini sangat bagus karena Anda dapat menggabungkannya denganPattern.CASE_INSENSITIVE
mjjaniec
13

Saya pikir apa yang Anda cari \Q$5\E. Lihat juga Pattern.quote(s)diperkenalkan di Java5.

Lihat Pola javadoc untuk detailnya.

Rob Oxspring
sumber
Saya ingin tahu apakah ada perbedaan antara ini dan menggunakan bendera LITERAL, karena javadoc mengatakan tidak ada bendera yang disematkan untuk menghidupkan dan mematikan LITERAL: java.sun.com/j2se/1.5.0/docs/api/java/ util / regex / ...
Chris Mazzola
15
Perhatikan bahwa secara harfiah menggunakan \ Q dan \ E tidak masalah jika Anda tahu input Anda. Pattern.quote juga akan menangani kasus di mana teks Anda sebenarnya mengandung urutan ini.
Jeremy Huiskamp
10

Pertama, jika

  • Anda menggunakan replaceAll ()
  • Anda TIDAK menggunakan Matcher.quoteReplacement ()
  • teks yang akan diganti termasuk $ 1

itu tidak akan menempatkan 1 di akhir. Ini akan melihat regex pencarian untuk grup yang cocok pertama dan sub ITULAH. Itu artinya $ 1, $ 2 atau $ 3 berarti dalam teks pengganti: kelompok yang cocok dari pola pencarian.

Saya sering menyambungkan string panjang teks ke file .properties, lalu menghasilkan subjek dan badan email dari mereka. Memang, ini tampaknya menjadi cara standar untuk melakukan i18n di Spring Framework. Saya menempatkan tag XML, sebagai placeholder, ke dalam string dan saya menggunakan replaceAll () untuk mengganti tag XML dengan nilai-nilai saat runtime.

Saya mengalami masalah ketika pengguna memasukkan angka dolar, dengan tanda dolar. replaceAll () tersedak karenanya, dengan yang berikut ini muncul di 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)

Dalam hal ini, pengguna telah memasukkan "$ 3" di suatu tempat di input mereka dan replaceAll () pergi mencari di regex pencarian untuk kelompok yang cocok ketiga, tidak menemukan satu, dan muntah.

Diberikan:

// "msg" is a string from a .properties file, containing "<userInput />" among other tags
// "userInput" is a String containing the user's input

mengganti

msg = msg.replaceAll("<userInput \\/>", userInput);

dengan

msg = msg.replaceAll("<userInput \\/>", Matcher.quoteReplacement(userInput));

memecahkan masalah. Pengguna dapat memasukkan karakter apa pun, termasuk tanda dolar, tanpa masalah. Itu berperilaku persis seperti yang Anda harapkan.

Meower68
sumber
6

Untuk memiliki pola yang dilindungi, Anda dapat mengganti semua simbol dengan "\\", kecuali angka dan huruf. Dan setelah itu Anda dapat menempatkan dalam pola yang dilindungi itu simbol khusus Anda untuk membuat pola ini bekerja tidak seperti teks yang dikutip bodoh, tetapi benar-benar seperti patten, tetapi Anda sendiri. Tanpa simbol khusus pengguna.

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");
    }
}
Boy Moskow
sumber
Anda tidak harus keluar dari ruang. Jadi Anda bisa menambahkan pola Anda ke "([^ a-zA-z0-9])".
Erel Segal-Halevi
5
Kesalahan ketik kecil, konsekuensi besar: "([^ a-zA-z0-9])" "juga tidak cocok (yaitu tidak melarikan diri) [, \,], ^ yang tentu saja ingin Anda hindari! Kesalahan ketik adalah 'z' kedua yang harus menjadi 'Z', jika tidak semuanya termasuk ASCII 65 hingga ASCII 122
Zefiro
3

Pattern.quote ("blabla") berfungsi dengan baik.

Pattern.quote () berfungsi dengan baik. Itu melampirkan kalimat dengan karakter " \ Q " dan " \ E ", dan jika itu lolos "\ Q" dan "\ E". Namun, jika Anda perlu melakukan pelarian ekspresi reguler yang sebenarnya (atau pelolosan khusus), Anda dapat menggunakan kode ini:

String someText = "Some/s/wText*/,**";
System.out.println(someText.replaceAll("[-\\[\\]{}()*+?.,\\\\\\\\^$|#\\\\s]", "\\\\$0"));

Metode ini mengembalikan: Some / \ s / wText * / \, **

Contoh kode dan tes:

String someText = "Some\\E/s/wText*/,**";
System.out.println("Pattern.quote: "+ Pattern.quote(someText));
System.out.println("Full escape: "+someText.replaceAll("[-\\[\\]{}()*+?.,\\\\\\\\^$|#\\\\s]", "\\\\$0"));
Adam111p
sumber
-2

Simbol ^ (Negasi) digunakan untuk mencocokkan sesuatu yang tidak ada dalam grup karakter.

Ini tautan ke Ekspresi Reguler

Ini adalah info gambar tentang negasi:

Info tentang negasi

Akhil Kathi
sumber