Alguém conhece uma ferramenta não baseada em linha para pesquisar / substituir "binariamente" cadeias de caracteres de maneira um pouco eficiente em termos de memória? Veja esta pergunta também.
Eu tenho um arquivo de texto de + 2 GB que gostaria de processar semelhante ao que isso parece fazer:
sed -e 's/>\n/>/g'
Isso significa que eu quero remover todas as novas linhas que ocorrem depois de um >
, mas não em nenhum outro lugar, para que isso exclua tr -d
.
Este comando (que obtive da resposta de uma pergunta semelhante ) falha com couldn't re-allocate memory
:
sed --unbuffered ':a;N;$!ba;s/>\n/>/g'
Então, existem outros métodos sem recorrer a C? Eu odeio perl, mas estou disposto a fazer uma exceção neste caso :-)
Não tenho certeza de nenhum caractere que não ocorra nos dados; portanto, substituir temporariamente \n
por outro é algo que eu gostaria de evitar, se possível.
Alguma boa ideia, alguém?
fonte
--unbuffered
?--unbuffered
ficar sem memória$!
faz?$!
é. Espero que isso precise de muita memória.sed
não é a ferramenta adequada neste caso.Respostas:
Isso realmente é trivial no Perl, você não deve odiar!
Explicação
-i
: edite o arquivo no local e crie um backup do original chamadofile.bak
. Se você não quiser um backup, use-operl -i -pe
.-pe
: leia o arquivo de entrada linha por linha e imprima cada linha após aplicar o script fornecido como-e
.s/>\n/>/
: a substituição, assim comosed
.E aqui está uma
awk
abordagem:fonte
awk '{ORS=/>$/?"":"\n"}1'
':a;N;$!ba;s/>\n/>/g'
em sua pergunta, você renunciou ao seu direito de reclamar sobre legibilidade! : Pfoo ? bar : baz
construção, mas não consegui fazê-la funcionar.Uma
perl
solução:Explicação
s///
é usado para substituição de string.(?<=>)
é lookbehind padrão.\n
corresponde à nova linha.Todo o padrão significa remover todas as novas linhas que têm
>
antes.fonte
s/>\n/>/
?s/>\K\n//
também funcionariaQue tal agora:
Para o GNU sed, você também pode tentar adicionar a opção
-u
(--unbuffered
) conforme a pergunta. O GNU sed também está satisfeito com isso como uma linha simples:fonte
\n
se o arquivo terminar>\n
, mas provavelmente é preferível de qualquer maneira.}
precisa estar em uma expressão separada? isso não funcionará como uma expressão multilinha?b loop\n}
ou-e 'b loop' -e '}'
mas não comob loop;}
e certamente não como eb loop}
porque são válidos em nomes de rótulos (embora ninguém em sã consciência o usasse. E isso significa que o GNU sed não é compatível com POSIX) e o comando precisa ser separado do comando.}
;
}
b
sed
está feliz com todos os itens acima, mesmo com--posix
! O padrão também possui o seguinte para expressões entre chaves -The list of sed functions shall be surrounded by braces and separated by <newline>s
. Isso não significa que ponto e vírgula deve ser usado apenas fora do aparelho?>
. O original nunca teve um, isso foi apontado por Stéphane.Você deve poder usar
sed
com oN
comando, mas o truque será excluir uma linha do espaço do padrão cada vez que adicionar outra (para que o espaço do padrão sempre contenha apenas 2 linhas consecutivas, em vez de tentar ler a totalidade arquivo) - tenteEDIT: depois de reler Famous Sed One-Liners de Peteris Krumins Explained Eu acredito que uma
sed
solução melhor seriaque anexa apenas a linha a seguir no caso de já ter feito uma
>
correspondência no final e deve retornar condicionalmente para lidar com o caso de linhas correspondentes consecutivas (é o número 39 de Krumin) . Anexe uma linha à próxima se terminar com uma barra invertida "\" exatamente, exceto a substituição de>
por\
como o caractere de junção e o fato de que o caractere de junção é retido na saída).fonte
>
(que também é GNU específicos)sed
não fornece uma maneira de emitir saída sem uma nova linha final. Sua abordagem usandoN
fundamentalmente funciona, mas armazena linhas incompletas na memória e, portanto, pode falhar se as linhas ficarem muito longas (implantações de sed geralmente não são projetadas para lidar com linhas extremamente longas).Você pode usar o awk.
Uma abordagem alternativa é usar
tr
para trocar o caractere de nova linha por um caractere "chato" e que ocorre com frequência. O espaço pode funcionar aqui - escolha um caractere que tende a aparecer em todas as linhas ou pelo menos em uma grande proporção de linhas nos seus dados.fonte
sed
não funciona sem um buffer de 2,5 gigabytes.tr
abordagem - mikeserv, você publicou uma abordagem diferente (válida, mas menos genérica) que também usatr
.que tal usar ed?
(via http://wiki.bash-hackers.org/howto/edit-ed )
fonte
Acabei usando gsar como descrito nesta resposta assim:
fonte
Existem várias maneiras de fazer isso, e a maioria aqui é realmente boa, mas acho que essa é a minha favorita:
Ou até:
fonte
*
. Do jeito que está agora, ele excluirá todas as linhas em branco após uma linha que termine com a>
. … Hmm. Olhando para a pergunta, vejo que é um pouco ambígua. A pergunta diz: “Eu quero remover todas as novas linhas que ocorrem depois de um>
...” Eu interpreto que isso significa que>\n\n\n\n\nfoo
deve ser alterado para\n\n\n\nfoo
, mas acho quefoo
pode ser a saída desejada.printf '>\n>\n\n>>\n>\n>>>\n>\nf\n\nff\n>\n' | tr '>\n' '\n>' | sed 's/^>*//;H;/./!d;x;y/\n>/>\n/'
- que resulta em>>>>>>>>>>f\n\nff\n\n
mim com a primeira resposta. No entanto, estou curioso para saber o que você está fazendo para quebrá-lo, porque gostaria de corrigi-lo. Quanto ao segundo ponto - não concordo que seja ambíguo. O OP não pede para remover todos>
anterior um\n
ewline, mas em vez de remover todas as\n
ewlines seguintes um>
.>\n\n\n\n\n
, somente a primeira nova linha é após a>
; todos os outros estão seguindo outras novas linhas. Observe que a sugestão do OP "é isso que eu quero, se funcionasse"sed -e 's/>\n/>/g'
não erased -e 's/>\n*/>/g'
.s/>\n/>/
em>\n\n\n\n\n
ainda seria algo ques/>\n/>/
editaria.