¿Cuál es una buena manera de filtrar un archivo de texto para eliminar líneas vacías?


11

Tengo un archivo .csv (en una Mac) que tiene un montón de líneas vacías, por ejemplo:

"1", "2", "lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum 

lorem ipsum ","2","3","4"
"1", "2", "lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum 

lorem ipsum ","2","3","4"

Que quiero convertir a:

"1", "2", "lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum ","2","3","4"
"1", "2", "lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum  lorem ipsum ","2","3","4"

Sé que debe haber un trazador de líneas, pero no sé awk o sed. Cualquier consejo muy apreciado!


1
Según esa muestra, realmente desea eliminar los saltos de línea incrustados de los campos. ¿Es eso correcto? En otras palabras, ¿hay 6 líneas de entrada y debería haber 2 líneas de salida?
manatwork

Sí, eso es exactamente de lo que estoy tratando de deshacerme: nuevas líneas incrustadas dentro de una cadena entre comillas.
pitosalas

Entonces, lo que necesita es algo que elimine las nuevas líneas entre comillas. Eso va a ser un poco más complicado, porque necesitas una expresión regular multilínea.
tongpu

Respuestas:


11

Puede usar el modo grep -v(inversión inversa) para hacer esto:

grep -v '^$' old-file.csv > new-file.csv

Tenga en cuenta que estos deben ser archivos diferentes, debido a cómo funcionan los redireccionamientos de shell. El archivo de salida se abre (y se vacía) antes de leer el archivo de entrada. Si tiene más usos (no de forma predeterminada en Mac OS X), puede usar spongepara solucionar esto:

grep -v '^$' file.csv | sponge file.csv

Pero, por supuesto, es más difícil regresar si algo sale mal.

Si las "líneas en blanco" en realidad pueden contener espacios (parece que sí), puede usar esto en su lugar:

egrep -v '^[[:space:]]*$' old-file.csv > new-file.csv

Eso ignorará las líneas en blanco, así como las líneas que contienen solo espacios en blanco. Por supuesto, puedes hacer la misma spongetransformación.


Gracias ... No borró ninguna línea vacía ... ¿Quizás ^ $ no coincide? Pero las líneas están vacías a lo mejor de mi conocimiento. Recuerde que este es un cdv creado por Excel en una Mac ... ¿Eso dice algo? (No huyas gritando porque dije Excel :)
pitosalas

@pitosalas Probablemente no sean líneas vacías. Intente cambiarlo a egrep -v '^[[:space:]]*$'... note grep -> egrep y el extraño nuevo patrón
derobert

No funcionó Eliminé un montón de comillas dobles e hice un desastre ...
pitosalas

@pitosalas No estoy seguro de cómo eliminaría las comillas dobles. Solo debería poder eliminar espacios en blanco. Y de hecho, eso es lo que hace cuando lo
pruebo

@pitosalas, ¿podría verificar si alguno de estos comandos escupe algo que parece razonable (en lugar de galimatías): iconv -f utf16le file.csv | headoiconv -f utf16be file.csv | head
derobert

8

La opción más fácil es justa grep .. Aquí, el punto significa "unir cualquier cosa", por lo que si la línea está vacía, no coincide. De lo contrario, imprime toda la línea tal como está.


6

Para eliminar líneas vacías, en su lugar , con ksh93:

sed '/./!d' file 1<>; file

El <>;operador de redirección es específico de ksh93 y es el mismo que el <>operador estándar , excepto que ksh trunca el archivo después de que el comando ha finalizado.

sed '/./!d'es una forma complicada de escribir grep ., pero desafortunadamente GNU grep al menos se queja si su stdout apunta al mismo archivo que su stdin. Dirías que uno podría escribir:

grep . file | cat 1<>; file

Pero desafortunadamente, hay un error en ksh93 (al menos mi versión (93u +)), ya que el archivo parece estar truncado a cero en ese caso.

grep . file | { cat; } 1<>; file

Parece evitar ese error, pero ahora es mucho más complicado que el comando sed.


Combine sus respuestas en una entrada bien formateada con una guía rápida sobre cuándo debe emplearse cada solución. Los diferentes enfoques a los diferentes problemas, todos mezclados en respuestas flotantes, han hecho que esta pregunta sea un desastre para leer.
Caleb

@Caleb, todo se reduce a que la pregunta es muy poco clara, por lo que todas las respuestas de todos son para diferentes interpretaciones de la pregunta. Para cada respuesta, traté de decir qué pregunta intenta responder.
Stéphane Chazelas

Solo para tu información: Intenté lo awk '/./' file 1<>; fileque funcionó. Para mí, eso es aún más claro quesed '/./!d'
grebneke

5

Aquí hay una Perlfrase para ello:

perl -pi -e 's/^\s*\n//' yourfile

EDITAR: Código mejorado basado en los comentarios de ruakh a continuación.


1
O bienperl -ni -e '/./ and print' yourfile
derobert el

1
@peterph $es un ancla (es decir, ancho cero), por lo que excluye la nueva línea. En cuanto al espacio superfluo, es la razón por la que agregué /xque no quería Perlintentar interpolar `$ \` en la expresión regular
Joseph R.

1
No necesitas el $, dado que tienes el \n. (Alternativamente, no necesita el \n, dado que tiene el \s*y el $; pero creo s/^\s*\n//que aclara que se elimina la nueva línea). Tampoco necesita el /m; No tiene ningún efecto sobre este comando. Y una vez que te deshagas del $y del espacio, no necesitarás el /x.
ruakh

1
@JosephR .: El \nmismo se puede eliminar; lo que no puedes hacer es eliminar tanto el $ como el \n. Entonces s/^\s*//tendría el problema que usted describe, pero s/^\s*$//estaría bien, debido a la \s*y la $. (¿Ves lo que quiero decir?)
ruakh

1
@JosephR .: Lo que sucede es, $ puede coincidir antes de una nueva línea (siempre que sea el /mindicador está activado, o el salto de línea es el último carácter de la cadena, o ambos), pero puede también coincidir con el final de la cadena. Por ejemplo, "abc" =~ m/^abc$/es cierto. En el caso de \s*$, \s*es lo suficientemente codicioso como para consumir la nueva línea, y luego $coincide con el final de la cadena. (Pero creo que s/^\s*\n//es más claro, de todos modos, por lo que su respuesta está bien como está ahora.)
ruakh

5

Según la aclaración en los comentarios a su pregunta, algo como:

awk -v RS= -v ORS= 1

puede hacer lo que quieras

Un separador de registro vacío es un caso especial que indica awkque los registros deben ser párrafos (separados por secuencias de líneas vacías). Establecer el separador de registro de salida en la cadena vacía también significa que el contenido de esos párrafos (sin los separadores) debe concatenarse. 1es solo una verdadera condición para imprimir cada registro.

Sin embargo, eso omitiría la nueva línea final, por lo que podría hacer:

awk -v RS= -v ORS= '1;END{if (NR) printf "\n"}'

3

Sé que esto habría sido más fácil si entregara el archivo, pero desafortunadamente contenía información confidencial que no podía compartir. Mientras tanto, me escribí un guión de rubí que parecía hacer el truco:

require 'csv'
c = CSV.open("outfile1.csv", "w")
CSV.foreach("data.csv", :encoding => 'windows-1251:utf-8') do |row|
  row = row.map { |a| a.class == String ? a.gsub(/\r/, '') : a}
  c << row
end
c.close

Gracias a todos por ayudar!


2
awk '
    length == 0 {next} 
    /^[^"]/ && /"$/ {print; next} 
    {printf("%s", $0)}
' filename

produce

"1", "2", "lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum ","2","3","4"
"1", "2", "lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum ","2","3","4"

2

Encontré una idea para una posible solución en stackoverflow .

sed -i ':a;N;$!ba;s/[^"]\n\s*\n/ /g' file.csv

Probablemente debería hacer una copia de seguridad de su archivo csv antes de probarlo, pero al menos para el ejemplo que proporcionó, funciona a la perfección.

En la respuesta se ofrece una buena explicación sobre el funcionamiento interno de esta expresión, solo la edité para buscar líneas que no terminen con un "( [^"]\n).


1

Si, desde su propia respuesta, desea eliminar los caracteres de nueva línea contenidos dentro de las cadenas entre comillas, puede hacer lo siguiente:

 perl -0777 -pe 's/".*?"/$_=$&;s:\n::g;$_/gse'

También puede usar use perl's -iflag para editar los archivos en su lugar .

 perl -0777 -pe 's/".*?"/$_=$&;s:\n::g;$_/gse' file1 file2...

O con GNU awk:

 awk -v RS=\" 'NR%2==0 {gsub("\n","")}; {printf "%s", $0 RT}'

o:

 awk -vRS=\" '1-NR%2{gsub("\n","")}{ORS=RT}1'

(si estás compitiendo por el más corto)

Tenga en cuenta que aquellos suponen que no hay caracteres de comillas dobles escapadas en la entrada.


0

En efecto, parece que quiere más que eliminar líneas vacías, pero elimina cada secuencia de 2 o más caracteres de nueva línea.

Lo que podrías hacer con perl:

perl -0777 -pe 's/\n{2,}//gs' file

También puede usar use perl's -iflag para editar los archivos en su lugar .

perl -0777 -pi -e 's/\n{2,}//gs' file1 file2...

0

Hay una forma cada vez más corta de eliminar líneas vacías en AWK:

awk 'NF' file

Pero para obtener la salida que desea, todo lo que necesita es un simple revestimiento:

awk 'NF {printf("%s ", $0); i++;} !(i % 2) {printf("\n");}' file

Explicación

En AWK, una línea vacía significa que la fila / registro no tiene campos, es decir, la NFvariable (Número de campos) es cero. El único trazo anterior solo se ejecutará cuando NF > 0, imprima todas las líneas, pero las vacías.

El i++es el contador de líneas no vacías.

El !(i % 2)se utiliza para imprimir dos líneas no vacías consecutivas en la forma de la salida deseada, es decir, cada vez que se encuentra un múltiplo de 2, la moduloinstrucción !(i % 2)produce 1, lo que termina la concatenación de dos líneas no vacías.


¡Culpa mía! Lo siento. No leí toda su pregunta y el resultado deseado. La respuesta está arreglada ahora. Gracias. :-)
Marcelo Augusto

0

Puede usar Vim en modo Ex:

ex -sc v/./d -cx b.csv
  1. v/./ encontrar líneas vacías

  2. d Eliminar

  3. x guardar y cerrar

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.