Recientemente hice una pregunta sobre cómo eliminar el carácter de nueva línea si ocurre después de otro carácter específico.
Las herramientas de procesamiento de texto de Unix son muy potentes, pero casi todas tratan con líneas de texto, lo cual está bien la mayor parte del tiempo cuando la entrada cabe en la memoria disponible.
Pero, ¿qué debo hacer si deseo reemplazar una secuencia de texto en un archivo enorme que no contiene líneas nuevas?
Por ejemplo, ¿reemplazar <foobar>con \n<foobar>sin leer la entrada línea por línea? (dado que solo hay una línea y tiene una longitud de 2.5G caracteres).
text-processing
MattBianco
fuente
fuente

perlopython?gsar( home.online.no/~tjaberg ) que intentaré.Respuestas:
Lo primero que se me ocurre al enfrentar este tipo de problema es cambiar el separador de registros. En la mayoría de las herramientas, esto está configurado
\nde manera predeterminada, pero se puede cambiar. Por ejemplo:Perl
Explicación
-0: esto establece el separador de registro de entrada en un carácter dado su valor hexadecimal . En este caso, lo estoy configurando a>cuyo valor hexadecimal es3E. El formato general es-0xHEX_VALUE. Esto es solo un truco para romper la línea en trozos manejables.-pe: imprime cada línea de entrada después de aplicar la secuencia de comandos dada por-e.s/<foobar>/\n$&/: una simple sustitución. El$&es lo que fue igualado, en este caso<foobar>.awk
Explicación
RS="<": establece el separador de registro de entrada en>.gsub(/foobar>/,"\n<foobar>"): sustituye todos los casos defoobar>con\n<foobar>. Tenga en cuenta que debido a queRSse ha establecido en<, todos<se eliminan del archivo de entrada (así es comoawkfunciona), por lo que debemos hacer coincidirfoobar>(sin a<) y reemplazar con\n<foobar>.printf "%s",$0: imprime la "línea" actual después de la sustitución.$0es el registro actual,awkpor lo que contendrá lo que estaba antes de<.Los probé en un archivo de una sola línea de 2.3 GB creado con estos comandos:
Tanto el
awky lasperlcantidades insignificantes usadas de memoria.fuente
Tie::Fileperldoc.perl.org/Tie/File.html . Creo que son las mejores característicasPerlcuando se trata de archivos enormes.Tie::Filees un módulo central desde entoncesv5.7.3.gsar (búsqueda general y reemplazo) es una herramienta muy útil para exactamente este propósito.
La mayoría de las respuestas a esta pregunta utilizan herramientas basadas en registros y varios trucos para que se adapten al problema, como cambiar el carácter separador de registros predeterminado a algo que se supone que ocurre con suficiente frecuencia en la entrada para no hacer que cada registro sea demasiado grande para manejarlo.
En muchos casos esto es muy bueno e incluso legible. Me gustan los problemas que pueden ser fácilmente resueltos / eficientemente con las herramientas disponibles en todas partes, tales como
awk,tr,sedy el shell Bourne.Realizar una búsqueda binaria y reemplazarlo en un archivo enorme arbitrario con contenido aleatorio no se ajusta muy bien a estas herramientas estándar de Unix.
Algunos de ustedes pueden pensar que esto es hacer trampa, pero no veo cómo puede estar mal usar la herramienta adecuada para el trabajo. En este caso, se trata de un programa llamado C
gsarque está licenciado bajo GPL v2 , por lo que me sorprende bastante que no haya un paquete para esta herramienta tan útil ni en gentoo , redhat ni ubuntu .gsarutiliza una variante binaria del algoritmo de búsqueda de cadenas de Boyer-Moore .El uso es sencillo:
donde
-Fsignifica modo "filtro", es decir, lectura,stdinescriturastdout. También hay métodos para operar en archivos.-sespecifica la cadena de búsqueda y-rel reemplazo. La notación de dos puntos se puede usar para especificar valores de bytes arbitrarios.Se admite el modo que no distingue entre mayúsculas y minúsculas (
-i), pero no se admiten expresiones regulares, ya que el algoritmo utiliza la longitud de la cadena de búsqueda para optimizar la búsqueda.La herramienta también se puede usar solo para buscar, un poco como
grep.gsar -bgenera los desplazamientos de bytes de la cadena de búsqueda coincidente egsar -limprime el nombre de archivo y el número de coincidencias, si corresponde, un poco como combinargrep -lconwc.La herramienta fue escrita por Tormod Tjaberg (inicial) y Hans Peter Verne (mejoras).
fuente
gsar.En el caso estrecho donde las cadenas de destino y de reemplazo son de la misma longitud, el mapeo de memoria puede venir al rescate. Esto es especialmente útil si el reemplazo debe realizarse en el lugar. Básicamente, está asignando un archivo a la memoria virtual de un proceso, y el espacio de direcciones para el direccionamiento de 64 bits es enorme. Tenga en cuenta que el archivo no se asigna necesariamente a la memoria física de una vez , por lo que se pueden tratar archivos que tienen varias veces el tamaño de la memoria física disponible en la máquina.
Aquí hay un ejemplo de Python que reemplaza
foobarconXXXXXXfuente
Hay muchas herramientas para esto:
ddes lo que desea usar si desea bloquear un archivo: lea de manera confiable solo un cierto número de bytes solo un cierto número de veces. Maneja de forma portátil el bloqueo y desbloqueo de secuencias de archivos:tr -dc '[:graph:]' </dev/urandom | dd bs=32 count=1 cbs=8 conv=unblock,sync 2>/dev/nullUI(#Q5\e BKX2?A:Z RAxGm:qv t!;/v!)NTambién lo uso
trarriba porque puede manejar la conversión de cualquier byte ASCII a cualquier otro (o, en este caso, eliminar cualquier byte ASCII que no sea un carácter imprimible sin espacio). Es lo que usé en respuesta a su otra pregunta esta mañana, de hecho, cuando lo hice:Hay muchos parecidos . Esa lista debe proporcionar un subconjunto de denominador común más bajo con el que pueda familiarizarse.
Pero, si fuera a hacer procesamiento de texto en 2.5 gbs de archivo binario, podría comenzar con
od. Puede darle unooctal dumpo cualquiera de varios otros formatos. Puede especificar todo tipo de opciones, pero solo haré un byte por línea en un\Cformato de escape:Los datos que obtendrá
odserán regulares en cualquier intervalo que especifique, como se muestra a continuación. Pero primero, aquí hay una respuesta a su pregunta:Ese poco más arriba delimita en
\nlíneas de tensión,\0nulos,\tabdominales y al<spaces>tiempo que conserva la\Ccadena de escape para el delimitador. Tenga en cuenta las funcionesHyxutilizadas: cada vez quesedencuentra un delimitador, intercambia el contenido de sus memorias intermedias. De esta manera,sedsolo retiene tanta información como debe para delimitar de manera confiable el archivo y no sucumbe a desbordamientos de búfer, es decir, no, siempre y cuando realmente encuentre sus delimitadores. Mientras lo haga,sedcontinuará procesando su entrada yodcontinuará proporcionándola hasta que se encuentreEOF.Como es, su salida se ve así:
Entonces si quiero
foobar:Ahora, si desea hacer uso de los
Cescapes, es bastante fácil,sedya que la\\barra invertida doble escapó de todas sus barras invertidas de entrada individuales, por lo que si seprintfejecuta desde allíxargsno tendrá problemas para producir el resultado según sus especificaciones. Peroxargscome comillas, así que tendrás que volver a comillas dobles:Eso podría haberse guardado tan fácilmente en una variable de shell y salir más tarde de manera idéntica. El último
sedinserta una\barra diagonal inversa antes de cada carácter en su entrada, y eso es todo.Y así es como se ve todo antes de que lo
sedalcance:fuente
Awk opera en sucesivos registros. Puede usar cualquier carácter como separador de registros (excepto el byte nulo en muchas implementaciones). Algunas implementaciones admiten expresiones regulares arbitrarias (que no coinciden con la cadena vacía) como separador de registros, pero esto puede ser difícil de manejar porque el separador de registros se trunca desde el final de cada registro antes de guardarlo
$0(GNU awk establece la variableRTen el separador de registros que se eliminó del final del registro actual). Tenga en cuenta queprinttermina su salida con el separador de registro de salida,ORSque es una nueva línea de forma predeterminada y se establece independientemente del separador de registro de entradaRS.Puede seleccionar efectivamente un carácter diferente como el separador de registros para otras herramientas (
sort,sed, ...) mediante el canje de los saltos de línea con ese personaje contr.Muchas utilidades de texto GNU admiten el uso de un byte nulo en lugar de una nueva línea como separador.
fuente