¿Cómo puedo truncar un archivo de texto (codificado en UTF-8) a un número dado de caracteres? No me importan las longitudes de línea y el corte puede estar en el medio de la palabra.
cut
Parece funcionar en líneas, pero quiero un archivo completo.head -c
usa bytes, no caracteres.
text-processing
Pitel
fuente
fuente
cut
todavía no admite caracteres de varios bytes. Si lo hiciera, podrías hacerlocut -zc-1234 | tr -d '\0'
.Respuestas:
Algunos sistemas tienen un
truncate
comando que trunca los archivos a una cantidad de bytes (no caracteres).No conozco ninguno que se trunca a varios caracteres, aunque podría recurrir al
perl
que está instalado de forma predeterminada en la mayoría de los sistemas:perl
Con
-Mopen=locale
, usamos la noción de la configuración regional de qué caracteres son (por lo tanto, en las configuraciones regionales que usan el juego de caracteres UTF-8, eso es caracteres codificados UTF-8). Reemplace con-CS
si desea que la E / S se decodifique / codifique en UTF-8, independientemente del conjunto de caracteres del entorno local.$/ = \1234
: configuramos el separador de registros como una referencia a un entero, que es una forma de especificar registros de longitud fija (en número de caracteres ).luego, al leer el primer registro, truncamos stdin en su lugar (al final del primer registro) y salimos.
GNU sed
Con GNU
sed
, podría hacerlo (suponiendo que el archivo no contenga caracteres NUL o secuencias de bytes que no formen caracteres válidos, los cuales deberían ser ciertos para los archivos de texto):Pero eso es mucho menos eficiente, ya que lee el archivo completo y lo almacena completo en la memoria, y escribe una nueva copia.
GNU awk
Lo mismo con GNU
awk
:-e code -E /dev/null "$file"
siendo una forma de pasar nombres de archivos arbitrarios agawk
RS='^$'
: modo sorber .Conchas incorporadas
Con
ksh93
,bash
ozsh
(con conchas distintoszsh
, suponiendo que el contenido no contiene NUL bytes):Con
zsh
:O:
Con
ksh93
obash
(cuidado , es falso para los caracteres de varios bytes en varias versiones debash
):ksh93
También puede truncar el archivo en su lugar en lugar de reescribirlo con su<>;
operador de redirección:iconv + cabeza
Para imprimir los primeros 1234 caracteres, otra opción podría ser convertir a una codificación con un número fijo de bytes por carácter como
UTF32BE
/UCS-4
:head -c
No es estándar, sino bastante común. Un equivalente estándar seríadd bs=1 count="$((1234 * 4))"
pero sería menos eficiente, ya que leería la entrada y escribiría la salida un byte a la vez¹.iconv
es un comando estándar pero los nombres de codificación no están estandarizados, por lo que puede encontrar sistemas sinUCS-4
Notas
En cualquier caso, aunque la salida tendría como máximo 1234 caracteres, puede terminar siendo texto no válido, ya que posiblemente terminaría en una línea no delimitada.
También tenga en cuenta que si bien esas soluciones no cortarían el texto en el medio de un carácter, podrían romperlo en el medio de un grafema , como un
é
expresado como U + 0065 U + 0301 (e
seguido de un acento agudo combinado), o grafemas de sílabas Hangul en sus formas descompuestas.¹ y en la entrada de tubería no puede usar
bs
valores distintos de 1 de manera confiable a menos que use laiflag=fullblock
extensión GNU, ya quedd
podría hacer lecturas cortas si lee la tubería más rápido de lo que laiconv
llenafuente
dd bs=1234 count=4
Si sabe que el archivo de texto contiene Unicode codificado como UTF-8, primero debe decodificar el UTF-8 para obtener una secuencia de entidades de caracteres Unicode y dividirlas.
Elegiría Python 3.x para el trabajo.
Con Python 3.x, la función open () tiene un argumento adicional de palabra clave
encoding=
para leer archivos de texto . La descripción del método io.TextIOBase.read () parece prometedora.Entonces, usando Python 3 se vería así:
Obviamente, una herramienta real agregaría argumentos de línea de comandos, manejo de errores, etc.
Con Python 2.x, podría implementar su propio objeto tipo archivo y decodificar el archivo de entrada línea por línea.
fuente
Me gustaría agregar otro enfoque. Probablemente no sea el mejor rendimiento sabio, y mucho más largo, pero fácil de entender:
Invocarlo con
$ ./scriptname <desired chars> <input file>
.Esto elimina el último carácter uno por uno hasta que se cumpla el objetivo, lo que parece realmente un mal rendimiento, especialmente para archivos más grandes. Solo quería presentar esto como una idea para mostrar más posibilidades.
fuente
wc
cuenta con el orden de O (n ^ 2) bytes totales para un punto objetivo a la mitad del archivo. Debería ser posible la búsqueda binaria en lugar de la búsqueda lineal utilizando una variable que aumente o disminuya, comoecho -n "${result::-$chop}" | wc -m
o algo así. (Y mientras lo hace, hágalo seguro incluso si el contenido del archivo comienza con-e
algo o algo así, tal vez usandoprintf
). Pero aún así no superarás los métodos que solo miran cada carácter de entrada una vez, por lo que probablemente no valga la pena.$result
hasta que coincida con la longitud deseada, pero si la longitud deseada es un número alto, es igual de ineficiente.$desired_chars
bytes en el extremo inferior o tal vez4*$desired_chars
en el extremo superior. Pero aún así creo que es mejor usar algo completamente diferente.