Khoảng trắng đối sánh Regex - Java


106

API Java cho các trạng thái biểu thức chính quy\s sẽ khớp với khoảng trắng. Vì vậy, regex \\s\\sphải khớp với hai khoảng trắng.

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

Mục đích của việc này là thay thế tất cả các trường hợp của hai khoảng trắng liên tiếp bằng một khoảng trắng duy nhất. Tuy nhiên điều này không thực sự hoạt động.

Tôi có hiểu lầm nghiêm trọng về regexes hoặc thuật ngữ "khoảng trắng" không?


1
Chuỗi có một hàm ReplaceAll sẽ giúp bạn tiết kiệm một vài dòng mã. download.oracle.com/javase/1.5.0/docs/api/java/lang/String.html
Zach L

1
Đó không phải là sự hiểu lầm của bạn, mà là của Java. Hãy thử tách một chuỗi "abc \xA0 def \x85 xyz"để xem ý tôi là: chỉ có ba trường ở đó.
tchrist

3
Bạn đã thử "\\ s +". Với điều này, bạn thay thế hai hoặc nhiều khoảng trắng thành một.
hrzafer

Tôi đã tự hỏi trong hơn một giờ tại sao phần tách của tôi không bị tách theo khoảng trắng. Cảm ơn rất nhiều!
Marcin

Câu trả lời:


44

Vâng, bạn cần lấy kết quả của matcher.replaceAll():

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

18
Gah! Tôi cảm thấy mình như một tên ngốc lớn nhất trên trái đất. Tôi và hai người khác dường như không nhận thấy điều đó. Tôi đoán đôi khi những lỗi nhỏ ngu ngốc nhất khiến chúng ta phải hứng chịu, hả?

Quá đúng! Tôi đoán điều đó xảy ra với sản phẩm tốt nhất của họ
saibharath

Điều gì xảy ra nếu tôi cần lấy nếu văn bản có Khoảng trắng.?
Gilberto Ibarra

Theo câu trả lời của tôi dưới đây, hãy sử dụng \ p {Zs} thay vì \ s nếu bạn muốn khớp với khoảng trắng unicode.
Robert

194

Bạn không thể sử dụng \strong Java để đối sánh khoảng trắng trên bộ ký tự gốc của riêng nó, vì Java không hỗ trợ thuộc tính khoảng trắng Unicode - mặc dù làm như vậy là bắt buộc để đáp ứng RL1.2 của UTS # 18! Những gì nó có không phù hợp với tiêu chuẩn, than ôi.

Unicode định nghĩa 26 điểm mã là \p{White_Space}: 20 điểm trong số đó là các loại \pZ GeneralCategory = Separator , và 6 điểm còn lại là \p{Cc} GeneralCategory = Control .

Khoảng trắng là một thuộc tính khá ổn định và những đặc tính đó đã tồn tại hầu như mãi mãi. Mặc dù vậy, Java không có thuộc tính nào tuân theo Tiêu chuẩn Unicode cho những thứ này, vì vậy bạn phải sử dụng mã như sau:

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

Bây giờ bạn có thể sử dụng whitespace_charclass + "+"làm mẫu trong của bạn replaceAll.


Xin lỗi 'về tất cả những điều đó. Các regex của Java không hoạt động tốt trên bộ ký tự gốc của chính nó, và vì vậy bạn thực sự phải vượt qua các vòng lặp kỳ lạ để làm cho chúng hoạt động.

Và nếu bạn nghĩ khoảng trắng là xấu, bạn nên xem bạn phải làm gì để có được \w\bcuối cùng là cư xử đúng mực!

Vâng, nó có thể, và vâng, đó là một mớ hỗn độn. Đó là từ thiện, thậm chí. Cách dễ nhất để có được một thư viện regex phù hợp với tiêu chuẩn cho Java là JNI chuyển sang nội dung của ICU. Đó là những gì Google làm cho Android, bởi vì OraSun's không đo lường được.

Nếu bạn không muốn làm điều đó nhưng vẫn muốn gắn bó với Java, tôi có một thư viện viết lại regex front-end tôi đã viết rằng "sửa chữa" các mẫu của Java, ít nhất là để chúng tuân theo các yêu cầu của RL1.2a trong UTS # 18, Biểu thức chính quy Unicode .


12
Cảm ơn vì đã quan tâm đến những hạn chế regex của Java. +1
ridgerunner

4
Tôi đã bỏ phiếu cho câu trả lời này là hữu ích và thấy rằng tôi đã có. Vì vậy, cảm ơn bạn một giây thời gian :)
Andrew Wyld

5
điều này thực sự cũ. có chính xác là điều này đã được sửa trong java7 với cờ UNICODE_CHARACTER_CLASS không? (hoặc sử dụng (U)?)
kritzikratzi

5
@tchrist Nếu điều này được khắc phục trong java 7+, bạn có thể cập nhật câu trả lời với cách hiện đúng để thực hiện việc này không?
beerbajay

7
Với Java 7+, bạn có thể thực hiện: "(? U) \ s" để chạy regex với sự tuân thủ Tiêu chuẩn Kỹ thuật Unicode. Hoặc bạn có thể đặt cờ UNICODE_CHARACTER_CLASS thành true khi tạo mẫu. Đây là tài liệu: docs.oracle.com/javase/7/docs/api/java/util/regex/…
Didier A.

15

Đối với Java (không phải php, không phải javascript, không phải bất kỳ thứ gì khác):

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

Các chuỗi là bất biến, do đó bạn phải gán kết quả cho một cái gì đó, chẳng hạn như 'txt = txt.replaceAll ()' Tôi đã không bỏ phiếu cho câu trả lời của bạn, nhưng đó có thể là lý do tại sao người khác làm như vậy.
Đã đăng ký

6
Tôi biết replaceAll trả về một chuỗi quan trọng điều 4 Programers java là p \\ {javaSpaceChar}
surfealokesea

2
Câu hỏi ban đầu đã mắc lỗi khi không gán chuỗi mới cho một biến. Do đó, chỉ ra sai lầm đó là điểm quan trọng nhất của câu trả lời.
Đã đăng ký

Điều này hoàn toàn giải quyết được vấn đề của tôi ở Groovy! Cuối cùng! Tôi đã thử mọi regex mà tôi có thể tìm thấy sẽ phù hợp với tất cả các khoảng trắng bao gồm cả KHÔNG GIAN-ĐỘT PHÁ (ASCII 160) !!!
Piko

5

khi tôi gửi câu hỏi đến diễn đàn Regexbuddy (ứng dụng dành cho nhà phát triển regex), tôi đã nhận được câu trả lời chính xác hơn cho câu hỏi Java của mình:

"Tác giả tin nhắn: Jan Goyvaerts

Trong Java, các viết tắt \ s, \ d và \ w chỉ bao gồm các ký tự ASCII. ... Đây không phải là một lỗi trong Java, mà chỉ đơn giản là một trong nhiều điều bạn cần lưu ý khi làm việc với biểu thức chính quy. Để khớp với tất cả khoảng trắng Unicode cũng như ngắt dòng, bạn có thể sử dụng [\ s \ p {Z}] trong Java. RegexBuddy chưa hỗ trợ các thuộc tính dành riêng cho Java như \ p {javaSpaceChar} (khớp với các ký tự giống hệt như [\ s \ p {Z}]).

... \ s \ s sẽ khớp với hai khoảng trắng, nếu đầu vào chỉ là ASCII. Vấn đề thực sự là với mã của OP, như đã được chỉ ra bởi câu trả lời được chấp nhận trong câu hỏi đó. "


3
[\s\p{z}]bỏ qua ký tự Unicode "dòng tiếp theo" U + 0085. Sử dụng [\s\u0085\p{Z}].
Robert Tupelo-Schneck

3

Dường như làm việc cho tôi:

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

sẽ in:

" a  b   c"

Tôi nghĩ rằng bạn định làm điều này thay vì mã của bạn:

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

System.out.println(result);

3

Đối với mục đích của bạn, bạn có thể sử dụng đoạn mã này:

import org.apache.commons.lang3.StringUtils;

StringUtils.normalizeSpace(string);

Điều này sẽ bình thường hóa khoảng cách thành đơn và cũng sẽ loại bỏ khoảng trắng đầu và cuối.

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
Mike, trong khi tôi đánh giá cao bạn đã dành thời gian trả lời, câu hỏi này đã được giải quyết vài tháng trước. Không cần thiết phải trả lời những câu hỏi cũ như thế này.

6
Nếu ai đó có thể đưa ra một giải pháp khác, tốt hơn, thì việc trả lời các câu hỏi cũ là hoàn toàn hợp pháp.
james.garriss

1

Java đã phát triển kể từ khi vấn đề này lần đầu tiên được đưa ra. Bạn có thể khớp tất cả các cách ký tự khoảng trắng unicode bằng cách sử dụng \p{Zs}nhóm.

Vì vậy, nếu bạn muốn thay thế một hoặc nhiều không gian lạ bằng một không gian đơn giản, bạn có thể thực hiện điều này:

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

Cũng đáng biết, nếu bạn đã sử dụng trim()chức năng chuỗi bạn nên có một cái nhìn tại (tương đối mới) strip(), stripLeading()stripTrailing()các chức năng trên dây. Công cụ này có thể giúp bạn cắt bỏ tất cả các loại ký tự khoảng trắng cực kỳ nhỏ. Để biết thêm thông tin về không gian nào được bao gồm, hãy xem Character.isWhitespace()chức năng của Java .


-3

Sử dụng khoảng trắng trong RE là một vấn đề khó khăn, nhưng tôi tin rằng chúng hiệu quả. Vấn đề của OP cũng có thể được giải quyết bằng cách sử dụng StringTokenizer hoặc phương thức split (). Tuy nhiên, để sử dụng RE (bỏ ghi chú println () để xem cách trình đối sánh đang chia nhỏ Chuỗi), đây là mã mẫu:

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

Nó tạo ra như sau (biên dịch với javac và chạy tại dấu nhắc lệnh):

% java Two21WS Ban đầu: "ab cdef gh ij kl" Two21WS: "ab cdef gh ij kl"


8
WTF !? Tại sao bạn lại muốn làm tất cả những điều đó trong khi bạn chỉ có thể gọi điện replaceAll()?
Alan Moore
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.