ช่องว่างที่ตรงกัน Regex - Java


106

Java API สำหรับนิพจน์ทั่วไประบุว่า\sจะตรงกับช่องว่าง ดังนั้น regex \\s\\sควรจับคู่ช่องว่างสองช่อง

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

จุดมุ่งหมายคือการแทนที่อินสแตนซ์ทั้งหมดของช่องว่างสองช่องที่ติดต่อกันด้วยช่องว่างเดียว อย่างไรก็ตามสิ่งนี้ไม่ได้ผลจริง

ฉันมีความเข้าใจผิดอย่างร้ายแรงเกี่ยวกับนิพจน์ทั่วไปหรือคำว่า "ช่องว่าง" หรือไม่?


1
String มีฟังก์ชัน replaceAll ที่จะช่วยคุณประหยัดโค้ดไม่กี่บรรทัด download.oracle.com/javase/1.5.0/docs/api/java/lang/String.html
Zach L

1
ไม่ใช่ความเข้าใจผิดของคุณ แต่เป็นของ Java ลองแยกสตริง"abc \xA0 def \x85 xyz"เพื่อดูว่าฉันหมายถึงอะไร: มีเพียงสามฟิลด์ที่นั่น
tchrist

3
คุณลอง "\\ s +" ด้วยวิธีนี้คุณจะแทนที่ช่องว่างสองช่องหรือมากกว่าเป็นหนึ่ง
hrzafer

ฉันสงสัยมานานกว่าหนึ่งชั่วโมงแล้วว่าทำไมการแยกของฉันจึงไม่แยกออกจากช่องว่าง ขอบคุณล้าน!
Marcin

คำตอบ:


44

ใช่คุณต้องคว้าผลลัพธ์ของmatcher.replaceAll():

String result = matcher.replaceAll(" ");
System.out.println(result);

18
Gah. ฉันรู้สึกเหมือนเป็นคนงี่เง่าที่ยิ่งใหญ่ที่สุดในโลก ทั้งฉันและอีกสองคนดูเหมือนจะไม่สังเกตเห็นสิ่งนั้น ฉันเดาว่าข้อผิดพลาดเล็ก ๆ น้อย ๆ ที่โง่ที่สุดทำให้เราผิดหวังในบางครั้งใช่มั้ย?

จริงดิ! ฉันเดาว่าจะเกิดขึ้นกับคนที่ดีที่สุด
saibharath

จะเกิดอะไรขึ้นถ้าฉันต้องการถ้าข้อความนั้นมี White Spaces?
Gilberto Ibarra

ตามคำตอบของฉันด้านล่างใช้ \ p {Zs} แทน \ s หากคุณต้องการจับคู่ช่องว่างยูนิโคด
Robert

195

คุณไม่สามารถใช้\sใน Java เพื่อจับคู่พื้นที่สีขาวบนชุดอักขระดั้งเดิมของตัวเองได้เนื่องจาก Java ไม่สนับสนุนคุณสมบัติพื้นที่สีขาว Unicode แม้ว่าการทำเช่นนั้นจะต้องเป็นไปตามRL1.2 ของ UTS # 18อย่างเคร่งครัด! สิ่งที่มีไม่เป็นไปตามมาตรฐานอนิจจา

Unicode กำหนด 26 คะแนนเป็นรหัสที่\p{White_Space}20 ของพวกเขาเป็นประเภทต่างๆของ\pZ GeneralCategory = แยกและที่เหลืออีก 6 GeneralCategory \p{Cc} = ควบคุม

พื้นที่สีขาวเป็นสถานที่ให้บริการที่ค่อนข้างมั่นคงและพื้นที่เดียวกันเหล่านั้นคงอยู่ตลอดไป ถึงกระนั้น Java ก็ไม่มีคุณสมบัติที่สอดคล้องกับ The Unicode Standard สำหรับสิ่งเหล่านี้ดังนั้นคุณต้องใช้รหัสเช่นนี้แทน:

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 + "]";

ตอนนี้คุณสามารถใช้whitespace_charclass + "+"เป็นรูปแบบในreplaceAllไฟล์.


ขอโทษ 'การแข่งขันทั้งหมดนั้น regexes ของ Java ทำงานได้ไม่ดีนักกับชุดอักขระดั้งเดิมของตัวเองดังนั้นคุณต้องกระโดดผ่านห่วงที่แปลกใหม่เพื่อให้ทำงานได้

และถ้าคุณคิดว่าพื้นที่สีขาวไม่ดีคุณจะเห็นสิ่งที่คุณต้องทำเพื่อให้ได้\wและ\bในที่สุดก็ประพฤติอย่างถูกต้อง!

ใช่มันเป็นไปได้และใช่มันเป็นเรื่องยุ่งเหยิง นั่นคือการกุศลแม้ วิธีที่ง่ายที่สุดในการรับไลบรารี regex ที่สอดคล้องกับมาตรฐานสำหรับ Java คือการ JNI ไปยังสิ่งของของ ICU นั่นคือสิ่งที่ Google ทำสำหรับ Android เพราะ OraSun ไม่สามารถวัดได้

หากคุณไม่ต้องการทำเช่นนั้น แต่ยังต้องการยึดติดกับ Java ฉันมีไลบรารีการเขียน regex ส่วนหน้าที่ฉันเขียนว่า "แก้ไข" รูปแบบของ Java อย่างน้อยก็เพื่อให้เป็นไปตามข้อกำหนดของRL1.2a ใน UTS # 18 Unicode นิพจน์ปกติ


12
ขอขอบคุณสำหรับข้อมูลเกี่ยวกับข้อ จำกัด regex ของ Java +1
ridgerunner

4
ฉันไปโหวตคำตอบนี้ว่ามีประโยชน์และพบว่าฉันมีอยู่แล้ว ขอบคุณเป็นครั้งที่สอง :)
Andrew Wyld

5
นี่มันเก่าจริงๆ ถูกต้องหรือไม่ที่สิ่งนี้ได้รับการแก้ไขใน java7 ด้วยแฟล็ก UNICODE_CHARACTER_CLASS (หรือใช้ (? u))
kritzikratzi

5
@tchrist หากสิ่งนี้ได้รับการแก้ไขใน java 7+ คุณสามารถอัปเดตคำตอบด้วยวิธีที่ถูกต้องในการทำสิ่งนี้ได้หรือไม่?
beerbajay

7
ด้วย Java 7+ คุณสามารถทำ: "(? u) \ s" เพื่อเรียกใช้ regex ด้วยความสอดคล้องของ Unicode Technical Standard หรือคุณสามารถตั้งค่าสถานะ UNICODE_CHARACTER_CLASS เป็นจริงเมื่อสร้างรูปแบบ นี่คือเอกสาร: docs.oracle.com/javase/7/docs/api/java/util/regex/…
Didier A.

15

สำหรับ Java (ไม่ใช่ php ไม่ใช่ javascript ไม่ใช่อย่างอื่น):

txt.replaceAll("\\p{javaSpaceChar}{2,}"," ")

สตริงไม่เปลี่ยนรูปดังนั้นคุณต้องกำหนดผลลัพธ์ให้กับบางสิ่งเช่น 'txt = txt.replaceAll ()' ฉันไม่ได้โหวตคำตอบของคุณ แต่นั่นอาจเป็นสาเหตุที่มีคนอื่นทำเช่นนั้น
Enwired

6
ฉันรู้ว่า replaceAll ส่งคืนสตริงสิ่งที่สำคัญโปรแกรมเมอร์จาวา 4 คือ \\ p {javaSpaceChar}
surfealokesea

2
คำถามเดิมทำให้เกิดข้อผิดพลาดในการไม่กำหนดสตริงใหม่ให้กับตัวแปร การชี้ให้เห็นความผิดพลาดนั้นจึงเป็นประเด็นสำคัญที่สุดของคำตอบ
ออกอากาศ

สิ่งนี้ช่วยแก้ปัญหาของฉันใน Groovy ได้โดยสิ้นเชิง! ในที่สุด! ลอง regex ทุกครั้งที่ฉันพบว่าจะตรงกับพื้นที่สีขาวทั้งหมดรวมถึง NON-BREAK-SPACE (ASCII 160) !!!
Piko

5

เมื่อฉันส่งคำถามไปยังฟอรัม Regexbuddy (แอปพลิเคชันสำหรับนักพัฒนา regex) ฉันได้รับคำตอบที่แน่นอนมากขึ้นสำหรับคำถาม Java ของฉัน:

"ผู้เขียนข้อความ: Jan Goyvaerts

ใน Java shorthands \ s, \ d และ \ w รวมเฉพาะอักขระ ASCII ... นี่ไม่ใช่ข้อผิดพลาดใน Java แต่เป็นเพียงหนึ่งในหลาย ๆ สิ่งที่คุณต้องระวังเมื่อทำงานกับนิพจน์ทั่วไป เพื่อให้ตรงกับช่องว่าง Unicode ทั้งหมดและการแบ่งบรรทัดคุณสามารถใช้ [\ s \ p {Z}] ใน Java RegexBuddy ยังไม่สนับสนุนคุณสมบัติเฉพาะของ Java เช่น \ p {javaSpaceChar} (ซึ่งตรงกับอักขระเดียวกับ [\ s \ p {Z}])

... \ s \ s จะจับคู่ช่องว่างสองช่องถ้าอินพุตเป็น ASCII เท่านั้น ปัญหาที่แท้จริงคือรหัสของ OP ตามที่คำตอบที่ได้รับการยอมรับในคำถามนั้น "


3
[\s\p{z}]ละเว้นอักขระ Unicode "บรรทัดถัดไป" U + 0085 ใช้[\s\u0085\p{Z}].
Robert Tupelo-Schneck

3

ดูเหมือนจะทำงานให้ฉัน:

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

จะพิมพ์:

" a  b   c"

ฉันคิดว่าคุณตั้งใจจะทำสิ่งนี้แทนรหัสของคุณ:

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

System.out.println(result);

3

เพื่อวัตถุประสงค์ของคุณคุณสามารถใช้ตัวอย่างข้อมูลนี้:

import org.apache.commons.lang3.StringUtils;

StringUtils.normalizeSpace(string);

การทำเช่นนี้จะทำให้ระยะห่างเป็นปกติและจะตัดช่องว่างเริ่มต้นและต่อท้ายด้วย

String sampleString = "Hello    world!";
sampleString.replaceAll("\\s{2}", " "); // replaces exactly two consecutive spaces
sampleString.replaceAll("\\s{2,}", " "); // replaces two or more consecutive white spaces

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

3
ไมค์ขอขอบคุณที่สละเวลาตอบคำถามนี้ได้รับการแก้ไขเมื่อหลายเดือนก่อน ไม่จำเป็นต้องตอบคำถามเก่าเช่นนี้

6
หากมีใครสามารถแสดงวิธีแก้ปัญหาที่แตกต่างและดีกว่าได้การตอบคำถามเก่า ๆ ก็เป็นเรื่องที่ถูกต้อง
james.garriss

1

Java มีการพัฒนาตั้งแต่ปัญหานี้เกิดขึ้นครั้งแรก คุณสามารถจับคู่อักขระช่องว่าง Unicode ได้ทุกรูปแบบโดยใช้\p{Zs}กลุ่ม

ดังนั้นหากคุณต้องการแทนที่ช่องว่างที่แปลกใหม่อย่างน้อยหนึ่งช่องด้วยพื้นที่ธรรมดาคุณสามารถทำได้:

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

นอกจากนี้มูลค่ารู้ถ้าคุณเคยใช้trim()ฟังก์ชั่นสตริงคุณควรจะดูที่ (ค่อนข้างใหม่บริการ) strip(), stripLeading()และstripTrailing()ฟังก์ชั่นในสาย สามารถช่วยคุณตัดแต่งอักขระเว้นวรรคสีขาวกระรอกทุกประเภท สำหรับข้อมูลเพิ่มเติมเกี่ยวกับพื้นที่ที่รวมไว้โปรดดูที่Character.isWhitespace()ฟังก์ชันของ Java


-3

การใช้ช่องว่างใน RE เป็นความเจ็บปวด แต่ฉันเชื่อว่ามันได้ผล ปัญหาของ OP สามารถแก้ไขได้โดยใช้ StringTokenizer หรือวิธีการแยก () อย่างไรก็ตามในการใช้ RE (ยกเลิกการใส่เครื่องหมายถูก println () เพื่อดูว่าตัวจับคู่แบ่งสตริงอย่างไร) นี่คือโค้ดตัวอย่าง:

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

สร้างสิ่งต่อไปนี้ (คอมไพล์ด้วย javac และเรียกใช้ที่พรอมต์คำสั่ง):

% java Two21WS เริ่มต้น: "ab cdef gh ij kl" Two21WS: "ab cdef gh ij kl"


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