En un archivo de texto, quiero eliminar ,
(comas) y también "
(comillas) (solo si las comillas dobles contienen números separados por comas).
56,72,"12,34,54",x,y,"foo,a,b,bar"
Salida esperada
56,72,123454,x,y,"foo,a,b,bar"
Nota:Muestro la línea anterior solo como un ejemplo. Mi archivo de texto contiene muchas líneas como las anteriores y los números separados por comas presentes entre comillas dobles deben variar. Eso es,
56,72,"12,34,54",x,y,"foo,a,b,bar"
56,92,"12,34",x,y,"foo,a,b,bar"
56,72,"12,34,54,78,76,54,67",x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar","12,34,54"
56,72,x,y,"foo,a,b,bar","12,34,54","45,57,84,92","bar,foo"
Rendimiento esperado:
56,72,123454,x,y,"foo,a,b,bar"
56,92,1234,x,y,"foo,a,b,bar"
56,72,12345478765467,x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar",123454
56,72,x,y,"foo,a,b,bar",123454,45578492,"bar,foo"
Hay n
varios números presentes entre comillas dobles separados por comas. Y también deje las comillas dobles que contienen caracteres tal como están.
Me encanta sed
la herramienta de procesamiento de texto. Me alegraría que publicaras alguna sed
solución para esto.
Respuesta1
Si Perl está bien, aquí hay una forma breve (y probablemente rápida, aunque no necesariamente simple :)) de hacerlo:
perl -pe 's:"(\d[\d,]+)":$1=~y/,//dr:eg' file
El e
indicador del s:::
operador (que es simplemente otra forma de escribir s///
) hace que el reemplazo se trate como una expresión que se evalúa cada vez. Esa expresión toma la $1
captura de la expresión regular (a la que ya le faltan las comillas) y la traduce ( y///
, que también se puede escribir como tr///
) eliminando ( /d
) todas las comas. La r
bandera to y
es necesaria para que el valor sea la cadena traducida, en lugar del recuento de traducciones.
Para aquellos que de alguna manera se sienten mancillados por Perl, aquí está el equivalente en Python. En realidad, Python no es una herramienta de shell de una sola línea, pero a veces se le puede engatusar para que coopere. Lo siguiente se puede escribir como una línea (a diferencia de for
los bucles, que no se pueden escribir), pero el desplazamiento horizontal lo hace (aún más) ilegible:
python -c '
import re;
import sys;
r=re.compile("\"(\d+(,\d+)*)\"");
all(not sys.stdout.write(r.sub(lambda m:m.group(1).replace(",",""),l))
for l in sys.stdin)
' < file
Respuesta2
Esto (adaptado deaquí) debería hacer lo que necesitas, aunque el Perl de @rici es mucho más simple:
$ sed -r ':a;s/(("[0-9,]*",?)*"[0-9,]*),/\1/;ta; s/""/","/g;
s/"([0-9]*)",?/\1,/g ' file
56,72,123454,x,y,"foo,a,b,bar"
56,92,1234,x,y,"foo,a,b,bar"
56,72,12345478765467,x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar",123454,
56,72,x,y,"foo,a,b,bar",123454,45578492,"bar,foo"
Explicación
:a
: define una etiqueta llamadaa
.s/(("[0-9,]*",?)*"[0-9,]*),/\1/
: Este necesita ser descompuesto- En primer lugar, utilizando esta construcción
(foo(bar))
:\1
seráfoobar
y\2
serábar
. "[0-9,]*",?
: coincide con 0 o más de0-9
o,
, seguido de 0 o 1,
.("[0-9,]*",?)*
: coincide con 0 o más de los anteriores."[0-9,]*
: coincide con 0 o más de0-9
o,
que vienen justo después de un"
- En primer lugar, utilizando esta construcción
ta;
: vuelve a la etiquetaa
y ejecuta de nuevosila sustitución fue exitosa.s/""/","/g;
: Postprocesamiento. Reemplazar""
con","
.s/"([0-9]*)",?/\1,/g
: elimina todas las comillas alrededor de los números.
Esto podría ser más fácil de entender con otro ejemplo:
$ echo '"1,2,3,4"' | sed -nr ':a;s/(("[0-9,]*",?)*"[0-9,]*),/\1/;p;ta;'
"1,2,34"
"1,234"
"1234"
"1234"
Entonces, si bien puedes encontrar un número que está justo después de una comilla y seguido de una coma y otro número, une los dos números y repite el proceso hasta que ya no sea posible.
En este punto creo que es útil mencionar una cita que info sed
aparece en la sección que describe funciones avanzadas, como la etiqueta utilizada anteriormente (gracias por encontrar a @Braiam):
En la mayoría de los casos, el uso de estos comandos indica que probablemente sea mejor programar en algo como `awk' o Perl.
Respuesta3
Para datos CSV, usaría un lenguaje con un analizador CSV real. Por ejemplo con Rubí:
ruby -rcsv -pe '
row = CSV::parse_line($_).map {|e| e.delete!(",") if e =~ /^[\d,]+$/; e}
$_ = CSV::generate_line(row)
' <<END
56,72,"12,34,54",x,y,"foo,a,b,bar"
56,92,"12,34",x,y,"foo,a,b,bar"
56,72,"12,34,54,78,76,54,67",x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar","12,34,54"
56,72,x,y,"foo,a,b,bar","12,34,54","45,57,84,92","bar,foo"
END
56,72,123454,x,y,"foo,a,b,bar"
56,92,1234,x,y,"foo,a,b,bar"
56,72,12345478765467,x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar",123454
56,72,x,y,"foo,a,b,bar",123454,45578492,"bar,foo"
Respuesta4
Usandorakú(anteriormente conocido como Perl_6)
~$ raku -pe 's:g/ \" ~ \" (\d+) ** 2..* % "," /{$0.join}/;' file
Entrada de muestra:
56,72,"12,34,54",x,y,"foo,a,b,bar"
56,92,"12,34",x,y,"foo,a,b,bar"
56,72,"12,34,54,78,76,54,67",x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar","12,34,54"
56,72,x,y,"foo,a,b,bar","12,34,54","45,57,84,92","bar,foo"
Salida de muestra:
56,72,123454,x,y,"foo,a,b,bar"
56,92,1234,x,y,"foo,a,b,bar"
56,72,12345478765467,x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar",123454
56,72,x,y,"foo,a,b,bar",123454,45578492,"bar,foo"
Raku es un lenguaje de programación de la familia Perl con una serie de potentes funciones de expresiones regulares. Consulte la URL a continuación para obtener una descripción general de esta respuesta:
https://unix.stackexchange.com/a/722570/227738
En el código anterior, se reconocen los dígitos y se eliminan las comas incrustadas. La expresión regular aprovecha el hecho de queestructuras anidadasse puede denotar con la nueva notación ~ tilde (anidada) de Raku, de modo que \" ~ \" [\d+]
significa "uno o más dígitos entre "comillas dobles".
Además,estructuras repetidasse puede denotar con el nuevo %
cuantificador modificado de Raku para estructuras repetidas. La notación [\d+] ** 2..* % "," significa "uno o más dígitos separados por ,
comas con este patrón repetido ** 2..*
dos o más veces. [Si hay un separador final (p. ej. coma), use a %%
en lugar de %
en la sintaxis].
Esto es sólo un comienzo. Los archivos CSV con separadores alternativos, nuevas líneas incrustadas, comas incrustadas, campos potencialmente en blanco, etc., realmente necesitan ser manejados por un verdadero analizador CSV como Text::CSV
el módulo de Raku. Consulte los enlaces a continuación para obtener más detalles.
https://docs.raku.org/language/regexes
https://raku.land/github:Tux/Text::CSV
https://raku.org