Como posso ler, analisar e depois “não ler” e reler o início de um fluxo de entrada no Perl?

StackOverflow https://stackoverflow.com/questions/4059800

  •  27-09-2019
  •  | 
  •  

Pergunta

Estou lendo e processando um fluxo de entrada de o Argv FileHandle em Perl (ou seja, o while(<>) construir) um arquivo de arquivo regular, que pode ser stdin. No entanto, preciso analisar uma parcela significativa da entrada para detectar em qual dos quatro formatos diferentes, mas extremamente semelhantes aqui). Depois de decidir em qual formato os dados estão, preciso voltar e analisar essas linhas pela segunda vez para realmente ler os dados.

Então, preciso ler as primeiras 500 linhas do fluxo duas vezes. Ou, para olhar de outra maneira, preciso ler as primeiras 500 linhas e depois "colocá -las de volta" para que eu possa lê -las novamente. Como posso estar lendo o Stdin, não posso simplesmente procurar de volta ao começo. E os arquivos são enormes, então não consigo ler tudo na memória (embora ler essas primeiras 500 linhas na memória esteja OK). Qual é a melhor forma de fazer isso?

Como alternativa, posso duplicar o fluxo de entrada de alguma forma?

Editar: Espere um minuto. Acabei de perceber que não posso mais processar a entrada como um grande fluxo, porque tenho que detectar o formato de cada arquivo de forma independente. Então, eu não posso usar o argv. O resto da pergunta ainda permanece, no entanto.

Foi útil?

Solução

Como você disse, se o arquivo de arquivo pode ser stdin, você não pode usar seek para rebobiná -lo. Mas ainda é bem simples. Eu não me incomodaria com um módulo:

my @lines;

while (<$file>) {
  push @lines, $_;
  last if @lines == 500;
}

... # examine @lines to determine format

while (defined( $_ = @lines ? shift @lines : <$file> )) {
  ... # process line
}

Lembre -se de que você precisa de um explícito defined nesse caso, porque o caso especial que adiciona um implícito defined para alguns while Loops não se aplica a essa expressão mais complexa.

Outras dicas

é uma Módulo CPAN que fornece um unread Método para o IO::Handle classe. No entanto, seus avisos tornam um um tanto cauteloso. Eu avaliaria sua adequação com cuidado.

Se você realmente precisar salvar 500 linhas, cada uma razoavelmente curta, esse módulo pode ser suficiente; Seu exemplo usa STDIN.

No entanto, estou nervoso com o Magic Argv. Se seu <> O operador faz com que vários arquivos distintos sejam abertos e lidos, então não sei se você poderá fazer backup de um arquivo diferente do que o atualmente está aberto.

Então você pode acabar apenas escrevendo a lógica de pushback. Ou isso, ou impondo alguma restrição de tipo ao processamento de argv relacionado a vários arquivos de entrada e/ou à natureza de STDIN.

A maioria dos meus programas com o processamento Magic ARGV tem guardas no início que lêem algo como:

if (@ARGV == 0 && -t STDIN) {
    # select one or the other of the next two lines:

    # opt 1: emit warning 
    warn "$0: reading stdin from /dev/tty\n";

    # opt 2: populate @ARGV
    @ARGV = grep { -f && -T } <*>;  # glob plain textfiles

 }

No segundo caso acima, onde o padrão é de todos os arquivos de texto simples no diretório atual, também deve decidir o que fazer se grep produz a lista vazia.

Para alguns programas que esperam ou pelo menos admitem argumentos de diretório, ocasionalmente vou inicializar um vazio @ARGV para "." Em vez disso, para que o programa indique o diretório de trabalho atual do processo.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top