Regexp salta il modello

Problema

Devo sostituire tutti i simboli asterisco (‘*’) con il simbolo percentuale (‘%’). I simboli asterisco tra parentesi quadre dovrebbero essere ignorati.

Esempio

[Test] public void Replace_all_asterisks_outside_the_square_brackets() { var input = "Hel[*o], w*rld!"; var output = Regex.Replace(input, "What_pattern_should_be_there?", "%") Assert.AreEqual("Hel[*o], w%rld!", output)); } 

Prova a guardare avanti:

 \*(?![^\[\]]*\]) 

Ecco una soluzione un po ‘più forte, che si occupa meglio dei blocchi [] e persino di caratteri di escape \[ caratteri:

 string text = @"h*H\[el[*o], w*rl\]d!"; string pattern = @" \\. # Match an escaped character. (to skip over it) | \[ # Match a character class (?:\\.|[^\]])* # which may also contain escaped characters (to skip over it) \] | (?\*) # Match `*` and add it to a group. "; text = Regex.Replace(text, pattern, match => match.Groups["Asterisk"].Success ? "%" : match.Value, RegexOptions.IgnorePatternWhitespace); 

Se non ti preoccupi dei personaggi sfuggiti, puoi semplificarlo per:

 \[ # Skip a character class [^\]]* # until the first ']' \] | (?\*) 

Che può essere scritto senza commenti come: @"\[[^\]]*\]|(?\*)" .

Per capire perché funziona, dobbiamo capire come funziona Regex.Replace: per ogni posizione nella stringa, cerca di far corrispondere la regex. Se fallisce, muove un personaggio . Se riesce, si sposta sull’intera partita.
Qui, abbiamo partite fittizie per i [...] blocchi in modo da poter saltare gli asterischi che non vogliamo sostituire, e abbinare solo quelli solitari. Tale decisione viene presa in una funzione di callback che controlla se Asterisk stato abbinato o meno.

Non sono riuscito a trovare una soluzione RegEx pura. Quindi ti fornisco una soluzione pragmatica. L’ho provato e funziona:

 [Test] public void Replace_all_asterisks_outside_the_square_brackets() { var input = "H*]e*l[*o], w*rl[*d*o] [o*] [o*o]."; var actual = ReplaceAsterisksNotInSquareBrackets(input); var expected = "H%]e%l[*o], w%rl[*d*o] [o*] [o*o]."; Assert.AreEqual(expected, actual); } private static string ReplaceAsterisksNotInSquareBrackets(string s) { Regex rx = new Regex(@"(?< =\[[^\[\]]*)(?\*)(?=[^\[\]]*\])"); var matches = rx.Matches(s); s = s.Replace('*', '%'); foreach (Match match in matches) { s = s.Remove(match.Groups["asterisk"].Index, 1); s = s.Insert(match.Groups["asterisk"].Index, "*"); } return s; } 

MODIFICATO

Va bene ecco il mio ultimo tentativo;)

Usando lookbehind negativo (?< !) E lookahead negativo (?!) .

 var output = Regex.Replace(input, @"(?< !\[)\*(?!\])", "%"); 

Questo passa anche il test nel commento ad un'altra risposta "Hel*o], w*rld!"