Почему я не могу дважды вызвать read () для открытого файла?

100

В своем упражнении я пытаюсь дважды прочитать содержимое данного файла с помощью этого read()метода. Как ни странно, когда я вызываю его второй раз, кажется, что он не возвращает содержимое файла в виде строки?

Вот код

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

Конечно, я знаю, что это не самый эффективный или лучший способ, дело не в этом. Дело в том, почему я не могу позвонить read()дважды? Мне нужно сбросить дескриптор файла? Или закрыть / снова открыть файл для этого?

вспомогательный метод
источник
2
Откуда вы взяли, что чтение не меняет состояние файла? Какой справочник или учебник вы используете?
S.Lott
Я считаю, что закрытие и повторное открытие файла должно работать на основе ответов ниже.
Энтони,
1
@Shynthriir: Закрытие и повторное открытие файла не всегда является хорошей идеей, поскольку это может иметь другие эффекты в системе (временные файлы, incron и т. Д.).
Игнасио Васкес-Абрамс,
3
Я просто хочу заявить очевидное: вы ДЕЙСТВИТЕЛЬНО вызывали read () дважды!
4
W / R / T / S.Lott, и через 5 лет: это действительно должно быть в документации python. Не очевидно, что следует предполагать, что чтение файлового объекта изменит состояние чего-либо, особенно если вы привыкли работать с неизменяемыми данными / программированием в функциональном стиле ...
Пол Гаудер 02

Ответы:

157

Вызов read()выполняет чтение всего файла и оставляет курсор чтения в конце файла (читать больше нечего). Если вы хотите прочитать определенное количество строк , в то время , вы могли бы использовать readline(), readlines()или итерацию по линиям с for line in handle:.

Чтобы ответить на ваш вопрос напрямую, как только файл был прочитан, read()вы можете использовать его seek(0)для возврата курсора чтения в начало файла (документы здесь ). Если вы знаете, что файл не будет слишком большим, вы также можете сохранить read()вывод в переменной, используя его в своих выражениях findall.

Пс. Не забудьте закрыть файл после того, как закончите с ним;)

Тим
источник
4
+1, Да, пожалуйста, прочтите временную переменную, чтобы избежать ненужного ввода-вывода файлов. Это ложная экономия, когда вы экономите память, потому что у вас меньше (явных) переменных.
Nick T
2
@NickT: я бы ожидал, что небольшой файл, который читается несколько раз, кэшируется ОС (по крайней мере, в Linux / OSX), поэтому нет дополнительного ввода-вывода файла для чтения дважды. Большие файлы, которые не помещаются в памяти, не кэшируются, но вы не хотите читать их в переменную, потому что вы начнете менять местами. Так что в случае сомнений всегда читайте несколько раз. Если вы точно знаете, что файлы маленькие, делайте все, что вам подходит.
Claude
3
Разборку можно автоматизировать с помощью with.
Cees Timmerman
30

да, как указано выше ...

напишу только пример:

>>> a = open('file.txt')
>>> a.read()
#output
>>> a.seek(0)
>>> a.read()
#same output
Муравей
источник
17

Все, кто ответил на этот вопрос, абсолютно прав - read()перемещаются по файлу, поэтому после того, как вы его вызвали, вы не можете вызвать его снова.

Я добавлю, что в вашем конкретном случае вам не нужно возвращаться к началу или повторно открывать файл, вы можете просто сохранить текст, который вы прочитали в локальной переменной, и использовать его дважды, или сколько угодно раз в своей программе:

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 На самом деле это было предложенное решение для этого упражнения ( code.google.com/intl/de-DE/edu/languages/google-python-class/… ). Но почему-то я не подумал о том, чтобы сохранить строку в переменной. Ооо!
helpermethod
1
В Python3 используйте pathlib. from pathlib import Path; text = Path(filename).read_text()
Заботится
14

Указатель чтения перемещается после последнего прочитанного байта / символа. Используйте seek()метод, чтобы перемотать указатель чтения в начало.

Игнасио Васкес-Абрамс
источник
2

У каждого открытого файла есть связанная позиция.
Когда вы читаете (), вы читаете с этой позиции. Например, read(10)читает первые 10 байтов из вновь открытого файла, затем другой read(10)читает следующие 10 байтов. read()без аргументов читает все содержимое файла, оставляя позицию файла в конце файла. В следующий раз, когда вы позвоните, read()читать нечего.

Вы можете использовать seekдля перемещения позиции файла. Или, возможно, в вашем случае лучше было бы сделать один read()и сохранить результат для обоих поисков.

Дуглас Лидер
источник
1

read() потребляет . Таким образом, вы можете сбросить файл, или обратиться к старту до повторного чтения. Или, если это соответствует вашей задаче, вы можете использовать read(n)только nбайты.

Towi
источник
1

Я всегда нахожу метод чтения чем-то вроде прогулки по темному переулку. Вы немного спускаетесь и останавливаетесь, но если вы не считаете свои шаги, вы не знаете, как далеко вы продвинулись. Seek дает решение путем изменения положения, другой вариант - Tell, который возвращает позицию вдоль файла. Возможно, api файла Python может объединять чтение и поиск в read_from (позиция, байты), чтобы упростить задачу - пока это не произойдет, вы должны прочитать эту страницу .

Whatnick
источник