Whitespace Matching Regex - Java

106

Die Java-API für reguläre Ausdrücke gibt an, dass \sLeerzeichen übereinstimmen. Der Regex \\s\\ssollte also mit zwei Leerzeichen übereinstimmen.

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

Ziel ist es, alle Instanzen von zwei aufeinanderfolgenden Leerzeichen durch ein einzelnes Leerzeichen zu ersetzen. Dies funktioniert jedoch nicht wirklich.

Habe ich ein schwerwiegendes Missverständnis von Regexen oder dem Begriff "Leerzeichen"?


quelle
1
String hat eine replaceAll-Funktion, mit der Sie einige Codezeilen speichern können. download.oracle.com/javase/1.5.0/docs/api/java/lang/String.html
Zach L
1
Es ist nicht dein Missverständnis, sondern das von Java. Versuchen Sie, eine Zeichenfolge "abc \xA0 def \x85 xyz"zu teilen, um zu sehen, was ich meine: Dort gibt es nur drei Felder.
Tchrist
3
Haben Sie versucht, "\\ s +". Damit ersetzen Sie zwei oder mehr Leerzeichen durch eins.
Hrzafer
Ich habe mich über eine Stunde lang gefragt, warum sich meine Aufteilung nicht über Leerzeichen aufteilt. Tausend Dank!
Marcin

Antworten:

44

Ja, Sie müssen das Ergebnis erfassen von matcher.replaceAll():

String result = matcher.replaceAll(" ");
System.out.println(result);
Raph Levien
quelle
18
Gah. Ich fühle mich wie der größte Idiot der Welt. Weder ich noch zwei andere Leute schienen das zu bemerken. Ich denke, die dümmsten kleinen Fehler werfen uns manchmal ab, oder?
So wahr! Ich denke, das passiert mit den Besten von ihnen
Saibharath
Was passiert, wenn ich bekommen muss, wenn der Text Leerzeichen enthält?
Gilberto Ibarra
Verwenden Sie gemäß meiner Antwort unten \ p {Zs} anstelle von \ s, wenn Sie mit Unicode-Leerzeichen übereinstimmen möchten.
Robert
193

Sie können \sin Java keine Leerzeichen für den eigenen nativen Zeichensatz verwenden, da Java die Unicode-Leerraum-Eigenschaft nicht unterstützt - obwohl dies unbedingt erforderlich ist, um RL1.2 von UTS # 18 zu erfüllen! Was es hat, ist leider nicht standardkonform.

Unicode definiert 26 Codepunkte als \p{White_Space}: 20 davon sind verschiedene Arten von \pZ GeneralCategory = Separator , und die restlichen 6 sind \p{Cc} GeneralCategory = Control .

Weißraum ist eine ziemlich stabile Eigenschaft, und diese gibt es praktisch schon immer. Trotzdem hat Java keine Eigenschaft, die dem Unicode-Standard für diese entspricht. Sie müssen stattdessen Code wie den folgenden verwenden:

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

Jetzt können Sie whitespace_charclass + "+"als Muster in Ihrem verwenden replaceAll.


Tut mir leid wegen all dem. Die regulären Ausdrücke von Java funktionieren mit ihrem eigenen nativen Zeichensatz einfach nicht sehr gut. Sie müssen also wirklich durch exotische Reifen springen, damit sie funktionieren.

Und wenn Sie denken , weißer Raum schlecht ist, sollten Sie sehen , was Sie tun müssen , um zu bekommen \wund \bendlich richtig zu verhalten!

Ja, es ist möglich und ja, es ist ein irrsinniges Durcheinander. Das ist sogar gemeinnützig. Der einfachste Weg, eine standardkonforme Regex-Bibliothek für Java zu erhalten, besteht darin, JNI auf die Intensivstation zu übertragen. Das macht Google für Android, weil OraSun's nicht mithalten kann.

Wenn Sie das nicht möchten, aber trotzdem bei Java bleiben möchten, habe ich eine von mir geschriebene Front-End-Regex-Umschreibungsbibliothek, die Javas Muster „korrigiert“, zumindest um sie an die Anforderungen von RL1.2a in UTS anzupassen # 18, Reguläre Unicode-Ausdrücke .

tchrist
quelle
12
Vielen Dank, dass Sie sich über die Regex-Einschränkungen von Java informiert haben. +1
Ridgerunner
4
Ich habe diese Antwort als hilfreich bewertet und festgestellt, dass ich sie bereits hatte. Also danke ein zweites Mal :)
Andrew Wyld
5
das ist wirklich alt. Ist es richtig, dass dies in Java7 mit dem Flag UNICODE_CHARACTER_CLASS behoben wurde? (oder mit (? U))
kritzikratzi
5
@tchrist Wenn dies in Java 7+ behoben ist, können Sie die Antwort mit der jetzt korrekten Methode aktualisieren, um dies zu tun?
Beerbajay
6
Mit Java 7+ können Sie Folgendes tun: "(? U) \ s", um den regulären Ausdruck mit Unicode Technical Standard-Konformität auszuführen. Oder Sie können das Flag UNICODE_CHARACTER_CLASS beim Erstellen des Musters auf true setzen. Hier ist das Dokument: docs.oracle.com/javase/7/docs/api/java/util/regex/…
Didier A.
15

Für Java (nicht PHP, nicht Javascript, kein anderes):

txt.replaceAll("\\p{javaSpaceChar}{2,}"," ")
surfealokesea
quelle
Zeichenfolgen sind unveränderlich, daher müssen Sie das Ergebnis etwas zuweisen, z. B. 'txt = txt.replaceAll ()'. Ich habe Ihre Antwort nicht abgelehnt, aber möglicherweise hat dies jemand anderes getan.
Enwired
6
Ich weiß, dass replaceAll einen String zurückgibt. Das Wichtigste, was 4 Java-Programmierer tun, ist \\ p {javaSpaceChar}
surfealokesea
2
Die ursprüngliche Frage machte den Fehler, die neue Zeichenfolge keiner Variablen zuzuweisen. Auf diesen Fehler hinzuweisen ist daher der wichtigste Punkt der Antwort.
Enwired
Dies hat mein Problem in Groovy total gelöst! Schließlich! Ich habe jeden regulären Ausdruck ausprobiert, den ich finden konnte und der zu allen Leerzeichen einschließlich NON-BREAK-SPACE (ASCII 160) passt !!!
Piko
5

Als ich eine Frage an ein Regexbuddy-Forum (Regex Developer Application) gesendet habe, habe ich eine genauere Antwort auf meine Java-Frage erhalten:

"Nachrichtenautor: Jan Goyvaerts

In Java enthalten die Kurzzeichen \ s, \ d und \ w nur ASCII-Zeichen. ... Dies ist kein Fehler in Java, sondern einfach eines der vielen Dinge, die Sie beachten müssen, wenn Sie mit regulären Ausdrücken arbeiten. Um alle Unicode-Leerzeichen sowie Zeilenumbrüche abzugleichen, können Sie [\ s \ p {Z}] in Java verwenden. RegexBuddy unterstützt noch keine Java-spezifischen Eigenschaften wie \ p {javaSpaceChar} (die genau den gleichen Zeichen wie [\ s \ p {Z}] entsprechen).

... \ s \ s stimmen mit zwei Leerzeichen überein, wenn die Eingabe nur ASCII ist. Das eigentliche Problem ist der OP-Code, wie aus der akzeptierten Antwort in dieser Frage hervorgeht. "

Tuomas
quelle
3
[\s\p{z}]lässt das Unicode-Zeichen "nächste Zeile" U + 0085 weg. Verwenden Sie [\s\u0085\p{Z}].
Robert Tupelo-Schneck
3

Scheint für mich zu arbeiten:

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

wird drucken:

" a  b   c"

Ich denke, Sie wollten dies anstelle Ihres Codes tun:

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
quelle
3

Für Ihren Zweck können Sie dieses Snnippet verwenden:

import org.apache.commons.lang3.StringUtils;

StringUtils.normalizeSpace(string);

Dies normalisiert den Abstand auf einfach und entfernt auch die Start- und nachfolgenden Leerzeichen.

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
quelle
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
quelle
3
Mike, obwohl ich es sehr schätze, dass Sie sich die Zeit nehmen, um zu antworten, wurde diese Frage vor einigen Monaten gelöst. So alte Fragen müssen nicht beantwortet werden.
5
Wenn jemand eine andere, bessere Lösung zeigen kann, ist die Beantwortung alter Fragen absolut legitim.
James.garriss
1

Java hat sich weiterentwickelt, seit dieses Problem zum ersten Mal angesprochen wurde. Mithilfe der \p{Zs}Gruppe können Sie alle Arten von Unicode-Leerzeichen abgleichen .

Wenn Sie also einen oder mehrere exotische Räume durch einen einfachen Raum ersetzen möchten, können Sie dies tun:

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

Auch wissenswert, wenn Sie die verwendet haben trim()String - Funktion sollten Sie einen Blick auf die (relativ neu) strip(), stripLeading()und stripTrailing()Funktionen auf Saiten. Das kann Ihnen helfen, alle Arten von Eichhörnchen-Leerzeichen abzuschneiden. Weitere Informationen dazu, welcher Speicherplatz enthalten ist, finden Sie in der Java- Character.isWhitespace()Funktion.

Robert
quelle
-3

Die Verwendung von Leerzeichen in RE ist ein Schmerz, aber ich glaube, dass sie funktionieren. Das Problem des OP kann auch mit StringTokenizer oder der split () -Methode gelöst werden. Um jedoch RE zu verwenden (kommentieren Sie println () aus, um anzuzeigen, wie der Matcher den String aufteilt), finden Sie hier einen Beispielcode:

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

Es wird Folgendes erzeugt (mit javac kompilieren und an der Eingabeaufforderung ausführen):

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

Manidip Sengupta
quelle
7
WTF!? Warum sollten Sie das alles tun wollen, wenn Sie replaceAll()stattdessen einfach anrufen können ?
Alan Moore