Чому я не можу двічі викликати read () у відкритому файлі?


98

Для вправи, яку я роблю, я намагаюся двічі прочитати вміст даного файлу, використовуючи 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,

Я вважаю, що закриття та повторне відкриття файлу має працювати на основі наведених нижче відповідей.
Ентоні,

@Shynthriir: Закриття та повторне відкриття файлу не завжди є гарною ідеєю, оскільки це може мати інші ефекти в системі (тимчасові файли, інкрон тощо).
Ігнасіо Васкес-Абрамс,

3
Я просто хочу сказати очевидне: Ви ДВИГАЛИ дзвінок read () двічі!

4
W / R / T / S.Lott, і починаючи з 5 років: це дійсно повинно бути в документації python. Не очевидно, що слід припускати, що читання файлового об'єкта може змінити стан будь-чого, особливо якщо він звик працювати з незмінними даними / програмуванням у функціональному стилі ...
Пол Гоудер,

Відповіді:


156

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

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

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


4
+1, так, будь ласка, прочитайте тимчасову змінну, щоб уникнути непотрібного введення / виводу файлів. Це помилкова економія, що ви економите будь-яку пам’ять, оскільки у вас менше (явних) змінних.
Nick T

2
@NickT: Я мав би очікувати, що невеликий файл, який читається кілька разів, кешується ОС (принаймні, на Linux / OSX), тому немає додаткового вводу-виводу файлу для читання двічі. Великі файли, які не поміщаються в пам’яті, не кешуються, але не потрібно читати їх у змінну, оскільки ви почнете обмінюватися. Тож у разі сумнівів завжди читайте кілька разів. Якщо ви точно знаєте, що файли невеликі, зробіть все, що дає найкращу програму.
Клод,

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байти.


1

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.