Como posso truncar um arquivo de texto (codificado em UTF-8) para um determinado número de caracteres? Eu não me importo com comprimentos de linha e o corte pode ser no meio da palavra.
cut
parece operar em linhas, mas eu quero um arquivo inteiro.head -c
usa bytes, não caracteres.
Alguns sistemas têm um
truncate
comando que trunca os arquivos em um número de bytes (não caracteres).Não conheço nenhum que seja truncado para vários caracteres, embora você possa recorrer ao
perl
que está instalado por padrão na maioria dos sistemas:perl
Com
-Mopen=locale
, usamos a noção de localidade de quais são os caracteres (portanto, em localidades usando o conjunto de caracteres UTF-8, são caracteres codificados em UTF-8). Substitua por-CS
se desejar que a E/S seja decodificada/codificada em UTF-8, independentemente do conjunto de caracteres da localidade.$/ = \1234
: definimos o separador de registro para uma referência a um inteiro que é uma maneira de especificar registros de comprimento fixo (em número de caracteres ).então, ao ler o primeiro registro, truncamos stdin no lugar (portanto, no final do primeiro registro) e saímos.
GNU sed
Com GNU
sed
, você poderia fazer (assumindo que o arquivo não contém caracteres NUL ou sequências de bytes que não formam caracteres válidos - ambos devem ser verdadeiros para arquivos de texto):Mas isso é muito menos eficiente, pois ele lê o arquivo inteiro e o armazena inteiro na memória e grava uma nova cópia.
GNU awk
O mesmo com GNU
awk
:-e code -E /dev/null "$file"
sendo uma maneira de passar nomes de arquivos arbitrários paragawk
RS='^$'
: modo slurp .Shell integrado
Com
ksh93
,bash
ouzsh
(com shells diferentes dezsh
, assumindo que o conteúdo não contém bytes NUL):Com
zsh
:Ou:
Com
ksh93
oubash
(cuidado , é falso para caracteres de vários bytes em várias versões debash
):ksh93
também pode truncar o arquivo no lugar em vez de reescrevê-lo com seu<>;
operador de redirecionamento:iconv + cabeça
Para imprimir os primeiros 1234 caracteres, outra opção poderia ser converter para uma codificação com um número fixo de bytes por caractere como
UTF32BE
/UCS-4
:head -c
não é padrão, mas bastante comum. Um equivalente padrão seria,dd bs=1 count="$((1234 * 4))"
mas seria menos eficiente, pois leria a entrada e escreveria a saída um byte por vez¹.iconv
é um comando padrão, mas os nomes de codificação não são padronizados, então você pode encontrar sistemas semUCS-4
Notas
De qualquer forma, embora a saída tenha no máximo 1234 caracteres, pode acabar não sendo um texto válido, pois possivelmente terminaria em uma linha não delimitada.
Observe também que, embora essas soluções não cortassem o texto no meio de um caractere, elas poderiam quebrá-lo no meio de um grafema , como a
é
expresso como U+0065 U+0301 (ae
seguido por um acento agudo combinado), ou grafemas de sílabas Hangul em suas formas decompostas.¹ e na entrada do pipe você não pode usar
bs
valores diferentes de 1 de forma confiável, a menos que você use aiflag=fullblock
extensão GNU, comodd
poderia fazer leituras curtas se ele ler o pipe mais rápido do queiconv
preenchê-loSe você sabe que o arquivo de texto contém Unicode codificado como UTF-8, você precisa primeiro decodificar o UTF-8 para obter uma sequência de entidades de caracteres Unicode e dividi-las.
Eu escolheria o Python 3.x para o trabalho.
Com o Python 3.x, a função open() tem um argumento de palavra-chave extra
encoding=
para ler arquivos de texto . A descrição do método io.TextIOBase.read() parece promissora.Então, usando o Python 3, ficaria assim:
Obviamente, uma ferramenta real adicionaria argumentos de linha de comando, tratamento de erros, etc.
Com o Python 2.x você pode implementar seu próprio objeto semelhante a um arquivo e decodificar o arquivo de entrada linha por linha.
Eu gostaria de adicionar outra abordagem. Provavelmente não é o melhor desempenho em termos de desempenho, e muito mais longo, mas fácil de entender:
Invoque-o com
$ ./scriptname <desired chars> <input file>
.Isso remove o último caractere um por um até que o objetivo seja alcançado, o que parece muito ruim em termos de desempenho, especialmente para arquivos maiores. Eu só queria apresentar isso como uma ideia para mostrar mais possibilidades.