Dopasowanie do białych znaków Regex - Java

106

Interfejs API języka Java dla wyrażeń regularnych określa, które \sbędą pasować do białych znaków. Zatem wyrażenie regularne \\s\\spowinno pasować do dwóch spacji.

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

Celem tego jest zastąpienie wszystkich wystąpień dwóch kolejnych białych znaków pojedynczą spacją. Jednak to w rzeczywistości nie działa.

Czy mam poważne niezrozumienie wyrażeń regularnych lub terminu „białe spacje”?


źródło
1
Ciąg ma funkcję replaceAll, która pozwala zaoszczędzić kilka wierszy kodu. download.oracle.com/javase/1.5.0/docs/api/java/lang/String.html
Zach L
1
To nie jest twoje nieporozumienie, ale Java. Spróbuj podzielić ciąg, "abc \xA0 def \x85 xyz"aby zobaczyć, co mam na myśli: są tam tylko trzy pola.
tchrist
3
Czy próbowałeś „\\ s +”. W ten sposób zamieniasz dwie lub więcej spacji na jedną.
hrzafer
Od ponad godziny zastanawiam się, dlaczego mój podział nie rozdziela się na białe znaki. Stukrotne dzięki!
Marcin

Odpowiedzi:

44

Tak, musisz pobrać wynik matcher.replaceAll():

String result = matcher.replaceAll(" ");
System.out.println(result);
Raph Levien
źródło
18
Gah. Czuję się jak największy idiota na ziemi. Ani ja, ani dwie inne osoby nie wydawały się tego zauważać. Wydaje mi się, że najgłupsze małe błędy czasami nas zrzucają, co?
Tak prawdziwe! Myślę, że tak się dzieje z najlepszym z nich
saibharath
Co się stanie, jeśli będę potrzebować, jeśli tekst zawiera białe spacje?
Gilberto Ibarra,
Zgodnie z moją odpowiedzią poniżej użyj \ p {Zs} zamiast \ s, jeśli chcesz dopasować białe znaki Unicode.
Robert
194

Nie można używać \sw Javie do dopasowywania białych znaków we własnym natywnym zestawie znaków, ponieważ Java nie obsługuje właściwości białych znaków Unicode - nawet jeśli jest to ściśle wymagane, aby spełnić wymagania RL1.2 UTS # 18! Niestety, to nie jest zgodne ze standardami.

Unicode definiuje 26 punktów kodowych jako \p{White_Space}: 20 z nich to różne rodzaje \pZ GeneralCategory = Separator , a pozostałe 6 to \p{Cc} GeneralCategory = Control .

Biała przestrzeń jest dość stabilną właściwością, a te same istnieją praktycznie od zawsze. Mimo to Java nie ma dla nich właściwości zgodnych ze standardem Unicode, więc zamiast tego musisz użyć takiego kodu:

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

Teraz możesz użyć whitespace_charclass + "+"jako wzoru w swoim replaceAll.


Przepraszam za to wszystko. Wyrażenia regularne Javy po prostu nie działają zbyt dobrze w swoim własnym natywnym zestawie znaków, więc naprawdę musisz przeskakiwać przez egzotyczne obręcze, aby działały.

A jeśli uważasz, biała przestrzeń jest źle, trzeba zobaczyć, co trzeba zrobić, aby dostać się \wi \bw końcu zachowywać się poprawnie!

Tak, jest to możliwe i tak, to przytłaczający bałagan. To nawet charytatywność. Najłatwiejszym sposobem uzyskania biblioteki regex zgodnej ze standardami dla Javy jest przejście przez JNI do rzeczy ICU. To właśnie robi Google dla Androida, ponieważ OraSun nie spełnia wymagań.

Jeśli nie chcesz tego robić, ale nadal chcesz pozostać przy Javie, mam bibliotekę do przepisywania wyrażeń regularnych typu front-end, napisałem, że „naprawia” wzorce Javy, przynajmniej po to, aby były zgodne z wymaganiami RL1.2a w UTS # 18, Wyrażenia regularne Unicode .

tchrist
źródło
12
Dzięki za zrozumienie ograniczeń wyrażenia regularnego Javy. +1
ridgerunner
4
Poszedłem zagłosować na tę odpowiedź jako pomocną i stwierdziłem, że już ją otrzymałem. Więc dziękuję po raz drugi :)
Andrew Wyld
5
to jest naprawdę stare. czy to prawda, że ​​zostało to naprawione w java7 z flagą UNICODE_CHARACTER_CLASS? (lub używając (? U))
kritzikratzi
5
@tchrist Jeśli problem został rozwiązany w java 7+, czy mógłbyś zaktualizować odpowiedź, podając poprawny sposób, aby to zrobić?
beerbajay
7
W Javie 7+ możesz wykonać: „(? U) \ s”, aby uruchomić wyrażenie regularne zgodne ze standardem technicznym Unicode. Lub możesz ustawić flagę UNICODE_CHARACTER_CLASS jako prawdziwą podczas tworzenia wzorca. Oto dokument: docs.oracle.com/javase/7/docs/api/java/util/regex/…
Didier A.
15

W przypadku Javy (nie php, nie javascript, nic innego):

txt.replaceAll("\\p{javaSpaceChar}{2,}"," ")
surfealokesea
źródło
Ciągi znaków są niezmienne, dlatego musisz przypisać wynik do czegoś, na przykład „txt = txt.replaceAll ()”. Nie głosowałem na twoją odpowiedź, ale może dlatego ktoś inny to zrobił.
Enwired
6
Wiem, że replaceAll zwraca ciąg, ważną rzeczą dla 4 programistów java jest \\ p {javaSpaceChar}
surfealokesea
2
Pierwotne pytanie popełniło błąd, nie przypisując nowego ciągu do zmiennej. Wskazanie na ten błąd jest zatem najważniejszym punktem odpowiedzi.
Enwired
To całkowicie rozwiązało mój problem w Groovy! Wreszcie! Próbowałem każdego wyrażenia regularnego, jakie mogłem znaleźć, pasującego do wszystkich białych znaków, w tym NON-BREAK-SPACE (ASCII 160) !!!
Piko
5

kiedy wysłałem pytanie na forum Regexbuddy (aplikacja dla programistów regex), otrzymałem dokładniejszą odpowiedź na moje pytanie dotyczące języka Java:

„Autor wiadomości: Jan Goyvaerts

W Javie skróty \ s, \ d i \ w zawierają tylko znaki ASCII. ... To nie jest błąd w Javie, ale po prostu jedna z wielu rzeczy, o których należy pamiętać podczas pracy z wyrażeniami regularnymi. Aby dopasować wszystkie białe znaki Unicode, a także podziały wierszy, możesz użyć [\ s \ p {Z}] w Javie. RegexBuddy nie obsługuje jeszcze właściwości specyficznych dla języka Java, takich jak \ p {javaSpaceChar} (które dopasowują dokładnie te same znaki co [\ s \ p {Z}]).

... \ s \ s dopasuje dwie spacje, jeśli dane wejściowe są tylko ASCII. Prawdziwy problem dotyczy kodu PO, na co wskazuje przyjęta odpowiedź na to pytanie ”.

Tuomas
źródło
3
[\s\p{z}]pomija znak „następnej linii” Unicode U + 0085. Użyj [\s\u0085\p{Z}].
Robert Tupelo-Schneck
3

Wydaje się, że działa dla mnie:

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

wydrukuje:

" a  b   c"

Myślę, że zamierzałeś to zrobić zamiast swojego kodu:

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
źródło
3

W swoim celu możesz użyć tego snnippet:

import org.apache.commons.lang3.StringUtils;

StringUtils.normalizeSpace(string);

Spowoduje to znormalizowanie odstępów do pojedynczych i usunięcie początkowych i końcowych białych znaków.

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
źródło
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;
 }
}
Mikrofon
źródło
3
Mike, chociaż doceniam, że poświęciłeś czas na udzielenie odpowiedzi, to pytanie zostało rozwiązane kilka miesięcy temu. Nie ma potrzeby odpowiadania na tak stare pytania.
6
Jeśli ktoś może wskazać inne, lepsze rozwiązanie, odpowiadanie na stare pytania jest całkowicie słuszne.
james.garriss
1

Java ewoluowała od momentu poruszenia tego problemu. Korzystając z \p{Zs}grupy, można dopasować wszystkie rodzaje znaków spacji Unicode .

Dlatego jeśli chcesz zastąpić jedną lub więcej egzotycznych przestrzeni zwykłą przestrzenią, możesz to zrobić:

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

Warto również wiedzieć, jeśli używałeś trim()funkcji ciąg należy spojrzeć na przycisk (stosunkowo nowy) strip(), stripLeading()oraz stripTrailing()funkcje na strunach. Może pomóc ci odciąć wszelkiego rodzaju zawijasko białe znaki. Aby uzyskać więcej informacji o tym, jaka przestrzeń jest uwzględniona, zobacz Character.isWhitespace()funkcję Java .

Robert
źródło
-3

Używanie białych znaków w RE jest uciążliwe, ale uważam, że działają. Problem OP można również rozwiązać za pomocą StringTokenizer lub metody split (). Jednak aby użyć RE (odkomentować println (), aby zobaczyć, jak dopasowujący rozbija ciąg), oto przykładowy kod:

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

Tworzy to, co następuje (skompiluj za pomocą javac i uruchom w wierszu poleceń):

% java Two21WS Początkowe: "ab cdef gh ij kl" Two21WS: "ab cdef gh ij kl"

Manidip Sengupta
źródło
8
WTF !? Dlaczego miałbyś chcieć to wszystko robić, skoro replaceAll()zamiast tego możesz po prostu zadzwonić ?
Alan Moore