import csv
with open('test.csv', 'r') as f:
reader = csv.reader(f)
for i in reader:
print(i)
CSV
id,name
001,jane
002,winky
003,beli
...
Пока программа будет читать только один раз файл csv. Программа будет читать с первых строк 001
при повторном запуске. Как я могу возобновить чтение, как в примере, если программа останавливает чтение на 002
, тогда следующим началом чтения будет 003
?
Что означает записать содержимое в другой файл, а затем начать сравнивать оба файла?
Возможный дубликат CSV читать определенную строку
Проще всего было бы сохранить количество строк, которые вы уже прочитали, и пропустить это количество вперед при следующем запуске.
@mrHOT не дубликат. Сначала прочитайте вопрос, пожалуйста.
Я просто хочу спросить, что вы имеете в виду под перезапуском, это выйти из программы, нажав crtl-c ??
если вы прервете цикл после, скажем, 2 итераций, вы увидите, что цикл снова возобновляет чтение, поскольку итератор не сбрасывается. Можете ли вы объяснить больше, чего вы хотите достичь?
@Jean-FrançoisFabre У меня проблема, когда программе внезапно не хватает памяти из-за другой непредсказуемой ситуации, из-за которой программа завершается. В этой ситуации мне нужно перезапустить программу, но я хочу, чтобы она возобновила последнюю строку записи.
@ kino.jom, если это проблема, вы можете просто установить флаг, когда вы достигнете n времени чтения строк (например, 500 строк), вы можете, например, дать вашей программе отдохнуть в течение 4 секунд, вполне естественно, что ваш заканчивается память, если это большой цикл без тайм-аута (время сна или отдыха)
@MobrineHayde Спасибо... поищу.
Как сценарий узнает, должен ли он возобновить чтение или начать сначала?
@MobrineHayde: приостановка программы не повлияет на объем используемой ею памяти — только ее завершение займет больше времени.
Для этого вам нужно отслеживать, насколько далеко вы прочитали файл на данный момент, file.tell()
может пригодиться. После этого вы можете начать читать свой файл с этого момента, используя file.seek()
.
Код будет выглядеть примерно так:
def read_from_position(last_position):
file = open("file_location")
file.seek(last_position)
file.readline() # Do what you want with this
return file.tell() # this is the updated last position
Вы можете добиться того же в своем коде, отслеживая, сколько строк вы прочитали в последний раз, и итерируя публикацию этого количества строк.
readline()
может выполнять упреждающее чтение и буферизацию за кулисами, в результате чего file.tell()
дает результаты, которые не обязательно совпадают с вашим прогрессом чтения.
В этом случае вы должны явно сохранять текущее местоположение каждый раз, что может быть немного затратным в вычислительном отношении, но это работает, и вот код:
import csv
def update_last(x):
with open('last.txt', 'w') as file:
file.write(str(x))
def get_last():
try:
with open('last.txt', 'r') as file:
return int(file.read().strip())
except:
with open('last.txt', 'w') as file:
file.write('0')
return 0
with open('your_file.txt', 'r') as f:
reader = csv.reader(f)
last = get_last() + 1
current = 1
for i in reader:
if current < last:
current += 1
continue
print(i)
current += 1
update_last(current)
pathlib.Path.write_text
упрощает многое из этого open/write/close
материала. И пусть ваш обработчик исключений в get_last
вызове update_last(0)
- СУХОЙ!
@PaulMcG: метод Path.write_text()
не был добавлен до Python 3.5, поэтому нет, использующий его, делает этот код совместимым с большим количеством версий ... кроме того, что делать это «старомодным» способом не так уж много работы в этом конкретном случае случае (ИМО). (На самом деле, я немного удивлен, что они вообще удосужились добавить такое тривиальное, как write_text()
.)
mrHOT: FWIW, я думаю, что в целом это правильный подход, и ваша реализация, безусловно, очень лаконична. Тем не менее, открытие и закрытие файла last.txt
каждый раз, когда читается строка, добавит много накладных расходов, особенно потому, что для этого требуется несколько вызовов ОС (что, как правило, очень дорого). У меня есть одно предложение: не записывать в него имя файла last.txt
, потому что это предотвращает использование кода более чем с одним CSV-файлом одновременно. Кроме того, необходим дополнительный код для удаления файла, когда он больше не нужен...
Используйте магию генераторов:
def get_rows(infile='test.csv'):
with open(infile) as f:
reader = csv.reader(f)
for row in reader:
yield row
for id, name in get_rows():
out = some_complex_business_logic(id, name)
print(out)
Генератор приостановится, пока вы запускаете свою сложную бизнес-логику, а затем прозрачно возобновить, когда вы будете готовы к следующей строке.
Для этого вам нужно будет постоянно сохранять текущее местоположение в другом файле каждый раз, когда строка считывается из CSV-файла, что, конечно, добавит некоторые накладные расходы на ее обработку.
Я думаю, что создание Тип диспетчера контекста в сочетании с оператором with
было бы очень хорошим подходом для решения этой проблемы и позволило бы в некоторой степени минимизировать накладные расходы.
В приведенном ниже коде реализуется диспетчер содержимого для чтения CSV-файлов, который позволяет автоматически возобновлять его или их чтение, если оно было прервано до того, как был прочитан весь файл (в контексте оператора with
).
Это делается путем создания отдельного файла «состояния» для отслеживания последней успешно прочитанной строки. Этот файл будет удален, если во время чтения не возникнет исключения, однако этого не произойдет, и он останется, если произойдет. Из-за этого при следующем чтении файла существующий файл состояния будет обнаружен и использован, чтобы позволить начать чтение с того места, где оно было остановлено ранее.
Примечательно, что поскольку каждый возобновляемый читатель CSV является отдельным объектом, вы можете создавать и использовать несколько одновременно. Связанный файл «состояния» для каждого из них остается открытым во время чтения CSV-файла, поэтому его не нужно повторно открывать и закрывать каждый раз при обновлении его содержимого.
import csv
import os
class ResumableCSVReader:
def __init__(self, filename):
self.filename = filename
self.state_filename = filename + '.state'
self.csvfile = None
self.statefile = None
def __enter__(self):
self.csvfile = open(self.filename, 'r', newline='')
try: # Open and read state file
with open(self.state_filename, 'r', buffering=1) as statefile:
self.start_row = int(statefile.read())
except FileNotFoundError: # No existing state file.
self.start_row = 0
self.statefile = open(self.state_filename, 'w', buffering=1)
return _CSVReaderContext(self)
def __exit__(self, exc_type, exc_val, exc_tb):
if self.csvfile:
self.csvfile.close()
if self.statefile:
self.statefile.close()
if not exc_type: # No exception?
os.remove(self.state_filename) # Delete state file.
class _CSVReaderContext:
def __init__(self, resumable):
self.resumable = resumable
self.reader = csv.reader(self.resumable.csvfile)
# Skip to start row.
for _ in range(self.resumable.start_row):
next(self.reader)
self.current_row = self.resumable.start_row
def __iter__(self):
return self
def __next__(self):
self.current_row += 1
row = next(self.reader)
# Update state file.
self.resumable.statefile.seek(0)
self.resumable.statefile.write(str(self.current_row)+'\n')
return row
if __name__ == '__main__':
csv_filename = 'resumable_data.csv'
# Read a few rows and raise an exception.
try:
with ResumableCSVReader(csv_filename) as resumable:
for _ in range(2):
print('row:', next(resumable))
raise MemoryError('Forced') # Cause exception.
except MemoryError:
pass # Expected, suppress to allow test to keep running.
# CSV file is now closed.
# Resume reading where left-off and continue to end of file.
print('\nResume reading\n')
with ResumableCSVReader(csv_filename) as resumable:
for row in resumable:
print('row:', row)
print('\ndone')
Выход:
row: ['id', 'name']
row: ['001', 'jane']
Resume reading
row: ['002', 'winky']
row: ['003', 'beli']
done
Вам придется сохранить свой прогресс в другом файле.