Regex di corrispondenza degli spazi vuoti - Java


106

L'API Java per le espressioni regolari indica che \scorrisponderanno agli spazi. Quindi la regex \\s\\sdovrebbe corrispondere a due spazi.

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

Lo scopo è sostituire tutte le istanze di due spazi bianchi consecutivi con un unico spazio. Tuttavia questo non funziona effettivamente.

Sto avendo un grave fraintendimento delle regex o del termine "spazi bianchi"?


1
String ha una funzione replaceAll che ti farà risparmiare poche righe di codice. download.oracle.com/javase/1.5.0/docs/api/java/lang/String.html
Zach L

1
Non è un tuo malinteso, ma di Java. Prova a dividere una stringa "abc \xA0 def \x85 xyz"per vedere cosa intendo: ci sono solo tre campi lì.
tchrist

3
Hai provato "\\ s +". Con questo sostituisci due o più spazi con uno.
hrzafer

Mi chiedo da più di un'ora perché la mia divisione non si divide su spazi bianchi. Grazie mille!
Marcin

Risposte:


44

Sì, devi prendere il risultato di matcher.replaceAll():

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

18
Gah. Mi sento il più grande idiota della terra. Né io né altre due persone sembravamo accorgercene. Immagino che i piccoli errori più stupidi a volte ci buttino fuori, eh?

Così vero! Immagino che ciò accada con i migliori di loro
saibharath

Cosa succede se devo ottenere se il testo ha spazi bianchi.?
Gilberto Ibarra

Secondo la mia risposta di seguito, usa \ p {Zs} invece di \ s se vuoi far corrispondere gli spazi unicode.
Robert

194

Non è possibile utilizzarlo \sin Java per abbinare lo spazio bianco sul proprio set di caratteri nativi, perché Java non supporta la proprietà dello spazio bianco Unicode, anche se è strettamente necessario per soddisfare RL1.2 di UTS # 18! Quello che ha non è conforme agli standard, ahimè.

Unicode definisce 26 punti di codice come \p{White_Space}: 20 di loro sono vari tipi di \pZ GeneralCategory = Separator , e i restanti 6 sono\p{Cc} GeneralCategory = Control .

Lo spazio bianco è una proprietà piuttosto stabile e quelle stesse esistono praticamente da sempre. Anche così, Java non ha proprietà conformi allo standard Unicode per questi, quindi devi invece usare codice come questo:

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

Ora puoi usare whitespace_charclass + "+" come modello nel tuo file replaceAll.


Mi dispiace per tutto questo. Le regex di Java semplicemente non funzionano molto bene sul proprio set di caratteri nativi, quindi devi davvero saltare attraverso cerchi esotici per farle funzionare.

E se pensi che lo spazio bianco sia negativo, dovresti vedere cosa devi fare per ottenerlo \w e \bfinalmente comportarti correttamente!

Sì, è possibile, e sì, è un casino stupefacente. Anche questo è caritatevole. Il modo più semplice per ottenere una libreria regex conforme agli standard per Java è utilizzare JNI per le cose di ICU. Questo è quello che fa Google per Android, perché quello di OraSun non è all'altezza.

Se non vuoi farlo ma vuoi comunque restare con Java, ho una libreria di riscrittura regex front-end che ho scritto che "corregge" i pattern di Java, almeno per renderli conformi ai requisiti di RL1.2a in UTS # 18, espressioni regolari Unicode .


12
Grazie per la testa sui limiti di regex di Java. +1
ridgerunner

4
Sono andato a votare questa risposta come utile e ho scoperto di averlo già fatto. Quindi grazie una seconda volta :)
Andrew Wyld

5
questo è davvero vecchio. è corretto che questo è stato risolto in java7 con il flag UNICODE_CHARACTER_CLASS? (o usando (? U))
kritzikratzi

5
@tchrist Se questo problema è stato risolto in java 7+, potresti aggiornare la risposta con il modo corretto per farlo?
beerbajay

7
Con Java 7+ puoi fare: "(? U) \ s" per eseguire la regex con conformità allo standard tecnico Unicode. Oppure puoi rendere vero il flag UNICODE_CHARACTER_CLASS quando crei il pattern. Ecco il documento: docs.oracle.com/javase/7/docs/api/java/util/regex/…
Didier A.

15

Per Java (non php, non javascript, non altri):

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

Le stringhe sono immutabili, quindi devi assegnare il risultato a qualcosa, come 'txt = txt.replaceAll ()' Non ho votato per difetto, ma questo potrebbe essere il motivo per cui qualcun altro lo ha fatto.
Registrato il

6
So che replaceAll restituisce una stringa la cosa importante per 4 programmatori java è \\ p {javaSpaceChar}
surfealokesea

2
La domanda originale ha commesso l'errore di non assegnare la nuova stringa a una variabile. Sottolineare quell'errore è quindi il punto più importante della risposta.
Registrato il

Questo ha completamente risolto il mio problema in Groovy! Finalmente! Ho provato ogni regex che ho trovato che corrispondesse a tutto lo spazio bianco incluso NON-BREAK-SPACE (ASCII 160) !!!
Piko

5

quando ho inviato una domanda a un forum Regexbuddy (applicazione per sviluppatori regex), ho ottenuto una risposta più precisa alla mia domanda su Java:

"Autore del messaggio: Jan Goyvaerts

In Java, le scorciatoie \ s, \ d e \ w includono solo caratteri ASCII. ... Questo non è un bug in Java, ma semplicemente una delle tante cose di cui devi essere consapevole quando lavori con le espressioni regolari. Per abbinare tutti gli spazi Unicode e le interruzioni di riga, puoi utilizzare [\ s \ p {Z}] in Java. RegexBuddy non supporta ancora le proprietà specifiche di Java come \ p {javaSpaceChar} (che corrisponde esattamente agli stessi caratteri di [\ s \ p {Z}]).

... \ s \ s corrisponderà a due spazi, se l'input è solo ASCII. Il vero problema è con il codice dell'OP, come viene sottolineato dalla risposta accettata in quella domanda. "


3
[\s\p{z}]omette il carattere "riga successiva" Unicode U + 0085. Usa [\s\u0085\p{Z}].
Robert Tupelo-Schneck,

3

Sembra funzionare per me:

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

stamperà:

" a  b   c"

Penso che intendevi farlo al posto del tuo codice:

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

System.out.println(result);

3

Per il tuo scopo puoi usare questo snnippet:

import org.apache.commons.lang3.StringUtils;

StringUtils.normalizeSpace(string);

Questo normalizzerà la spaziatura a singola e rimuoverà anche gli spazi bianchi iniziali e finali.

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, mentre apprezzo che tu abbia dedicato del tempo per rispondere, questa domanda è stata risolta diversi mesi fa. Non è necessario rispondere a domande vecchie come questa.

6
Se qualcuno può mostrare una soluzione diversa e migliore, rispondere a vecchie domande è perfettamente legittimo.
james.garriss

1

Java si è evoluto da quando questo problema è stato sollevato per la prima volta. Puoi abbinare tutti i tipi di caratteri di spazio Unicode utilizzando il \p{Zs}gruppo.

Quindi, se volessi sostituire uno o più spazi esotici con uno spazio semplice, potresti farlo:

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

Inoltre vale la pena conoscere, se hai utilizzato la trim()funzione di stringa si dovrebbe dare uno sguardo alla (relativamente nuovo) strip(), stripLeading()e le stripTrailing()funzioni sulle stringhe. Può aiutarti a tagliare tutti i tipi di caratteri di spazio bianco scoppiettanti. Per ulteriori informazioni su cosa è incluso lo spazio, vedere la Character.isWhitespace()funzione Java .


-3

L'uso degli spazi bianchi in RE è un problema, ma credo che funzionino. Il problema dell'OP può anche essere risolto usando StringTokenizer o il metodo split (). Tuttavia, per utilizzare RE (rimuovere il commento da println () per vedere come il matcher sta spezzando la stringa), ecco un codice di esempio:

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

Produce quanto segue (compilare con javac ed eseguire al prompt dei comandi):

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


8
WTF !? Perché vorresti fare tutto questo quando puoi semplicemente chiamare replaceAll()?
Alan Moore
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.