Regex Pencocokan Spasi Putih - Java

106

API Java untuk ekspresi reguler menyatakan yang \sakan cocok dengan spasi. Jadi regex \\s\\sharus cocok dengan dua spasi.

Pattern whitespace = Pattern.compile("\\s\\s");
matcher = whitespace.matcher(modLine);
while (matcher.find()) matcher.replaceAll(" ");

Tujuannya adalah untuk mengganti semua contoh dari dua spasi berurutan dengan satu spasi. Namun ini sebenarnya tidak berhasil.

Apakah saya mengalami kesalahpahaman yang parah tentang ekspresi reguler atau istilah "spasi"?


sumber
1
String memiliki fungsi replaceAll yang akan menghemat beberapa baris kode. download.oracle.com/javase/1.5.0/docs/api/java/lang/String.html
Zach L
1
Ini bukan kesalahpahaman Anda, tapi Jawa. Coba pisahkan string seperti "abc \xA0 def \x85 xyz"untuk melihat apa yang saya maksud: hanya ada tiga bidang di sana.
tchrist
3
Apakah Anda mencoba "\\ s +". Dengan ini Anda mengganti dua atau lebih spasi menjadi satu.
hrzafer
Saya sudah bertanya-tanya selama lebih dari satu jam mengapa pemisahan saya tidak membelah spasi. Terima kasih banyak!
Marcin

Jawaban:

44

Ya, Anda perlu mengambil hasil dari matcher.replaceAll():

String result = matcher.replaceAll(" ");
System.out.println(result);
Raph Levien
sumber
18
Gah. Saya merasa seperti orang idiot terbesar di dunia. Baik saya maupun dua orang lain sepertinya tidak memperhatikan itu. Saya kira kesalahan kecil terbodoh terkadang membuat kita marah, eh?
Benar sekali! Saya kira itu terjadi dengan yang terbaik dari mereka
saibharath
Apa yang terjadi jika saya perlu mendapatkan jika teks memiliki Spasi Putih.?
Gilberto Ibarra
Sesuai jawaban saya di bawah, gunakan \ p {Zs} daripada \ s jika Anda ingin mencocokkan spasi kosong unicode.
Robert
194

Anda tidak dapat menggunakan \sdi Java untuk mencocokkan spasi putih pada kumpulan karakter aslinya sendiri, karena Java tidak mendukung properti spasi kosong Unicode - meskipun hal itu benar-benar diperlukan untuk memenuhi RL1.2 UTS # 18! Apa yang dimilikinya tidak sesuai standar, sayangnya.

Unicode mendefinisikan 26 poin kode sebagai \p{White_Space}: 20 di antaranya adalah berbagai macam \pZ GeneralCategory = Separator , dan 6 sisanya adalah \p{Cc} GeneralCategory = Control .

Ruang putih adalah properti yang cukup stabil, dan yang sama telah ada hampir selamanya. Meski begitu, Java tidak memiliki properti yang sesuai dengan The Unicode Standard untuk ini, jadi Anda harus menggunakan kode seperti ini:

String whitespace_chars =  ""       /* dummy empty string for homogeneity */
                        + "\\u0009" // CHARACTER TABULATION
                        + "\\u000A" // LINE FEED (LF)
                        + "\\u000B" // LINE TABULATION
                        + "\\u000C" // FORM FEED (FF)
                        + "\\u000D" // CARRIAGE RETURN (CR)
                        + "\\u0020" // SPACE
                        + "\\u0085" // NEXT LINE (NEL) 
                        + "\\u00A0" // NO-BREAK SPACE
                        + "\\u1680" // OGHAM SPACE MARK
                        + "\\u180E" // MONGOLIAN VOWEL SEPARATOR
                        + "\\u2000" // EN QUAD 
                        + "\\u2001" // EM QUAD 
                        + "\\u2002" // EN SPACE
                        + "\\u2003" // EM SPACE
                        + "\\u2004" // THREE-PER-EM SPACE
                        + "\\u2005" // FOUR-PER-EM SPACE
                        + "\\u2006" // SIX-PER-EM SPACE
                        + "\\u2007" // FIGURE SPACE
                        + "\\u2008" // PUNCTUATION SPACE
                        + "\\u2009" // THIN SPACE
                        + "\\u200A" // HAIR SPACE
                        + "\\u2028" // LINE SEPARATOR
                        + "\\u2029" // PARAGRAPH SEPARATOR
                        + "\\u202F" // NARROW NO-BREAK SPACE
                        + "\\u205F" // MEDIUM MATHEMATICAL SPACE
                        + "\\u3000" // IDEOGRAPHIC SPACE
                        ;        
/* A \s that actually works for Java’s native character set: Unicode */
String     whitespace_charclass = "["  + whitespace_chars + "]";    
/* A \S that actually works for  Java’s native character set: Unicode */
String not_whitespace_charclass = "[^" + whitespace_chars + "]";

Sekarang Anda dapat menggunakan whitespace_charclass + "+"sebagai pola di file replaceAll.


Maaf tentang semua itu. Regex Java tidak bekerja dengan baik pada kumpulan karakter aslinya, jadi Anda benar-benar harus melewati rintangan eksotis untuk membuatnya berfungsi.

Dan jika menurut Anda ruang kosong itu buruk, Anda harus melihat apa yang harus Anda lakukan untuk mendapatkan \wdan \bakhirnya berperilaku dengan benar!

Ya, itu mungkin, dan ya, ini adalah kekacauan yang mencengangkan. Itu bahkan beramal. Cara termudah untuk mendapatkan perpustakaan regex yang sesuai standar untuk Java adalah dengan JNI ke barang-barang ICU. Itulah yang dilakukan Google untuk Android, karena OraSun tidak sesuai.

Jika Anda tidak ingin melakukan itu tetapi masih ingin tetap menggunakan Java, saya memiliki pustaka penulisan ulang regex front-end. Saya menulis bahwa "memperbaiki" pola Java, setidaknya agar sesuai dengan persyaratan RL1.2a di UTS # 18, Unicode Regular Expressions .

tchrist
sumber
12
Terima kasih telah mengetahui tentang batasan regex Java. +1
ridgerunner
4
Saya pergi untuk memilih jawaban ini sebagai membantu dan menemukan saya sudah punya. Jadi terima kasih untuk kedua kalinya :)
Andrew Wyld
5
ini sangat tua. apakah benar bahwa ini telah diperbaiki di java7 dengan bendera UNICODE_CHARACTER_CLASS? (atau menggunakan (? U))
kritzikratzi
5
@tchrist Jika ini diperbaiki di java 7+, dapatkah Anda memperbarui jawaban dengan cara yang sekarang benar untuk melakukan ini?
beerbajay
7
Dengan Java 7+ Anda dapat melakukan: "(? U) \ s" untuk menjalankan regex dengan kesesuaian Standar Teknis Unicode. Atau Anda dapat membuat tanda UNICODE_CHARACTER_CLASS menjadi benar saat membuat pola. Ini doknya
Didier A.
15

Untuk Java (bukan php, bukan javascript, tidak lainnya):

txt.replaceAll("\\p{javaSpaceChar}{2,}"," ")
surfealokesea
sumber
String tidak dapat diubah, jadi Anda harus menetapkan hasilnya ke sesuatu, seperti 'txt = txt.replaceAll ()' Saya tidak memilih jawaban Anda, tapi itu mungkin alasan orang lain melakukannya.
Terpasang pada
6
Saya tahu replaceAll mengembalikan string yang penting 4 programmer java adalah \\ p {javaSpaceChar}
surfealokesea
2
Pertanyaan asli membuat kesalahan dengan tidak menetapkan string baru ke variabel. Menunjukkan kesalahan itu dengan demikian adalah poin terpenting dari jawabannya.
Terpasang
Ini benar-benar menyelesaikan masalah saya di Groovy! Akhirnya! Telah mencoba setiap regex yang dapat saya temukan yang cocok dengan semua ruang putih termasuk NON-BREAK-SPACE (ASCII 160) !!!
Piko
5

ketika saya mengirim pertanyaan ke forum Regexbuddy (aplikasi pengembang regex), saya mendapat jawaban yang lebih tepat untuk pertanyaan Java saya:

"Penulis pesan: Jan Goyvaerts

Di Java, singkatan \ s, \ d, dan \ w hanya menyertakan karakter ASCII. ... Ini bukan bug di Java, tetapi hanya salah satu dari banyak hal yang perlu Anda waspadai saat bekerja dengan ekspresi reguler. Untuk mencocokkan semua spasi kosong Unicode serta jeda baris, Anda dapat menggunakan [\ s \ p {Z}] di Java. RegexBuddy belum mendukung properti khusus Java seperti \ p {javaSpaceChar} (yang cocok dengan karakter yang sama persis dengan [\ s \ p {Z}]).

... \ s \ s akan cocok dengan dua spasi, jika inputnya hanya ASCII. Masalah sebenarnya adalah dengan kode OP, seperti yang ditunjukkan oleh jawaban yang diterima dalam pertanyaan itu. "

Tuomas
sumber
3
[\s\p{z}]menghilangkan karakter Unicode "baris berikutnya" U + 0085. Gunakan [\s\u0085\p{Z}].
Robert Tupelo-Schneck
3

Sepertinya berhasil untuk saya:

String s = "  a   b      c";
System.out.println("\""  + s.replaceAll("\\s\\s", " ") + "\"");

akan mencetak:

" a  b   c"

Saya pikir Anda bermaksud melakukan ini alih-alih kode Anda:

Pattern whitespace = Pattern.compile("\\s\\s");
Matcher matcher = whitespace.matcher(s);
String result = "";
if (matcher.find()) {
    result = matcher.replaceAll(" ");
}

System.out.println(result);
Mihai Toader
sumber
3

Untuk tujuan Anda, Anda dapat menggunakan snnippet ini:

import org.apache.commons.lang3.StringUtils;

StringUtils.normalizeSpace(string);

Ini akan menormalkan jarak menjadi tunggal dan akan menghapus spasi putih awal dan akhir juga.

String sampleString = "Hello    world!";
sampleString.replaceAll("\\s{2}", " "); // replaces exactly two consecutive spaces
sampleString.replaceAll("\\s{2,}", " "); // replaces two or more consecutive white spaces
Rashid Mv
sumber
1
Pattern whitespace = Pattern.compile("\\s\\s");
matcher = whitespace.matcher(modLine);

boolean flag = true;
while(flag)
{
 //Update your original search text with the result of the replace
 modLine = matcher.replaceAll(" ");
 //reset matcher to look at this "new" text
 matcher = whitespace.matcher(modLine);
 //search again ... and if no match , set flag to false to exit, else run again
 if(!matcher.find())
 {
 flag = false;
 }
}
Mike
sumber
3
Mike, meskipun saya menghargai Anda meluangkan waktu untuk menjawab, pertanyaan ini telah diselesaikan beberapa bulan yang lalu. Tidak perlu menjawab pertanyaan setua ini.
6
Jika seseorang dapat menunjukkan solusi yang berbeda dan lebih baik, menjawab pertanyaan lama sangatlah sah.
james.garriss
1

Java telah berkembang sejak masalah ini pertama kali diangkat. Anda dapat mencocokkan semua jenis karakter spasi unicode dengan menggunakan \p{Zs}grup.

Jadi, jika Anda ingin mengganti satu atau lebih ruang eksotis dengan ruang polos, Anda dapat melakukan ini:

String txt = "whatever my string is";
txt.replaceAll("\\p{Zs}+", " ")

Juga layak mengetahui, jika Anda telah menggunakan trim()fungsi string yang Anda harus melihat pada (relatif baru) strip(), stripLeading()dan stripTrailing()fungsi pada string. Dapat membantu Anda memangkas semua jenis karakter spasi kosong. Untuk informasi lebih lanjut tentang apa yang disertakan, lihat Character.isWhitespace()fungsi Java .

Robert
sumber
-3

Penggunaan spasi di RE memang menyebalkan, tapi saya yakin itu berhasil. Masalah OP juga dapat diselesaikan menggunakan StringTokenizer atau metode split (). Namun, untuk menggunakan RE (hapus tanda komentar println () untuk melihat bagaimana matcher memecah String), berikut ini contoh kode:

import java.util.regex.*;

public class Two21WS {
    private String  str = "";
    private Pattern pattern = Pattern.compile ("\\s{2,}");  // multiple spaces

    public Two21WS (String s) {
            StringBuffer sb = new StringBuffer();
            Matcher matcher = pattern.matcher (s);
            int startNext = 0;
            while (matcher.find (startNext)) {
                    if (startNext == 0)
                            sb.append (s.substring (0, matcher.start()));
                    else
                            sb.append (s.substring (startNext, matcher.start()));
                    sb.append (" ");
                    startNext = matcher.end();
                    //System.out.println ("Start, end = " + matcher.start()+", "+matcher.end() +
                    //                      ", sb: \"" + sb.toString() + "\"");
            }
            sb.append (s.substring (startNext));
            str = sb.toString();
    }

    public String toString () {
            return str;
    }

    public static void main (String[] args) {
            String tester = " a    b      cdef     gh  ij   kl";
            System.out.println ("Initial: \"" + tester + "\"");
            System.out.println ("Two21WS: \"" + new Two21WS(tester) + "\"");
}}

Ini menghasilkan yang berikut (kompilasi dengan javac dan jalankan pada prompt perintah):

% java Two21WS Awal: "ab cdef gh ij kl" Two21WS: "ab cdef gh ij kl"

Manidip Sengupta
sumber
8
WTF !? Mengapa Anda ingin melakukan semua itu ketika Anda bisa menelepon replaceAll()saja?
Alan Moore