Whitespace Matching Regex - Java


106

A API Java para expressões regulares indica que \scorresponderão aos espaços em branco. Portanto, a regex \\s\\sdeve corresponder a dois espaços.

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

O objetivo disso é substituir todas as instâncias de dois espaços em branco consecutivos por um único espaço. No entanto, isso não funciona realmente.

Estou tendo um grave mal-entendido sobre regexes ou o termo "espaço em branco"?


1
String tem uma função replaceAll que economizará algumas linhas de código. download.oracle.com/javase/1.5.0/docs/api/java/lang/String.html
Zach L

1
Não é o seu mal-entendido, mas o de Java. Experimente dividir uma string "abc \xA0 def \x85 xyz"para ver o que quero dizer: existem apenas três campos ali.
tchrist

3
Você tentou "\\ s +". Com isso, você substitui dois ou mais espaços por um.
hrzafer

Eu estive me perguntando por mais de uma hora por que minha divisão de \\ s não está se dividindo no espaço em branco. Obrigado um milhão!
Marcin

Respostas:


44

Sim, você precisa pegar o resultado de matcher.replaceAll():

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

18
Gah. Eu me sinto o maior idiota da terra. Nem eu nem duas outras pessoas pareciam notar isso. Acho que os pequenos erros mais estúpidos às vezes nos confundem, hein?

Tão verdade! Eu acho que isso acontece com os melhores deles
saibharath

O que acontecerá se eu precisar obter se o texto tiver espaços em branco.?
Gilberto Ibarra

De acordo com minha resposta abaixo, use \ p {Zs} ao invés de \ s se você quiser combinar os espaços em branco Unicode.
Robert,

194

Você não pode usar \sem Java para corresponder ao espaço em branco em seu próprio conjunto de caracteres nativos, porque o Java não oferece suporte à propriedade de espaço em branco Unicode - embora isso seja estritamente necessário para atender ao RL1.2 do UTS # 18! O que ele tem não está em conformidade com os padrões, infelizmente.

O Unicode define 26 pontos de código como \p{White_Space}: 20 deles são vários tipos de \pZ GeneralCategory = Separator e os 6 restantes são \p{Cc} GeneralCategory = Control .

O espaço em branco é uma propriedade bastante estável, e esses mesmos existem praticamente desde sempre. Mesmo assim, o Java não tem nenhuma propriedade que esteja em conformidade com o padrão Unicode para eles, então você deve usar um 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 + "]";

Agora você pode usar whitespace_charclass + "+"como padrão em seu replaceAll.


Desculpe por tudo isso. As regexes do Java simplesmente não funcionam muito bem em seu próprio conjunto de caracteres nativos e , portanto, você realmente precisa passar por obstáculos exóticos para fazê-los funcionar.

E se você acha que o espaço em branco é ruim, você deve ver o que você precisa fazer para conseguir \we \bfinalmente se comportar corretamente!

Sim, é possível e sim, é uma bagunça entorpecente. Isso é ser caridoso, até. A maneira mais fácil de obter uma biblioteca regex em conformidade com os padrões para Java é transferir JNI para as coisas do ICU. Isso é o que o Google faz para o Android, porque OraSun's não está à altura.

Se você não quiser fazer isso, mas ainda quiser ficar com o Java, eu tenho uma biblioteca de reescrita de regex de front-end que escrevi que "corrige" os padrões do Java, pelo menos para que estejam em conformidade com os requisitos de RL1.2a em UTS # 18, Expressões regulares Unicode .


12
Obrigado pelo aviso sobre as limitações de regex do Java. +1
ridgerunner de

4
Fui votar esta resposta como útil e descobri que já tinha feito isso. Então, obrigado pela segunda vez :)
Andrew Wyld

5
isso é muito antigo. é correto que isso foi corrigido em java7 com o sinalizador UNICODE_CHARACTER_CLASS? (ou usando (? U))
kritzikratzi

5
@tchrist Se isso for corrigido no java 7+, você poderia atualizar a resposta com a maneira agora correta de fazer isso?
beerbajay

7
Com Java 7, você pode fazer: "(? U) \ s" para executar o regex com conformidade com o Padrão Técnico Unicode. Ou você pode tornar o sinalizador UNICODE_CHARACTER_CLASS verdadeiro ao criar o padrão. Aqui está o documento: docs.oracle.com/javase/7/docs/api/java/util/regex/…
Didier A.

15

Para Java (não php, não javascript, nenhum outro):

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

Strings são imutáveis, portanto, você deve atribuir o resultado a algo, como 'txt = txt.replaceAll ()' Eu não votei contra sua resposta, mas pode ser por isso que outra pessoa o fez.
Conectado em

6
Eu sei que replaceAll retorna uma string, o que é importante para os programadores de Java 4 é \\ p {javaSpaceChar}
surfealokesea

2
A pergunta original cometeu o erro de não atribuir a nova string a uma variável. Apontar esse erro é, portanto, o ponto mais importante da resposta.
Conectado em

Isso resolveu totalmente o meu problema no Groovy! Finalmente! Tenho tentado todas as regex que pude encontrar que correspondam a todos os espaços em branco, incluindo NON-BREAK-SPACE (ASCII 160) !!!
Piko

5

quando enviei uma pergunta a um fórum Regexbuddy (aplicativo de desenvolvedor regex), obtive uma resposta mais exata à minha pergunta sobre Java:

"Autor da mensagem: Jan Goyvaerts

Em Java, as abreviações \ s, \ d e \ w incluem apenas caracteres ASCII. ... Este não é um bug em Java, mas simplesmente uma das muitas coisas que você precisa estar ciente ao trabalhar com expressões regulares. Para combinar todos os espaços em branco Unicode, bem como quebras de linha, você pode usar [\ s \ p {Z}] em Java. RegexBuddy ainda não oferece suporte a propriedades específicas de Java, como \ p {javaSpaceChar} (que corresponde exatamente aos mesmos caracteres que [\ s \ p {Z}]).

... \ s \ s corresponderá a dois espaços, se a entrada for apenas ASCII. O verdadeiro problema é com o código do OP, como é apontado pela resposta aceita nessa questão. ”


3
[\s\p{z}]omite o caractere Unicode "próxima linha" U + 0085. Use [\s\u0085\p{Z}].
Robert Tupelo-Schneck,

3

Parece funcionar para mim:

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

irá imprimir:

" a  b   c"

Acho que você pretendia fazer isso em vez de seu 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 sua finalidade, você pode usar este snnippet:

import org.apache.commons.lang3.StringUtils;

StringUtils.normalizeSpace(string);

Isso normalizará o espaçamento para simples e removerá os espaços em branco iniciais e finais também.

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, embora agradeça você ter dispensado seu tempo para responder, esta questão foi resolvida há vários meses. Não há necessidade de responder a perguntas tão antigas quanto esta.

6
Se alguém puder mostrar uma solução diferente e melhor, responder a perguntas antigas é perfeitamente legítimo.
james.garriss

1

Java evoluiu desde que esse problema foi levantado pela primeira vez. Você pode combinar todos os tipos de caracteres de espaço Unicode usando o \p{Zs}grupo.

Portanto, se você quiser substituir um ou mais espaços exóticos por um espaço simples, poderá fazer o seguinte:

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

Também vale a pena conhecer, se você usou a trim()função string você deve dar uma olhada no (relativamente novo) strip(), stripLeading()e stripTrailing()funções em cordas. O pode ajudá-lo a eliminar todos os tipos de caracteres de espaço em branco. Para obter mais informações sobre qual espaço está incluído, consulte a Character.isWhitespace()função Java .


-3

O uso de espaços em branco em ER é uma dor, mas acredito que funcionam. O problema do OP também pode ser resolvido usando StringTokenizer ou o método split (). No entanto, para usar RE (descomente println () para ver como o matcher está quebrando a String), aqui está um código de amostra:

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

Ele produz o seguinte (compilar com javac e executar no prompt de comando):

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


8
WTF !? Por que você faria tudo isso quando pode simplesmente ligar replaceAll()?
Alan Moore
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.