No Python, como leio em um arquivo binário e faço um loop sobre cada byte desse arquivo?
Python 2.4 e anteriores
f = open("myfile", "rb")
try:
byte = f.read(1)
while byte != "":
# Do stuff with byte.
byte = f.read(1)
finally:
f.close()
Python 2.5-2.7
with open("myfile", "rb") as f:
byte = f.read(1)
while byte != "":
# Do stuff with byte.
byte = f.read(1)
Observe que a instrução with não está disponível nas versões do Python abaixo de 2.5. Para usá-lo na v 2.5, você precisará importá-lo:
from __future__ import with_statement
Na versão 2.6, isso não é necessário.
Python 3
No Python 3, é um pouco diferente. Não vamos mais obter caracteres brutos do fluxo no modo de byte, mas objetos de byte; portanto, precisamos alterar a condição:
with open("myfile", "rb") as f:
byte = f.read(1)
while byte != b"":
# Do stuff with byte.
byte = f.read(1)
Ou, como Benhoyt diz, pule o que não é igual e tire proveito do fato de que b""
avalia como falso. Isso torna o código compatível entre 2.6 e 3.x sem nenhuma alteração. Isso também evitaria que você alterasse a condição se passar do modo de byte para texto ou vice-versa.
with open("myfile", "rb") as f:
byte = f.read(1)
while byte:
# Do stuff with byte.
byte = f.read(1)
python 3.8
A partir de agora, graças ao operador: =, o código acima pode ser escrito de forma mais curta.
with open("myfile", "rb") as f:
while (byte := f.read(1)):
# Do stuff with byte.
Este gerador gera bytes de um arquivo, lendo o arquivo em pedaços:
Consulte a documentação do Python para obter informações sobre iteradores e geradores .
fonte
8192 Byte = 8 kB
(na verdade é,KiB
mas isso não é tão conhecido). O valor é "totalmente" aleatória, mas 8 kB parece ser um valor adequado: não muita memória é desperdiçado e ainda não existem "muitas" as operações de leitura como na resposta aceita por Skurmedel ...for b in chunk:
loop mais internoyield from chunk
. Esta forma deyield
foi adicionada no Python 3.3 (consulte Expressões de rendimento ).Se o arquivo não for muito grande, é um problema mantê-lo na memória:
onde process_byte representa alguma operação que você deseja executar no byte passado.
Se você deseja processar um pedaço de cada vez:
A
with
declaração está disponível no Python 2.5 e superior.fonte
Para ler um arquivo - um byte de cada vez (ignorando o tampão) - você poderia usar o dois-argumento
iter(callable, sentinel)
construído em função de :Ele chama
file.read(1)
até retornar nadab''
(bytestring vazio). A memória não cresce ilimitada para arquivos grandes. Você pode passarbuffering=0
paraopen()
, para desativar o buffer - garante que apenas um byte seja lido por iteração (lento).with
A instrução fecha o arquivo automaticamente - incluindo o caso em que o código abaixo gera uma exceção.Apesar da presença de buffer interno por padrão, ainda é ineficiente processar um byte de cada vez. Por exemplo, aqui está o
blackhole.py
utilitário que come tudo o que é dado:Exemplo:
Ele processa ~ 1,5 GB / s quando está
chunksize == 32768
na minha máquina e apenas ~ 7,5 MB / s quandochunksize == 1
. Ou seja, é 200 vezes mais lento ler um byte de cada vez. Leve isso em consideração se você puder reescrever seu processamento para usar mais de um byte por vez e se precisar de desempenho.mmap
permite tratar um arquivo comobytearray
e um objeto de arquivo simultaneamente. Pode servir como uma alternativa para carregar o arquivo inteiro na memória, se você precisar acessar as duas interfaces. Em particular, você pode iterar um byte de cada vez em um arquivo mapeado na memória usando apenas umfor
loop simples :mmap
suporta a notação de fatia. Por exemplo,mm[i:i+len]
retornalen
bytes do arquivo começando na posiçãoi
. O protocolo do gerenciador de contexto não é suportado antes do Python 3.2; você precisa ligarmm.close()
explicitamente nesse caso. A iteração de cada byte usandommap
consome mais memória quefile.read(1)
, masmmap
é uma ordem de magnitude mais rápida.fonte
numpy
matrizes equivalentes mapeadas na memória (bytes).numpy.memmap()
e você pode obter os dados um byte por vez (ctypes.data). Você pode pensar em matrizes numpy como um pouco mais do que blobs na memória + metadados.O novo
pathlib
módulo do Python 3.5 é o módulo, que possui um método de conveniência específico para ler um arquivo como bytes, permitindo a iteração sobre os bytes. Considero isso uma resposta decente (se rápida e suja):Interessante que esta é a única resposta a ser mencionada
pathlib
.No Python 2, você provavelmente faria isso (como Vinay Sajip também sugere):
No caso de o arquivo ser muito grande para iterar na memória, você o agruparia, idiomaticamente, usando a
iter
função com acallable, sentinel
assinatura - a versão do Python 2:(Várias outras respostas mencionam isso, mas poucas oferecem um tamanho de leitura razoável.)
Prática recomendada para arquivos grandes ou leitura em buffer / interativa
Vamos criar uma função para fazer isso, incluindo usos idiomáticos da biblioteca padrão para Python 3.5+:
Note que usamos
file.read1
.file.read
blocos até obter todos os bytes solicitados ouEOF
.file.read1
nos permite evitar o bloqueio, e ele pode retornar mais rapidamente por causa disso. Nenhuma outra resposta menciona isso também.Demonstração do uso das melhores práticas:
Vamos criar um arquivo com um megabyte (na verdade mebibyte) de dados pseudo-aleatórios:
Agora vamos iterar sobre ele e materializá-lo na memória:
Podemos inspecionar qualquer parte dos dados, por exemplo, os últimos 100 e os primeiros 100 bytes:
Não itere por linhas para arquivos binários
Não faça o seguinte - isso puxa um pedaço de tamanho arbitrário até chegar a um caractere de nova linha - muito lento quando os pedaços são muito pequenos e, possivelmente, muito grandes também:
O que foi dito acima é bom apenas para arquivos de texto legíveis semanticamente humanos (como texto sem formatação, código, marcação, remarcação etc ... essencialmente qualquer coisa codificada como ascii, utf, latin, etc ...) que você deve abrir sem a
'b'
bandeira.fonte
path = Path(path), with path.open('rb') as file:
vez de usar a função aberta integrada? Ambos fazem a mesma coisa correta?Path
objeto porque é uma nova maneira muito conveniente de lidar com caminhos. Em vez de passar uma string para as funções "certas" cuidadosamente escolhidas, podemos simplesmente chamar os métodos no objeto path, que contém essencialmente a maior parte da funcionalidade importante que você deseja com o que é semanticamente uma string de caminho. Com IDEs que podem inspecionar, também podemos obter o preenchimento automático com mais facilidade. Poderíamos fazer o mesmo com oopen
built-in, mas há muitas vantagens ao escrever o programa para o programador usar oPath
objeto.file_byte_iterator
é muito mais rápido que todos os métodos que tentei nesta página. Parabéns a você!Para resumir todos os pontos brilhantes de chrispy, Skurmedel, Ben Hoyt e Peter Hansen, essa seria a solução ideal para processar um arquivo binário, um byte de cada vez:
Para python versões 2.6 e acima, porque:
Ou use a solução JF Sebastians para aumentar a velocidade
Ou se você quiser como uma função geradora, como demonstrado pelo codeape:
fonte
Python 3, leia todo o arquivo de uma vez:
Você pode iterar o que quiser usando a
data
variável.fonte
Depois de tentar todas as opções acima e usar a resposta do @Aaron Hall, eu estava recebendo erros de memória para um arquivo de ~ 90 Mb em um computador executando Windows 10, 8 Gb de RAM e Python 3.5 de 32 bits. Fui recomendado por um colega para usar
numpy
e ele faz maravilhas.De longe, o mais rápido para ler um arquivo binário inteiro (que eu testei) é:
Referência
Multidões mais rápido do que qualquer outro método até agora. Espero que ajude alguém!
fonte
numpy
, poderá valer a pena.Se você tiver muitos dados binários para ler, considere o módulo struct . Ele está documentado como convertendo "entre tipos C e Python", mas é claro que bytes são bytes, e se esses foram criados como tipos C não importa. Por exemplo, se seus dados binários contiverem dois inteiros de 2 bytes e um inteiro de 4 bytes, você poderá lê-los da seguinte maneira (exemplo extraído da
struct
documentação):Você pode achar isso mais conveniente, mais rápido ou ambos, do que fazer um loop explícito no conteúdo de um arquivo.
fonte
Este post em si não é uma resposta direta à pergunta. Em vez disso, é um benchmark extensível orientado a dados que pode ser usado para comparar muitas das respostas (e variações da utilização de novos recursos adicionados em versões posteriores e mais modernas do Python) postadas nesta pergunta - e, portanto, devem ser seja útil para determinar qual é o melhor desempenho.
Em alguns casos, modifiquei o código na resposta referenciada para torná-lo compatível com a estrutura de benchmark.
Primeiro, aqui estão os resultados para as atuais versões mais recentes do Python 2 e 3:
Também o executei com um arquivo de teste de 10 MiB muito maior (que levou quase uma hora para ser executado) e obtive resultados de desempenho comparáveis aos mostrados acima.
Aqui está o código usado para fazer o benchmarking:
fonte
yield from chunk
simfor byte in chunk: yield byte
? Estou pensando que devo reforçar minha resposta com isso.yield from
.enumerate
pois a iteração deve ser concluída - se não, pela última vez que verifiquei - a enumeração tem um pouco de sobrecarga nos custos em fazer a contabilidade do índice com + = 1; portanto, você pode alternativamente fazer a contabilidade em seu próprio código. Ou mesmo passar para um deque commaxlen=0
.enumerate
. Obrigado pelo feedback. Estarei adicionando uma atualização ao meu post que não a possui (embora eu ache que não mude muito os resultados). Também estará adicionando anumpy
resposta baseada em @Rick M.super().
vez detuple.
no seu__new__
você poderia usar osnamedtuple
nomes de atributo em vez de índices.se você está procurando algo rápido, aqui está um método que eu tenho usado e trabalha há anos:
se você deseja iterar chars em vez de ints, pode simplesmente usar
data = file.read()
, que deve ser um objeto bytes () em py3.fonte