Regex coincidente de espacios en blanco - Java


106

La API de Java para expresiones regulares indica que \scoincidirán con los espacios en blanco. Entonces, la expresión regular \\s\\sdebe coincidir con dos espacios.

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

El objetivo de esto es reemplazar todas las instancias de dos espacios en blanco consecutivos con un solo espacio. Sin embargo, esto no funciona realmente.

¿Estoy teniendo un malentendido grave de las expresiones regulares o el término "espacio en blanco"?


1
String tiene una función replaceAll que le ahorrará algunas líneas de código. download.oracle.com/javase/1.5.0/docs/api/java/lang/String.html
Zach L

1
No es tu malentendido, sino el de Java. Intente dividir una cadena como "abc \xA0 def \x85 xyz"para ver lo que quiero decir: solo hay tres campos allí.
tchrist

3
¿Intentaste "\\ s +"? Con esto, reemplaza dos o más espacios por uno.
hrzafer

Me he estado preguntando durante más de una hora por qué mi división no se divide en espacios en blanco. ¡Un millón de gracias!
Marcin

Respuestas:


44

Sí, necesitas obtener el resultado de matcher.replaceAll():

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

18
Gah. Me siento como el mayor idiota del mundo. Ni yo ni otras dos personas parecíamos darnos cuenta de eso. Supongo que los errores más estúpidos a veces nos desaniman, ¿eh?

¡Tan verdadero! Supongo que eso pasa con los mejores
saibharath

¿Qué sucede si necesito obtener si el texto tiene espacios en blanco?
Gilberto Ibarra

Según mi respuesta a continuación, use \ p {Zs} en lugar de \ s si desea hacer coincidir los espacios en blanco Unicode.
Robert

194

No puede usar \sen Java para hacer coincidir el espacio en blanco en su propio conjunto de caracteres nativo, porque Java no es compatible con la propiedad de espacio en blanco Unicode, ¡aunque hacerlo es estrictamente necesario para cumplir con el RL1.2 de UTS # 18! Lo que sí tiene no es conforme con los estándares, por desgracia.

Unicode define 26 puntos de código como \p{White_Space}: 20 de ellos son varios tipos de \pZ GeneralCategory = Separator , y los 6 restantes son \p{Cc} GeneralCategory = Control .

El espacio en blanco es una propiedad bastante estable, y esos mismos han existido prácticamente desde siempre. Aun así, Java no tiene ninguna propiedad que cumpla con el estándar Unicode para estos, por lo que debe usar un código como este:

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

Ahora puede usarlo whitespace_charclass + "+"como patrón en su replaceAll.


Lo siento por todo eso. Las expresiones regulares de Java simplemente no funcionan muy bien en su propio conjunto de caracteres nativo, por lo que realmente tienes que pasar por aros exóticos para que funcionen.

Y si crees que los espacios en blanco son malos, ¡deberías ver lo que tienes que hacer para conseguir \wy \bfinalmente comportarte correctamente!

Sí, es posible, y sí, es un lío alucinante. Eso es ser caritativo, incluso. La forma más fácil de obtener una biblioteca de expresiones regulares que cumpla con los estándares para Java es pasar de JNI a las cosas de ICU. Eso es lo que hace Google para Android, porque OraSun no está a la altura.

Si no quiere hacer eso pero aún quiere seguir con Java, tengo una biblioteca de reescritura de expresiones regulares de front-end que escribí que "corrige" los patrones de Java, al menos para que se ajusten a los requisitos de RL1.2a en UTS # 18, Expresiones regulares Unicode .


12
Gracias por conocer las limitaciones de las expresiones regulares de Java. +1
ridgerunner

4
Fui a votar esta respuesta como útil y descubrí que ya lo había hecho. Así que gracias por segunda vez :)
Andrew Wyld

5
esto es muy antiguo. ¿Es correcto que esto se corrigió en java7 con el indicador UNICODE_CHARACTER_CLASS? (o usando (? U))
kritzikratzi

5
@tchrist Si esto se corrige en java 7+, ¿podría actualizar la respuesta con la forma ahora correcta de hacer esto?
Beerbajay

7
Con Java 7+ puede hacer: "(? U) \ s" para ejecutar la expresión regular con conformidad con el estándar técnico Unicode. O puede hacer que el indicador UNICODE_CHARACTER_CLASS sea verdadero al crear el patrón. Aquí está el documento: docs.oracle.com/javase/7/docs/api/java/util/regex/…
Didier A.

15

Para Java (no php, no javascript, no cualquier otro):

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

Las cadenas son inmutables, por lo tanto, debe asignar el resultado a algo, como 'txt = txt.replaceAll ()' No voté en contra de su respuesta, pero esa podría ser la razón por la que alguien más lo hizo.
Escrito el

6
Sé que replaceAll devuelve una cadena, lo importante para los programadores de Java 4 es \\ p {javaSpaceChar}
surfealokesea

2
La pregunta original cometió el error de no asignar la nueva cadena a una variable. Señalar ese error es, por tanto, el punto más importante de la respuesta.
Escrito el

¡Esto resolvió totalmente mi problema en Groovy! ¡Finalmente! ¡He estado probando todas las expresiones regulares que pude encontrar que coincidieran con todos los espacios en blanco, incluido el NON-BREAK-SPACE (ASCII 160)!
Piko

5

cuando envié una pregunta a un foro de Regexbuddy (aplicación de desarrollador de expresiones regulares), obtuve una respuesta más exacta a mi pregunta de Java:

"Autor del mensaje: Jan Goyvaerts

En Java, las abreviaturas \ s, \ d y \ w solo incluyen caracteres ASCII. ... Esto no es un error en Java, sino simplemente una de las muchas cosas que debe tener en cuenta al trabajar con expresiones regulares. Para hacer coincidir todos los espacios en blanco Unicode, así como los saltos de línea, puede usar [\ s \ p {Z}] en Java. RegexBuddy aún no admite propiedades específicas de Java como \ p {javaSpaceChar} (que coincide exactamente con los mismos caracteres que [\ s \ p {Z}]).

... \ s \ s coincidirá con dos espacios, si la entrada es solo ASCII. El verdadero problema es con el código del OP, como lo indica la respuesta aceptada en esa pregunta ".


3
[\s\p{z}]omite el carácter Unicode de "línea siguiente" U + 0085. Utilice [\s\u0085\p{Z}].
Robert Tupelo-Schneck

3

Parece funcionar para mi:

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

imprimirá:

" a  b   c"

Creo que tenías la intención de hacer esto en lugar de tu código:

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

System.out.println(result);

3

Para su propósito, puede utilizar este fragmento:

import org.apache.commons.lang3.StringUtils;

StringUtils.normalizeSpace(string);

Esto normalizará el espaciado a sencillo y también eliminará los espacios en blanco iniciales y finales.

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, aunque agradezco que se haya tomado el tiempo de responder, esta pregunta se resolvió hace varios meses. No es necesario responder a preguntas tan antiguas como esta.

6
Si alguien puede mostrar una solución diferente y mejor, responder preguntas antiguas es perfectamente legítimo.
james.garriss

1

Java ha evolucionado desde que se planteó este problema por primera vez. Puede hacer coincidir todo tipo de caracteres de espacio Unicode utilizando el \p{Zs}grupo.

Por lo tanto, si desea reemplazar uno o más espacios exóticos con un espacio simple, puede hacer esto:

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

También vale la pena saber, si ha utilizado la trim()función de cadena que debe tomar un vistazo a la (relativamente nuevo) strip(), stripLeading()y stripTrailing()funciones en las cadenas. Puede ayudarlo a recortar todo tipo de caracteres de espacios en blanco retorcidos. Para obtener más información sobre qué espacio está incluido, consulte la Character.isWhitespace()función de Java .


-3

El uso de espacios en blanco en RE es un fastidio, pero creo que funcionan. El problema del OP también se puede resolver usando StringTokenizer o el método split (). Sin embargo, para usar RE (descomente println () para ver cómo el comparador está dividiendo la cadena), aquí hay un código de muestra:

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 lo siguiente (compilar con javac y ejecutar en el símbolo del sistema):

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


8
¡¿WTF ?! ¿Por qué querrías hacer todo eso cuando simplemente puedes llamar replaceAll()?
Alan Moore
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.