Domanda Regex: uno o più spazi al di fuori di un blocco di testo racchiuso tra virgolette

StackOverflow https://stackoverflow.com/questions/263985

  •  06-07-2019
  •  | 
  •  

Domanda

Voglio sostituire qualsiasi occorrenza di più di uno spazio con un singolo spazio, ma non intraprendere alcuna azione nel testo tra virgolette.

Esiste un modo per farlo con una regex Java? In tal caso, puoi provarci o darmi un suggerimento?

È stato utile?

Soluzione

Ecco un altro approccio, che utilizza un lookahead per determinare che tutte le virgolette dopo la posizione corrente vengono visualizzate in coppie.

text = text.replaceAll("  ++(?=(?:[^\"]*+\"[^\"]*+\")*+[^\"]*+$)", " ");

Se necessario, il lookahead può essere adattato per gestire le virgolette sfuggite all'interno delle sezioni citate.

Altri suggerimenti

Quando cerchi di abbinare qualcosa che può essere contenuto in qualcos'altro, può essere utile costruire un'espressione regolare che corrisponda ad entrambi, in questo modo:

("[^"\\]*(?:\\.[^"\\]*)*")|(  +)

Questo corrisponderà a una stringa tra virgolette o due o più spazi. Poiché le due espressioni sono combinate, corrisponderà a una stringa tra virgolette OPPURE due o più spazi, ma non spazi tra virgolette. Usando questa espressione, dovrai esaminare ogni corrispondenza per determinare se è una stringa tra virgolette o due o più spazi e agire di conseguenza:

Pattern spaceOrStringRegex = Pattern.compile( "(\"[^\"\\\\]*(?:\\\\.[^\"\\\\]*)*\")|(  +)" );

StringBuffer replacementBuffer = new StringBuffer();

Matcher spaceOrStringMatcher = spaceOrStringRegex.matcher( text );

while ( spaceOrStringMatcher.find() ) 
{
    // if the space group is the match
    if ( spaceOrStringMatcher.group( 2 ) != null ) 
    {
        // replace with a single space
        spaceOrStringMatcher.appendReplacement( replacementBuffer, " " );
    }
}

spaceOrStringMatcher.appendTail( replacementBuffer );

testo tra virgolette: le virgolette sono nella stessa riga o più righe?

Tokenizzalo ed emetti un singolo spazio tra i token. Un rapido google per "tokenizer java" che gestisce le virgolette " presentarsi: questo link

YMMV

modifica: SO non ha gradito quel link. Ecco il link di ricerca di Google: Google . È stato il primo risultato.

Personalmente, non uso Java, ma questo RegExp potrebbe fare il trucco:

([^\" ])*(\\\".*?\\\")*

Provando l'espressione con RegExBuddy, genera questo codice, mi sembra perfetto:

try {
    Pattern regex = Pattern.compile("([^\" ])*(\\\".*?\\\")*", Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
    Matcher regexMatcher = regex.matcher(subjectString);
    while (regexMatcher.find()) {
        for (int i = 1; i <= regexMatcher.groupCount(); i++) {
            // matched text: regexMatcher.group(i)
            // match start: regexMatcher.start(i)
            // match end: regexMatcher.end(i)

            // I suppose here you must use something like
            // sstr += regexMatcher.group(i) + " "
        }
    }
} catch (PatternSyntaxException ex) {
    // Syntax error in the regular expression
}

Almeno, sembra funzionare bene in Python:

import re

text = """
este  es   un texto de   prueba "para ver  como se comporta  " la funcion   sobre esto
"para ver  como se comporta  " la funcion   sobre esto  "o sobre otro" lo q sea
"""

ret = ""
print text  

reobj = re.compile(r'([^\" ])*(\".*?\")*', re.IGNORECASE)

for match in reobj.finditer(text):
    if match.group() <> "":
        ret = ret + match.group() + "|"

print ret

Dopo aver analizzato il contenuto citato, esegui il resto, alla rinfusa o pezzo per pezzo, se necessario:

String text = "ABC   DEF GHI   JKL";
text = text.replaceAll("( )+", " ");
// text: "ABC DEF GHI JKL"

Jeff, sei sulla buona strada, ma ci sono alcuni errori nel tuo codice, vale a dire: (1) Hai dimenticato di sfuggire alle virgolette all'interno delle classi di caratteri negate; (2) Le parentesi all'interno del primo gruppo di cattura dovrebbero essere state della varietà non di cattura; (3) Se il secondo set di catturare le parentesi non partecipa a una partita, group (2) restituisce null, e non lo stai testando; e (4) Se si verificano due o più spazi nella regex anziché uno o più , non è necessario controllare la durata della partita in un secondo momento. Ecco il codice rivisto:

import java.util.regex.*;

public class Test
{
  public static void main(String[] args) throws Exception
  {
    String text = "blah    blah  \"boo   boo boo\"  blah  blah";
    Pattern p = Pattern.compile( "(\"[^\"\\\\]*(?:\\\\.[^\"\\\\]*)*\")|(  +)" );
    StringBuffer sb = new StringBuffer();
    Matcher m = p.matcher( text );
    while ( m.find() ) 
    {
      if ( m.group( 2 ) != null ) 
      {
        m.appendReplacement( sb, " " );
      }
    }
    m.appendTail( sb );
    System.out.println( sb.toString() );
  }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top