Un enorme archivo de texto (hasta 2 GiB) contiene aproximadamente 100 duplicados exactos de cada línea (inútil en mi caso, ya que el archivo es una tabla de datos similar a CSV).
Lo que necesito es eliminar todas las repeticiones mientras (preferiblemente, pero esto se puede sacrificar por un aumento significativo del rendimiento) mantener el orden de secuencia original. En el resultado, cada línea debe ser única. Si hubiera 100 líneas iguales (generalmente los duplicados se extienden por el archivo y no serán vecinos), solo quedará uno del tipo.
He escrito un programa en Scala (considérelo Java si no sabe sobre Scala) para implementar esto. ¿Pero tal vez hay herramientas nativas escritas en C más rápidas capaces de hacerlo más rápido?
ACTUALIZACIÓN: la awk '!seen[$0]++' filename
solución parecía funcionar bien para mí, siempre y cuando los archivos estuvieran cerca de 2 GiB o menos, pero ahora que tengo que limpiar un archivo de 8 GiB ya no funciona. Parece tener infinito en una Mac con 4 GiB RAM y una PC con Windows 7 de 64 bits con 4 GiB RAM y 6 GiB swap simplemente se queda sin memoria. Y no me entusiasma probarlo en Linux con 4 GiB RAM dada esta experiencia.
sort -u
probablemente será más rápido.Respuestas:
Una
awk
solución vista en #bash (Freenode):fuente
awk
versión más detallada que utiliza 2 búsquedas de matriz (se muestra como una explicación ampliada en la respuesta de Gilles): 0m36.132s vs 0m49.958s ... para 50 millones de líneas ... Pensé que el cuello de botella sería la E / S, pero la búsqueda de matriz adicional es ... 1 millón de elementos en la matriz parece hacer una abolladura bastante significativa ...Hay un método simple (que no quiere decir obvio) que utiliza utilidades estándar que no requiere una gran memoria, excepto para ejecutarse
sort
, que en la mayoría de las implementaciones tiene optimizaciones específicas para archivos de gran tamaño (un buen algoritmo de ordenamiento externo). Una ventaja de este método es que solo recorre todas las líneas dentro de las utilidades especiales, nunca dentro de los lenguajes interpretados.Si todas las líneas comienzan con un carácter que no sea un espacio en blanco, puede prescindir de algunas de las opciones:
Para una gran cantidad de duplicación, un método que solo requiere almacenar una sola copia de cada línea en la memoria funcionará mejor. Con un poco de interpretación general, hay un script awk muy conciso para eso (ya publicado por enzotib ):
De manera menos concisa:
!seen[$0] {print} {seen[$0] += 1}
es decir, imprima la línea actual si aún no se ha visto, luego incremente elseen
contador para esta línea (las variables no inicializadas o los elementos de la matriz tienen el valor numérico 0).Para líneas largas, puede ahorrar memoria manteniendo solo una suma de comprobación no falsificable (por ejemplo, un resumen criptográfico) de cada línea. Por ejemplo, con SHA-1, solo necesita 20 bytes más una sobrecarga constante por línea. Pero calcular los resúmenes es bastante lento; este método solo ganará si tiene una CPU rápida (especialmente una con un acelerador de hardware para calcular los resúmenes) y no hay mucha memoria en relación con el tamaño del archivo y líneas suficientemente largas. Ninguna utilidad básica le permite calcular una suma de verificación para cada línea; tendría que soportar la sobrecarga de interpretación de Perl / Python / Ruby / ... o escribir un programa compilado dedicado.
fuente
awk '!seen[$0]++'
, ¿significa que si awk ve 2 líneas duplicadas, mantendrá la siempre primera e ignorará todas las siguientes? (¿O se quedará con el último?)sort -u
cambia el orden. Mi respuesta muestra soluciones que preservan el orden (el orden de las primeras ocurrencias, para ser precisos).Tenga en cuenta que el archivo de salida se ordenará.
fuente
awk
comando en otras respuestas, pero conceptualmente simple!sort -u
para eliminar duplicados durante la ordenación, en lugar de después. (Y ahorra ancho de banda de memoria) canalizándolo a otro programa). Esto solo es mejor que laawk
versión si desea que su salida también esté ordenada. (El OP sobre esta pregunta quiere que se conserve su orden original , por lo que esta es una buena respuesta para un caso de uso ligeramente diferente)Suponiendo que puede permitirse mantener tanto como el archivo desduplicado en la memoria (si sus datos están duplicados por un factor de 100, eso debería ser aproximadamente 20MiB + sobrecarga), puede hacer esto muy fácilmente con Perl.
Esto preserva el orden también.
Si lo desea, puede extraer el número de apariciones de cada línea del
%dup
hash, como un bono gratis adicional.Si lo prefiere
awk
, esto también debería hacerlo (la misma lógica que la versión perl, el mismo orden, los mismos datos recopilados en ladup
variable):fuente
uniq
hace todo soloComo ninguna otra respuesta proporcionó soporte in situ, aquí hay una:
fuente
GNU Awk 4.0.2
Puede usar
uniq
http://www.computerhope.com/unix/uuniq.htmuniq
informa o filtra líneas repetidas en un archivo.fuente
'uniq' does not detect repeated lines unless they are adjacent.
Por lo tanto, primero debe ordenarlo y perder el orden de las líneas no duplicadas.Forros Python One:
fuente
OrderedDict
Ninguna de las respuestas aquí funcionó para mí en mi Mac, así que escribí un simple script de Python que funciona para mí. Estoy ignorando los espacios en blanco iniciales / finales y tampoco me importa el consumo de memoria.
Guarde lo anterior en unique.py y ejecútelo así:
fuente
Con bash 4, se puede utilizar una solución de bash puro que aprovecha las matrices asociativas . Aquí hay un ejemplo
fuente
read
bucles para procesar archivos de texto grandes. bash tiene que leer un byte por vez para evitar sobrepasar una nueva línea. Bash tampoco es muy rápido en el procesamiento de texto en general en comparación con awk. Si usa esto,read -ra
evitará comer barras invertidas en su entrada. Además, no olvideunset llist
después del ciclo, si pone esto en una función de shell o lo usa de forma interactiva.