Substituindo cadeias em um arquivo muito grande

10

Eu tenho uma série muito longa de URLs sem caractere separador, no mesmo formato abaixo:

http://example.comhttp://example.nethttp://example.orghttp://etc...

Quero que cada URL esteja em uma nova linha. Tentei fazer isso substituindo todas as instâncias de "http: //" por "\ nhttp: //" usando sed

sed 's_http://_\nhttp://_g' urls.txt

mas ocorre uma falha de segmentação (violação de memória). Só posso supor que o tamanho do arquivo (mais de 100 GB) está causando o sed exceder algum limite.

Eu poderia dividir o arquivo em vários arquivos menores para processamento, mas todas as instâncias de "http: //" precisariam ser mantidas intactas.

Existe uma maneira melhor de fazer isso?

C Sawyer
fonte
Eu acho que o sed não gosta dos 100GB sem terminações de linha, enquanto tenta ler uma única linha em seu buffer.
jippie
a divisão (independentemente de "onde" o corte acontece), o processamento e a remontagem devem fornecer, no entanto, o resultado correto.
enzotib
3
Se você realmente possui um arquivo de texto de 100 GB contendo uma única linha longa, é melhor escrever um programa C rápido para fazer o trabalho.
precisa saber é o seguinte

Respostas:

11

Com awkvocê, você pode evitar a leitura de uma grande quantidade de texto de uma só vez:

awk -vRS='http://' -vORS='\nhttp://' 1 urls.txt > urlsperline.txt

O sucesso pode depender da awkimplementação usada . Por exemplo, gawkfunciona bem, mas mawktrava.

homem a trabalhar
fonte
6

Isso fará o trabalho:

perl -pe 'BEGIN { $/ = "//" } s!(?=http://\z)!\n!' urls.txt

Ao definir $ / , alterei a definição de uma linha para que termine com, em //vez de uma nova linha. Isso faz com que o Perl leia um URL de cada vez. É improvável que um URL contenha, //exceto após o esquema, mas tudo bem, se houver, o regex evitará a adição de novas linhas falsas.

Se você deseja evitar adicionar uma linha em branco antes do primeiro URL:

perl -pe 'BEGIN { $/ = "//"; print scalar <> } s!(?=http://\z)!\n!' urls.txt

Você pode tentar fazer comparações para ver se s!http://\z!\nhttp://!é mais rápido. Eles são equivalentes. Observe que a /gbandeira não é necessária na substituição, porque só pode haver uma correspondência por "linha".

cjm
fonte
O mecanismo perl regexp está bem com linhas com vários gigabytes de comprimento?
Alex4
2
@ Alexios, provavelmente não, mas não precisa ser. Desde que mudei $/, ele lidará apenas com um URL de cada vez.
Cjm 15/04
Ah, eu vejo o que você fez lá. Já faz um tempo desde os anos 90, e eu precisava man perlvar, mas faz sentido assim.
Alex4
O Linux permite que os URLs incorporem várias barras nos caminhos; portanto, esse código poderá falhar se você tiver algum deles. Testar a cadeia inteira, http e tudo, não terá esse problema.
21412 Joe
@ Joe, estou testando a http:parte da regex. Ele examinará todos //, mas não adicionará uma nova linha, a menos que encontre http://.
Cjm
5
  1. Altere todas as ocorrências de a :com uma nova linha para dividir o arquivo.
  2. Substituir
    • http no final da linha com
    • uma nova linha seguida por http:e acrescente a próxima linha
  3. Repita uma vez, para que as linhas pares e ímpares sejam atualizadas

Essas etapas são parecidas com:

tr ':' '\n' | sed -e '/http$/{N;s/http\n/\nhttp:/}' | sed -e '/http$/{N;s/http\n/\nhttp:/}'
  1. Verifique se há linhas que não começam http://, imprima os números das linhas. Isso ocorreria apenas se a: estivesse em algum lugar da URL que não fosse depois da http.

    grep -nv '^http://'

jippie
fonte