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

#python

#python #io

Вопрос:

Для упражнения, которое я выполняю, я пытаюсь дважды прочитать содержимое данного файла, используя 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() дважды? Нужно ли сбрасывать дескриптор файла? Или закрыть / повторно открыть файл, чтобы сделать это?

Комментарии:

1. Откуда вы взяли идею, что чтение не изменит состояние файла? Какую ссылку или учебник вы используете?

2. Я считаю, что закрытие и повторное открытие файла должны работать на основе приведенных ниже ответов.

3. @Shynthriir: закрытие и повторное открытие файла не всегда хорошая идея, поскольку это может иметь другие последствия в системе (временные файлы, incron и т. Д.).

4. Я просто хочу констатировать очевидное: вы дважды вызывали read()!

5. W / R / T / S.Лотт, и начиная с 5 лет: это действительно должно быть в документации python. Не очевидно, что следует предполагать, что чтение объекта file изменит состояние чего-либо, особенно если вы привыкли работать с неизменяемыми данными / программированием в функциональном стиле…

Ответ №1:

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

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

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

Комментарии:

1. 1, да, пожалуйста, прочитайте во временную переменную, чтобы избежать ненужного ввода-вывода файла. Это ложная экономия, что вы экономите память, потому что у вас меньше (явных) переменных.

2. @NickT: Я бы ожидал, что небольшой файл, считываемый несколько раз, кэшируется ОС (по крайней мере, в Linux / OSX), поэтому нет дополнительного ввода-вывода файлов для чтения дважды. Большие файлы, которые не помещаются в память, не кэшируются, но вы не хотите считывать их в переменную, потому что вы начнете менять местами. Поэтому в случае сомнений всегда читайте несколько раз. Если вы точно знаете, что файлы маленькие, делайте все, что дает самую приятную программу.

3. Удаление можно автоматизировать с помощью . with

Ответ №2:

Как предлагалось в других ответах, вы должны использовать seek() .

Я просто напишу пример:

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

Ответ №3:

Все, кто до сих пор отвечал на этот вопрос, абсолютно правы — 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 /… ). Но почему-то я не подумал о сохранении строки в переменной. Ого!

2. В Python3 используйте pathlib. from pathlib import Path; text = Path(filename).read_text() Заботится об открытии, закрытии и т. Д.

Ответ №4:

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

Ответ №5:

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

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

Ответ №6:

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

Ответ №7:

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