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
perl
opython
?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
\n
de 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 queRS
se ha establecido en<
, todos<
se eliminan del archivo de entrada (así es comoawk
funciona), 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.$0
es el registro actual,awk
por 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
awk
y lasperl
cantidades insignificantes usadas de memoria.fuente
Tie::File
perldoc.perl.org/Tie/File.html . Creo que son las mejores característicasPerl
cuando se trata de archivos enormes.Tie::File
es 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
,sed
y 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
gsar
que 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 .gsar
utiliza una variante binaria del algoritmo de búsqueda de cadenas de Boyer-Moore .El uso es sencillo:
donde
-F
significa modo "filtro", es decir, lectura,stdin
escriturastdout
. También hay métodos para operar en archivos.-s
especifica la cadena de búsqueda y-r
el 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 -b
genera los desplazamientos de bytes de la cadena de búsqueda coincidente egsar -l
imprime el nombre de archivo y el número de coincidencias, si corresponde, un poco como combinargrep -l
conwc
.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
foobar
conXXXXXX
fuente
Hay muchas herramientas para esto:
dd
es 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/null
UI(#Q5\e BKX2?A:Z RAxGm:qv t!;/v!)N
También lo uso
tr
arriba 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 dump
o cualquiera de varios otros formatos. Puede especificar todo tipo de opciones, pero solo haré un byte por línea en un\C
formato de escape:Los datos que obtendrá
od
será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
\n
líneas de tensión,\0
nulos,\t
abdominales y al<spaces>
tiempo que conserva la\C
cadena de escape para el delimitador. Tenga en cuenta las funcionesH
yx
utilizadas: cada vez quesed
encuentra un delimitador, intercambia el contenido de sus memorias intermedias. De esta manera,sed
solo 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,sed
continuará procesando su entrada yod
continuará proporcionándola hasta que se encuentreEOF
.Como es, su salida se ve así:
Entonces si quiero
foobar
:Ahora, si desea hacer uso de los
C
escapes, es bastante fácil,sed
ya que la\\
barra invertida doble escapó de todas sus barras invertidas de entrada individuales, por lo que si seprintf
ejecuta desde allíxargs
no tendrá problemas para producir el resultado según sus especificaciones. Peroxargs
come 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
sed
inserta 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
sed
alcance: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 variableRT
en el separador de registros que se eliminó del final del registro actual). Tenga en cuenta queprint
termina su salida con el separador de registro de salida,ORS
que 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