Analice un csv usando awk e ignorando las comas dentro de un campo
(7)
Tengo un archivo csv donde cada fila define una habitación en un edificio determinado. Junto con la sala, cada fila tiene un campo de piso. Lo que quiero extraer son todos los pisos en todos los edificios.
Mi archivo se ve así ...
"u_floor","u_room","name"
0,"00BDF","AIRPORT TEST "
0,0,"BRICKER HALL, JOHN W "
0,3,"BRICKER HALL, JOHN W "
0,5,"BRICKER HALL, JOHN W "
0,6,"BRICKER HALL, JOHN W "
0,7,"BRICKER HALL, JOHN W "
0,8,"BRICKER HALL, JOHN W "
0,9,"BRICKER HALL, JOHN W "
0,19,"BRICKER HALL, JOHN W "
0,20,"BRICKER HALL, JOHN W "
0,21,"BRICKER HALL, JOHN W "
0,25,"BRICKER HALL, JOHN W "
0,27,"BRICKER HALL, JOHN W "
0,29,"BRICKER HALL, JOHN W "
0,35,"BRICKER HALL, JOHN W "
0,45,"BRICKER HALL, JOHN W "
0,59,"BRICKER HALL, JOHN W "
0,60,"BRICKER HALL, JOHN W "
0,61,"BRICKER HALL, JOHN W "
0,63,"BRICKER HALL, JOHN W "
0,"0006M","BRICKER HALL, JOHN W "
0,"0008A","BRICKER HALL, JOHN W "
0,"0008B","BRICKER HALL, JOHN W "
0,"0008C","BRICKER HALL, JOHN W "
0,"0008D","BRICKER HALL, JOHN W "
0,"0008E","BRICKER HALL, JOHN W "
0,"0008F","BRICKER HALL, JOHN W "
0,"0008G","BRICKER HALL, JOHN W "
0,"0008H","BRICKER HALL, JOHN W "
Lo que quiero es todos los pisos en todos los edificios.
Estoy usando cat, awk, sort y uniq para obtener esta lista, aunque estoy teniendo un problema con el "," en el campo del nombre del edificio como "BRICKER HALL, JOHN W" y está generando toda mi generación de csv.
cat Buildings.csv | awk -F, ''{print $1","$2}'' | sort | uniq > Floors.csv
¿Cómo puedo obtener awk para usar la coma pero ignorar una coma entre "" de un campo? Alternativamente, ¿alguien tiene una mejor solución?
En base a la respuesta proporcionada sugiriendo un analizador cvs awk, pude obtener la solución:
cat Buildings.csv | awk -f csv.awk | awk -F" -> 2|" ''{print $2}'' | awk -F"|" ''{print $2","$3}'' | sort | uniq > floors.csv
Ahí queremos usar el programa csv awk y desde allí quiero usar un "-> 2 |" que es el formato basado en el programa csv awk. La impresión $ 2 allí imprime únicamente el contenido analizado csv, esto es porque el programa imprime la línea original seguida de "-> #" donde # es el conteo analizado desde csv. (Es decir, las columnas.) A partir de ahí puedo dividir este resultado awk csv en el "|" que es con lo que reemplaza la coma. ¡Entonces el género, uniq y pipe a un archivo y listo!
Gracias por la ayuda.
Analizadores de CSV completos como Perl''s Text::CSV_XS
están especialmente diseñados para manejar ese tipo de rareza.
perl -MText::CSV_XS -lne ''BEGIN{$csv=Text::CSV_XS->new()} if($csv->parse($_)){ @f=$csv->fields(); print "$f[0],$f[1]" }'' file
La línea de entrada se divide en array @f
El campo 1 es $f[0]
ya que Perl comienza a indexar a 0
salida:
u_floor,u_room
0,00BDF
0,0
0,3
0,5
0,6
0,7
0,8
0,9
0,19
0,20
0,21
0,25
0,27
0,29
0,35
0,45
0,59
0,60
0,61
0,63
0,0006M
0,0008A
0,0008B
0,0008C
0,0008D
0,0008E
0,0008F
0,0008G
0,0008H
Proporcioné más explicación de Text::CSV_XS
en mi respuesta aquí: analizar el archivo csv usando gawk
Dado que el problema es realmente distinguir entre una coma dentro de un campo CSV y la que separa los campos, podemos reemplazar el primer tipo de coma con otra cosa para que sea más fácil analizar más, es decir, algo como esto:
0,"00BDF","AIRPORT TEST "
0,0,"BRICKER HALL<comma> JOHN W "
Este script gawk (replace-comma.awk) hace eso:
BEGIN { RS = "(.)" }
RT == "/x022" { inside++; }
{ if (inside % 2 && RT == ",") printf("<comma>"); else printf(RT); }
Esto utiliza una característica de gawk que captura el separador de registro real en una variable llamada RT
. Divide cada carácter en un registro, y mientras leemos los registros, reemplazamos la coma encontrada dentro de una cita ( /x022
) con <comma>
.
La solución FPAT falla en un caso especial en el que tiene tanto comillas escapadas como comas dentro de comillas, pero esta solución funciona en todos los casos, es decir,
§ echo ''"Adams, John ""Big Foot""",1'' | gawk -vFPAT=''[^,]*|"[^"]*"'' ''{ print $1 }''
"Adams, John "
§ echo ''"Adams, John ""Big Foot""",1'' | gawk -f replace-comma.awk | gawk -F, ''{ print $1; }''
"Adams<comma> John ""Big Foot""",1
Como una línea para copiar y pegar fácilmente:
gawk ''BEGIN { RS = "(.)" } RT == "/x022" { inside++; } { if (inside % 2 && RT == ",") printf("<comma>"); else printf(RT); }''
El resultado extra que obtiene de csv.awk
proviene del código de demostración. Se pretende que use las funciones dentro del script para realizar el análisis sintáctico y luego lo muestre como lo desee.
Al final de csv.awk
está el { ... }
ciclo que muestra una de las funciones. Es ese código que está produciendo el -> 2|
.
En vez de eso, solo llame a la función de análisis e print csv[1], csv[2]
.
Esa parte del código se vería así:
{
num_fields = parse_csv($0, csv, ",", "/"", "/"", "//n", 1);
if (num_fields < 0) {
printf "ERROR: %s (%d) -> %s/n", csverr, num_fields, $0;
} else {
# printf "%s -> ", $0;
# printf "%s", num_fields;
# for (i = 0;i < num_fields;i++) {
# printf "|%s", csv[i];
# }
# printf "|/n";
print csv[1], csv[2]
}
}
your_script
como your_script
(por ejemplo).
Haz chmod +x your_script
.
Y el cat
es innecesario. Además, puedes sort -u
lugar de sort | uniq
sort | uniq
.
Su comando se vería así:
./yourscript Buildings.csv | sort -u > floors.csv
Mi solución es quitar comas de la csv usando:
decommaize () {
cat $1 | sed ''s/"[^"]*"/"((&))"/g'' | sed ''s//(/"((/"/)/([^",]*/)/(,/)/([^",]*/)/(/"))/"/)/"/2/4"/g'' | sed ''s/"(("/"/g'' | sed ''s/"))"/"/g'' > $2
}
Es decir, primero sustituya las comillas de apertura por "((" y comillas de cierre con "))", luego sustituya "((" cualquier cosa, lo que sea ")) por" whateverwhatever ", luego cambie todas las instancias restantes de" (("y "))" de regreso ".
Podrías probar este csv paser awkbased:
Puede usar un script que escribí llamado csvquote para que awk ignore las comas dentro de los campos entre comillas. El comando se convertiría en:
csvquote Buildings.csv | awk -F, ''{print $1","$2}'' | sort | uniq | csvquote -u > Floors.csv
y cortar podría ser un poco más fácil que awk para esto:
csvquote Buildings.csv | cut -d, -f1,2 | sort | uniq | csvquote -u > Floors.csv
Puede encontrar el código de csvquote aquí: https://github.com/dbro/csvquote
gawk -vFPAT=''[^,]*|"[^"]*"'' ''{print $1 "," $3}'' | sort | uniq
Esta es una increíble extensión de GNU Awk 4, donde se define un patrón de campo en lugar de un patrón de separador de campo. Hace maravillas para CSV. ( docs )
ETA (gracias mitch): Para eliminar las comillas que rodean, gsub("^/"|/"$","",$3)
; si hay más campos que solo $3
para procesarlos de esa manera, simplemente repáselos.
Tenga en cuenta que este enfoque simple no es tolerante a las entradas mal formadas, ni a algunos posibles caracteres especiales entre comillas, que cubren todas las que irían más allá del alcance de un único borrador.