Dlaczego nie mogę dwukrotnie wywołać read () w przypadku otwartego pliku?


100

W przypadku ćwiczenia, które wykonuję, próbuję dwukrotnie odczytać zawartość danego pliku read()metodą. O dziwo, kiedy wywołuję to po raz drugi, nie zwraca zawartości pliku jako ciągu?

Oto kod

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

Oczywiście wiem, że to nie jest najbardziej efektywny ani najlepszy sposób, nie o to tutaj chodzi. Chodzi o to, dlaczego nie mogę zadzwonić read()dwa razy? Czy muszę zresetować uchwyt pliku? Lub zamknij / ponownie otwórz plik, aby to zrobić?


2
Skąd pomysł, że odczyt nie zmieni stanu pliku? Z jakiego źródła lub samouczka korzystasz?
S.Lott,

Uważam, że zamykanie i ponowne otwieranie pliku powinno działać w oparciu o poniższe odpowiedzi.
Anthony,

@Shynthriir: Zamykanie i ponowne otwieranie pliku nie zawsze jest dobrym pomysłem, ponieważ może mieć inne skutki w systemie (pliki tymczasowe, incron itp.).
Ignacio Vazquez-Abrams

3
Chcę tylko powiedzieć, co oczywiste: WYWOŁAŁEŚ read () dwa razy!

4
W / R / T / S.Lott i od 5 lat: to naprawdę musi być w dokumentacji Pythona. Nie jest oczywiste, że należy założyć, że odczytanie obiektu pliku zmieniłoby stan czegokolwiek, zwłaszcza jeśli ktoś jest przyzwyczajony do pracy z niezmiennymi danymi / programowaniem w stylu funkcjonalnym ...
Paul Gowder

Odpowiedzi:


157

Wywołanie read()czyta cały plik i pozostawia kursor odczytu na końcu pliku (bez niczego więcej do przeczytania). Jeśli szukasz czytać pewną liczbę wierszy w danym momencie można użyć readline(), readlines()lub iterację liniach for line in handle:.

Aby odpowiedzieć bezpośrednio na twoje pytanie, po przeczytaniu pliku read()możesz użyć, seek(0)aby przywrócić kursor odczytu na początek pliku (dokumenty są tutaj ). Jeśli wiesz, że plik nie będzie zbyt duży, możesz również zapisać read()wynik do zmiennej, używając jej w swoich wyrażeniach findall.

Ps. Nie zapomnij zamknąć pliku, gdy skończysz;)


4
+1, Tak, proszę przeczytać zmienną tymczasową, aby uniknąć niepotrzebnego wejścia / wyjścia pliku. To fałszywa ekonomia, że ​​oszczędzasz jakąkolwiek pamięć, ponieważ masz mniej (jawnych) zmiennych.
Nick T

2
@NickT: Spodziewałbym się, że mały plik odczytywany wiele razy zostanie zapisany w pamięci podręcznej systemu operacyjnego (przynajmniej w systemie Linux / OSX), więc nie ma dodatkowego wejścia / wyjścia pliku do dwukrotnego odczytu. Duże pliki, które nie mieszczą się w pamięci, nie są zapisywane w pamięci podręcznej, ale nie chcesz czytać ich w zmiennej, ponieważ zaczniesz zmieniać. Dlatego w razie wątpliwości zawsze czytaj wiele razy. Jeśli wiesz na pewno, że pliki są małe, zrób wszystko, co daje najlepszy program.
Claude

3
Zrywanie można zautomatyzować za pomocą with.
Cees Timmerman

30

tak, jak wyżej ...

napiszę tylko przykład:

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

17

Każdy, kto do tej pory odpowiedział na to pytanie, ma całkowitą rację - read()przechodzi przez plik, więc po wywołaniu go nie można go ponownie wywołać.

Dodam, że w twoim konkretnym przypadku nie musisz szukać początku ani ponownie otwierać pliku, możesz po prostu zapisać przeczytany tekst w zmiennej lokalnej i użyć go dwukrotnie, lub tyle razy, ile chcesz, w swoim programie:

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

1
+1 Właściwie to było proponowane rozwiązanie dla tego ćwiczenia ( code.google.com/intl/de-DE/edu/languages/google-python-class/… ). Ale jakoś nie pomyślałem o przechowywaniu łańcucha w zmiennej. D'oh!
helpermethod

1
W Pythonie3 użyj pathlib. from pathlib import Path; text = Path(filename).read_text()
Dba

14

Wskaźnik odczytu przesuwa się za ostatnim odczytanym bajtem / znakiem. Użyj seek()metody, aby przewinąć wskaźnik odczytu do początku.


2

Każdy otwarty plik ma przypisaną pozycję.
Kiedy czytasz (), czytasz z tej pozycji. Na przykład read(10)czyta pierwsze 10 bajtów z nowo otwartego pliku, a następnie kolejny read(10)czyta następne 10 bajtów. read()bez argumentów odczytuje całą zawartość pliku, pozostawiając pozycję pliku na końcu pliku. Następnym razem, gdy zadzwonisz, read()nie ma nic do czytania.

Możesz użyć, seekaby przesunąć pozycję pliku. Lub prawdopodobnie lepiej w twoim przypadku byłoby zrobić jedno read()i zachować wynik dla obu wyszukiwań.


1

read() konsumuje . Tak więc, można przywrócić plik lub szukać na początku przed ponownym czytaniu. Lub, jeśli pasuje do twojego zadania, możesz użyć read(n)do zużywania tylko nbajtów.


1

Zawsze uważam, że metoda czytania jest czymś w rodzaju spaceru ciemną uliczką. Schodzisz trochę w dół i zatrzymujesz się, ale jeśli nie liczysz kroków, nie jesteś pewien, jak daleko jesteś. Seek daje rozwiązanie poprzez zmianę pozycji, drugą opcją jest Tell, która zwraca pozycję wzdłuż pliku. Może to być plik API Pythona, który może łączyć odczyt i wyszukiwanie w read_from (pozycja, bajty), aby było to prostsze - do tego czasu powinieneś przeczytać tę stronę .

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.