Por que não consigo chamar read () duas vezes em um arquivo aberto?

98

Para um exercício que estou fazendo, estou tentando ler o conteúdo de um determinado arquivo duas vezes usando o read()método. Estranhamente, quando o chamo pela segunda vez, ele não parece retornar o conteúdo do arquivo como uma string?

Aqui está o código

f = f.open()

# get the year
match = re.search(r'Popularity in (\d+)', f.read())

if match:
  print match.group(1)

# get all the names
matches = re.findall(r'<td>(\d+)</td><td>(\w+)</td><td>(\w+)</td>', f.read())

if matches:
  # matches is always None

Claro que sei que essa não é a maneira mais eficiente ou a melhor, esse não é o ponto aqui. A questão é: por que não posso ligar read()duas vezes? Tenho que redefinir o identificador de arquivo? Ou feche / reabra o arquivo para fazer isso?

método auxiliar
fonte
2
De onde você tirou a ideia de que ler não mudaria o estado do arquivo? Que referência ou tutorial você está usando?
S.Lott,
Acredito que fechar e reabrir o arquivo deve funcionar com base nas respostas abaixo.
Anthony,
@Shynthriir: Fechar e reabrir o arquivo nem sempre é uma boa ideia, pois pode ter outros efeitos no sistema (arquivos temporários, incron, etc.).
Ignacio Vazquez-Abrams
3
Eu só quero dizer o óbvio: Você DID leitura call () duas vezes!
4
W / R / T / S.Lott, e a partir de 5 anos: isso realmente precisa estar na documentação do python. Não é óbvio que se deva supor que a leitura de um objeto de arquivo mudaria o estado de qualquer coisa, especialmente se alguém está acostumado a trabalhar com dados imutáveis ​​/ programação de estilo funcional ...
Paul Gowder

Respostas:

156

Chamar read()lê todo o arquivo e deixa o cursor de leitura no final do arquivo (sem nada mais para ler). Se você estiver olhando para ler um determinado número de linhas de cada vez que você poderia usar readline(), readlines()ou iterate através de linhas com for line in handle:.

Para responder à sua pergunta diretamente, uma vez que um arquivo foi lido, read()você pode usar seek(0)para retornar o cursor de leitura ao início do arquivo (os documentos estão aqui ). Se você sabe que o arquivo não será muito grande, você também pode salvar a read()saída em uma variável, usando-a em suas expressões findall.

Ps. Não se esqueça de fechar o arquivo depois de terminar;)

Tim
fonte
4
+1, Sim, leia a variável temporária para evitar E / S desnecessária de arquivo. É uma falsa economia que você está economizando memória porque tem menos variáveis ​​(explícitas).
Nick T
2
@NickT: Eu esperaria que um pequeno arquivo lido várias vezes fosse armazenado em cache pelo sistema operacional (pelo menos no Linux / OSX), portanto, nenhum arquivo extra de E / S para leitura duas vezes. Arquivos grandes que não cabem na memória não são armazenados em cache, mas você não deseja lê-los em uma variável porque começará a trocar. Portanto, em caso de dúvida, sempre leia várias vezes. Se você tem certeza de que os arquivos são pequenos, faça o que for melhor para o programa.
Claude
3
A demolição pode ser automatizada com with.
Cees Timmerman
30

sim, como acima ...

vou escrever apenas um exemplo:

>>> a = open('file.txt')
>>> a.read()
#output
>>> a.seek(0)
>>> a.read()
#same output
Formiga
fonte
17

Todos que responderam a essa pergunta até agora estão absolutamente certos - read()percorrem o arquivo, então, depois de chamá-lo, você não pode chamá-lo novamente.

O que acrescentarei é que, em seu caso particular, você não precisa voltar ao início ou reabrir o arquivo, você pode simplesmente armazenar o texto que leu em uma variável local e usá-lo duas vezes, ou quantas vezes você quiser, em seu programa:

f = f.open()
text = f.read() # read the file into a local variable
# get the year
match = re.search(r'Popularity in (\d+)', text)
if match:
  print match.group(1)
# get all the names
matches = re.findall(r'<td>(\d+)</td><td>(\w+)</td><td>(\w+)</td>', text)
if matches:
  # matches will now not always be None
Tom Anderson
fonte
1
+1 Na verdade, esta foi a solução proposta para este exercício ( code.google.com/intl/de-DE/edu/languages/google-python-class/… ). Mas, de alguma forma, não pensei em armazenar a string em uma variável. D'oh!
método do ajudante de
1
Com Python3, use pathlib. from pathlib import Path; text = Path(filename).read_text()Cuida de abrir, fechar, etc.
PaulMcG
14

O ponteiro de leitura move-se para depois do último byte / caractere lido. Use o seek()método para retroceder o ponteiro de leitura até o início.

Ignacio Vazquez-Abrams
fonte
2

Cada arquivo aberto tem uma posição associada.
Quando você lê (), você lê daquela posição. Por exemplo, read(10)lê os primeiros 10 bytes de um arquivo recém-aberto, depois outro read(10)lê os próximos 10 bytes. read()sem argumentos lê todo o conteúdo do arquivo, deixando a posição do arquivo no final do arquivo. Na próxima vez que você ligar, read()não haverá nada para ler.

Você pode usar seekpara mover a posição do arquivo. Ou provavelmente melhor no seu caso seria fazer um read()e manter o resultado para ambas as pesquisas.

Douglas Leeder
fonte
1

read() consome . Portanto, você pode redefinir o arquivo ou procurar o início antes de relê-lo. Ou, se for adequado para sua tarefa, você pode usar read(n)para consumir apenas nbytes.

towi
fonte
1

Sempre acho o método de leitura algo como uma caminhada por um beco escuro. Você desce um pouco e para, mas se não está contando seus passos, não tem certeza de quão longe está. Seek fornece a solução por reposicionamento, a outra opção é Tell, que retorna a posição ao longo do arquivo. Pode ser que o arquivo Python api possa combinar leitura e busca em um read_from (posição, bytes) para torná-lo mais simples - até que isso aconteça, você deve ler esta página .

whatnick
fonte