Domanda Regex: uno o più spazi al di fuori di un blocco di testo racchiuso tra virgolette
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?
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() );
}
}