c# - solo - expresiones regulares ejemplos
¿Se puede usar Regex para esta manipulación de cadenas en particular? (9)
Necesito reemplazar el carácter (decir) x con el carácter (por ejemplo) P en una cadena, pero solo si está contenido en una subcadena entre comillas. Un ejemplo lo aclara:
axbx''cxdxe''fxgh''ixj''k -> axbx''cPdPe''fxgh''iPj''k
Supongamos, en aras de la simplicidad, que las citas siempre vienen en pares.
La forma obvia es simplemente procesar la cadena un carácter a la vez (un enfoque de máquina de estado simple);
sin embargo, me pregunto si se pueden usar expresiones regulares para hacer todo el procesamiento de una vez.
Mi idioma de destino es C #, pero supongo que mi pregunta se refiere a cualquier idioma que tenga compatibilidad incorporada o de biblioteca para expresiones regulares.
Convertí el código python de Greg Hewgill a C # ¡y funcionó!
[Test]
public void ReplaceTextInQuotes()
{
Assert.AreEqual("axbx''cPdPe''fxgh''iPj''k",
Regex.Replace("axbx''cxdxe''fxgh''ixj''k",
@"x(?=[^'']*''([^'']|''[^'']*'')*$)", "P"));
}
Esa prueba pasó.
El truco es usar un grupo que no sea de captura para que coincida con la parte de la cadena que sigue a la coincidencia (carácter x ) que estamos buscando. Intentar hacer coincidir la cuerda hasta x solo encontrará la primera o la última ocurrencia, dependiendo de si se usan cuantificadores no codiciosos. Aquí está la idea de Greg transpuesta a Tcl, con comentarios.
set strIn {axbx''cxdxe''fxgh''ixj''k} set regex {(?x) # enable expanded syntax # - allows comments, ignores whitespace x # the actual match (?= # non-matching group [^'']*'' # match to end of current quoted substring ## ## assuming quotes are in pairs, ## make sure we actually were ## inside a quoted substring ## by making sure the rest of the string ## is what we expect it to be ## ( [^'']* # match any non-quoted substring | # ...or... ''[^'']*'' # any quoted substring, including the quotes )* # any number of times $ # until we run out of string :) ) # end of non-matching group } #the same regular expression without the comments set regexCondensed {(?x)x(?=[^'']*''([^'']|''[^'']*'')*$)} set replRegex {P} set nMatches [regsub -all -- $regex $strIn $replRegex strOut] puts "$nMatches replacements. " if {$nMatches > 0} { puts "Original: |$strIn|" puts "Result: |$strOut|" } exit
Esto imprime:
3 replacements.
Original: |axbx''cxdxe''fxgh''ixj''k|
Result: |axbx''cPdPe''fxgh''iPj''k|
Lamento romper tus esperanzas, pero necesitas un autómata pushdown para hacer eso. Hay más información aquí: Pushdown Autómata
En resumen, las expresiones regulares, que son máquinas de estado finito solo pueden leer y no tienen memoria, mientras que el autómata pushdown tiene una pila y capacidades de manipulación.
Editar: ortografía ...
No con expresiones regulares regulares Las expresiones regulares no tienen "memoria", por lo que no pueden distinguir entre citas "internas" o "externas".
Necesitas algo más poderoso, por ejemplo, usando gema sería directo:
''<repl>''=$0
repl:x=P
Pude hacer esto con Python:
>>> import re
>>> re.sub(r"x(?=[^'']*''([^'']|''[^'']*'')*$)", "P", "axbx''cxdxe''fxgh''ixj''k")
"axbx''cPdPe''fxgh''iPj''k"
Lo que hace es usar la coincidencia que no captura (? = ...) para verificar que el carácter x se encuentre dentro de una cadena entrecomillada. Busca algunos caracteres que no sean las comillas hasta la siguiente cita, luego busca una secuencia de caracteres individuales o grupos de caracteres citados, hasta el final de la cadena.
Esto se basa en su suposición de que las comillas siempre están equilibradas. Esto tampoco es muy eficiente.
#!/usr/bin/perl -w
use strict;
# Break up the string.
# The spliting uses quotes
# as the delimiter.
# Put every broken substring
# into the @fields array.
my @fields;
while (<>) {
@fields = split /''/, $_;
}
# For every substring indexed with an odd
# number, search for x and replace it
# with P.
my $count;
my $end = $#fields;
for ($count=0; $count < $end; $count++) {
if ($count % 2 == 1) {
$fields[$count] =~ s/a/P/g;
}
}
¿No haría este pedazo el trabajo?
Una solución más general (y más simple) que permite cotizaciones sin pares.
- Buscar cadena citada
Reemplazar ''x'' por ''P'' en la cadena
#!/usr/bin/env python import re text = "axbx''cxdxe''fxgh''ixj''k" s = re.sub("''.*?''", lambda m: re.sub("x", "P", m.group(0)), text) print s == "axbx''cPdPe''fxgh''iPj''k", s # -> True axbx''cPdPe''fxgh''iPj''k
Pattern: (?s)/G((?:^[^'']*''|(?<=.))(?:''[^'']*''|[^''x]+)*+)x
Replacement: /1P
-
/G
- Ancla cada partida al final de la anterior, o el inicio de la cadena. -
(?:^[^'']*''|(?<=.))
- Si está al principio de la cadena, haga coincidir la primera cita. -
(?:''[^'']*''|[^''x]+)*+
- Relaciona cualquier bloque de caracteres sin comillas, o cualquier caracter (sin cita) hasta una ''x''.
Un barrido a través de la cadena fuente, a excepción de un solo personaje detrás de la mirada.
Discusiones similares sobre el texto equilibrado reemplazan: ¿Se pueden usar expresiones regulares para unir patrones anidados?
Aunque puedes probar esto en Vim, pero funciona bien solo si la cadena está en una línea, y solo hay un par de ''s.
:%s:/(''[^'']*/)x/([^'']*''/):/1P/2:gci
Si hay un par más o incluso un desequilibrado, podría fallar. Así es como incluí la bandera c
confirmar en el comando ex
.
Lo mismo se puede hacer con sed, sin la interacción, o con awk
para que pueda agregar alguna interacción.
Una posible solución es romper las líneas en pares de ''
s luego puedes hacerlo con la solución vim.