Recentemente, fiz uma pergunta sobre como remover o caractere de nova linha se ele ocorrer após outro caractere específico.
As ferramentas de processamento de texto Unix são muito poderosas, mas quase todas elas lidam com linhas de texto, o que é bom na maioria das vezes quando a entrada se encaixa na memória disponível.
Mas o que devo fazer se desejar substituir uma sequência de texto em um arquivo enorme que não contenha novas linhas?
Por exemplo, substitua <foobar>
por \n<foobar>
sem ler a entrada linha por linha? (uma vez que existe apenas uma linha e tem 2,5 G de comprimento).
text-processing
MattBianco
fonte
fonte
perl
oupython
?gsar
( home.online.no/~tjaberg ) que tentarei.Respostas:
A primeira coisa que me ocorre ao enfrentar esse tipo de problema é alterar o separador de registros. Na maioria das ferramentas, isso é definido como
\n
padrão, mas pode ser alterado. Por exemplo:Perl
Explicação
-0
: define o separador de registros de entrada para um caractere, devido ao seu valor hexadecimal . Nesse caso, estou configurando-o para>
cujo valor hexadecimal é3E
. O formato geral é-0xHEX_VALUE
. Este é apenas um truque para quebrar a linha em pedaços gerenciáveis.-pe
: imprime cada linha de entrada após aplicar o script fornecido por-e
.s/<foobar>/\n$&/
: uma simples substituição. O$&
é o que foi correspondido, neste caso<foobar>
.awk
Explicação
RS="<"
: defina o separador de registros de entrada como>
.gsub(/foobar>/,"\n<foobar>")
: substitua todos os casos defoobar>
com\n<foobar>
. Observe que, comoRS
foi definido como<
, todos<
são removidos do arquivo de entrada (é assim queawk
funciona); portanto, precisamos corresponderfoobar>
(sem a<
) e substituir por\n<foobar>
.printf "%s",$0
: imprime a "linha" atual após a substituição.$0
é o registro atual,awk
então ele conterá o que estava antes do<
.Testei-os em um arquivo de linha única de 2,3 GB criado com estes comandos:
Tanto a quantidade
awk
quanto aperl
quantidade negligenciável de memória usada.fonte
Tie::File
perldoc.perl.org/Tie/File.html . Eu acho que são as melhores característicasPerl
ao lidar com arquivos enormes.Tie::File
é um módulo básico desde entãov5.7.3
.O gsar (pesquisa geral e substituição) é uma ferramenta muito útil exatamente para esse fim.
A maioria das respostas a essa pergunta usa ferramentas baseadas em registros e vários truques para adaptá-las ao problema, como alternar o caractere separador de registros padrão para algo que se supõe estar ocorrendo com freqüência suficiente na entrada para não tornar cada registro muito grande para lidar.
Em muitos casos, isso é muito bom e até legível. Eu gosto de problemas que podem ser facilmente / eficientemente resolvidos com ferramentas em todos os lugares-disponíveis, tais como
awk
,tr
,sed
e o shell Bourne.A realização de uma pesquisa binária e a substituição em um arquivo enorme arbitrário com conteúdo aleatório não se encaixa muito bem nessas ferramentas unix padrão.
Alguns de vocês podem pensar que isso é trapaça, mas não vejo como o uso da ferramenta certa para o trabalho possa estar errado. Nesse caso, é um programa C chamado
gsar
licenciado sob a GPL v2 , por isso me surpreende bastante que não exista um pacote para essa ferramenta muito útil no gentoo , redhat ou no ubuntu .gsar
usa uma variante binária do algoritmo de pesquisa de string de Boyer-Moore .O uso é direto:
onde
-F
significa modo "filtro", ou seja, leia astdin
gravação parastdout
. Existem métodos para operar em arquivos também.-s
especifica a cadeia de pesquisa e-r
a substituição. A notação de dois pontos pode ser usada para especificar valores de bytes arbitrários.O modo que não diferencia maiúsculas de minúsculas é suportado (
-i
), mas não há suporte para expressões regulares, pois o algoritmo usa o comprimento da cadeia de pesquisa para otimizar a pesquisa.A ferramenta também pode ser usada apenas para pesquisa, um pouco como
grep
.gsar -b
outputs os deslocamentos byte da cadeia de pesquisa correspondida, egsar -l
impressões nome do arquivo e número de partidas, se houver, um pouco como combinargrep -l
comwc
.A ferramenta foi escrita por Tormod Tjaberg (inicial) e Hans Peter Verne (melhorias).
fonte
gsar
.No caso restrito em que as seqüências de destino e de substituição têm o mesmo comprimento, o mapeamento de memória pode ser útil. Isso é especialmente útil se a substituição precisar ser realizada no local. Você está basicamente mapeando um arquivo na memória virtual de um processo, e o espaço de endereço para o endereçamento de 64 bits é enorme. Observe que o arquivo não é necessariamente mapeado para a memória física de uma só vez , para que arquivos com várias vezes o tamanho da memória física disponível na máquina possam ser tratados.
Aqui está um exemplo de Python que substitui
foobar
porXXXXXX
fonte
Existem muitas ferramentas para isso:
dd
é o que você deseja usar se desejar bloquear um arquivo - leia com confiabilidade apenas um certo número de bytes apenas um certo número de vezes. Ele lida de maneira portável com o bloqueio e desbloqueio de fluxos de arquivos: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
Eu também uso
tr
acima porque ele pode manipular a conversão de qualquer byte ASCII para qualquer outro (ou, nesse caso, excluir qualquer byte ASCII que não seja um caractere imprimível que não seja espaço). É o que eu usei em resposta à sua outra pergunta esta manhã, de fato, quando eu fiz:Existem muitos similares . Essa lista deve fornecer um subconjunto de denominador comum mais baixo com o qual você possa se familiarizar.
Mas, se eu fosse fazer o processamento de texto em 2,5 gbs de arquivo binário, eu poderia começar
od
. Pode fornecer a você umoctal dump
ou vários outros formatos. Você pode especificar todos os tipos de opções - mas vou fazer um byte por linha em um\C
formato de escape:Os dados que você obterá
od
serão regulares no intervalo que você especificar - como mostramos abaixo. Mas primeiro - aqui está uma resposta para sua pergunta:Esse pouco acima delimita
\n
linhas ew,\0
nulos,\t
abs e<spaces>
preserva a\C
string de escape para o delimitador. Observe as funçõesH
ex
usadas - sempre quesed
encontra um delimitador, ele troca o conteúdo de seus buffers de memória. Dessa maneira,sed
apenas retém o máximo de informações necessárias para delimitar o arquivo com segurança e não sucumbe às excedências de buffer - não é, desde que encontre seus delimitadores. Enquanto isso,sed
continuará processando sua entrada eod
continuará fornecendo-a até encontrarEOF
.Como é, sua saída é assim:
Então, se eu quiser
foobar
:Agora, se você quiser usar os
C
escapes, é muito fácil - porque a barra invertidased
dupla já\\
escapou de todas as barras invertidas de entrada única, portanto, aprintf
execução dexargs
não terá problemas para produzir a saída conforme sua especificação. Mas comoxargs
aspas shell, você precisará citá-las novamente:Isso poderia ter sido facilmente salvo em uma variável do shell e produzido posteriormente de maneira idêntica. O último
sed
insere uma\
barra invertida antes de cada caractere em sua entrada, e isso é tudo.E aqui está o que tudo parece antes de
sed
se apossar:fonte
O Awk opera em registros sucessivos. Ele pode usar qualquer caractere como separador de registros (exceto o byte nulo em muitas implementações). Algumas implementações suportam expressões regulares arbitrárias (que não correspondem à cadeia vazia) como separador de registros, mas isso pode ser complicado porque o separador de registros é truncado no final de cada registro antes de ser armazenado
$0
(o GNU awk define a variávelRT
como separador de registros que foi retirado do final do registro atual). Observe queprint
finaliza sua saída com o separador de registros de saída,ORS
que é uma nova linha por padrão e configurada independentemente do separador de registros de entradaRS
.Você pode efetivamente selecionar um personagem diferente como o separador de registro para outras ferramentas (
sort
,sed
...), trocando novas linhas com aquele personagem comtr
.Muitos utilitários de texto GNU suportam o uso de um byte nulo em vez de uma nova linha como separador.
fonte