Python: запись в файл внутри цикла, считывающего этот файл?

77
8

Мне интересно, если в python (windows) я могу написать указателю на файл в той же структуре цикла, что и чтение из этого же файла.

Если я хочу добавить к файловому потоку и продолжить чтение (включая письменные данные), будет ли следующая работа?:

f = open('myfile.xyz', "a+b")
f.seek(0, 0)
for l in f:
l = "{}\n".format(l.rstrip()) # convert windows lines to posix, removing other terminal whitespace
specific_condition = do_stuff(l) # returns True/False
if specific_condition:
p = f.tell()
f.seek(0,os.SEEK_END)
f.write(multiline_string)
f.seek(p,os.SEEK_SET)
f.close()

У меня ранее возникали проблемы с python balking всякий раз, когда я меняю объект, который я повторяю (например, удаляя элемент в dict который я повторяю), но поскольку я не меняю указатель на файл в этом случай, есть ли какие-либо проблемы? Отличается ли ответ, если строка, которую я прочитал, была последней строкой в файле?

Мне особенно интересно узнать о python 2.7, но если ответ py3+ отличается, я уверен, что в конце концов это тоже оценю.

спросил(а) 2021-01-25T16:26:39+03:00 4 месяца, 4 недели назад
1
Решение
89

Отвечая на мой вопрос re: py2.7, следующие работы, хотя и запутанные. Заметьте, я не мог использовать синтаксис for line in f синтаксисе, из-за "оптимизации" на "прочитанном" в python.

import base64, os

csvstring = "this,is,a,csv,string\r\nthis,is,another,such,string"
b64string = base64.b64encode(csvstring).decode('utf-8')

f = open('myfile.xyz', "w+b")
f.write(b64string+os.linesep)
f.write(b64string) # no final \n to test case where file doesn't end in \n
f.close()

fmode = "a+b"
f = open('myfile.xyz', fmode)
f.seek(0,os.SEEK_END)
eofpos = f.tell() # aka writepos
try:
if not eofpos: raise EOFError
f.seek(eofpos-1, os.SEEK_SET)
lastchar = f.read(1).replace("\r","\n") # if not \n (or \r), we'll have to append the eol before writing anything else
except (EOFError,IOError,OSError):
eofpos = 0
lastchar="\n" # we don't have to add
eol = os.linesep if fmode.endswith("b") else "\n"
f.seek(0, os.SEEK_SET)
firstline = True
linecount = 0
readpos = 0
while 1:
line = f.readline()
if not line: break # while (line = f.readline()): # empty string is False
#for line in f:
"""
can't use 'for line in f', due to read-ahead of file object
(see https://docs.python.org/2/library/stdtypes.html#file.next)
When a file is used as an iterator, typically in a for loop (for example, for line in f: print line),
the next() method is called repeatedly. This method returns the next input line, or raises StopIteration when EOF is hit.
In order to make a for loop the most efficient way of looping over the lines of a file (a very common operation),
the next() method uses a hidden read-ahead buffer. As a consequence of using a read-ahead buffer, combining next()
with other file methods (like readline()) does not work right. However, using seek() to reposition the file to an absolute
position will flush the read-ahead buffer.
"""
linecount += 1
l = line.rstrip("\n\r") # get rid of terminal end-of-line characters, regardless of the order
print("{}: {} ({})".format(linecount, l, line.replace("\r","\\r").replace("\n","\\n")))
if firstline:
if fmode.endswith("b"):
if len(l) != len(line):
eol = line[len(l):] # grab the end of line used elsewhere in this file, so as to not throw off the file reader later
else:
eol = os.linesep
else: # text files, don't use linesep, use \n
eol = "\n"
firstline = False
if (len(l)):
if l.count(',') < 2:
print("\tLine {} with insufficient delimiters (,) encountered. Trying to see if it encoded. length {} line: {}".format(linecount,len(l),l))
try:
prepend = '' if (lastchar == '\n') else eol
testme = prepend + base64.b64decode(l).decode('utf-8', errors='ignore') + eol
if testme.count(',') >= 1: # here we'll just require 2+ items
readpos = f.tell()
f.seek(eofpos,os.SEEK_SET) # otherwise after a read, the filepointer is set back to the original eol before write
f.write(testme)
eofpos += len(testme)
f.flush() # otherwise the write may be thrown out with subsequent re-writes
f.seek(readpos,os.SEEK_SET)
lastchar = '\n' # the end of file should now either be \r or \r\n after the next write
except (TypeError,UnicodeError): # e.g. padding error
print("\t\tWARNING: line not able to be unencoded")
except (EOFError,IOError,OSError):
print("\t\tERROR: writing unencoded data back to file")
else: # 3 or more items, 2 or more commas
datstrm = l.split(",")
print("\t{} item line {} OK: {}".format(len(datstrm),linecount,l))
else:
print("\tBlank line encountered at line {}".format(linecount))
f.close()

вывод:

1: dGhpcyxpcyxhLGNzdixzdHJpbmcNCnRoaXMsaXMsYW5vdGhlcixzdWNoLHN0cmluZw== (dGhpcyxpcyxhLGNzdixzdHJpbmcNCnRoaXMsaXMsYW5vdGhlcixzdWNoLHN0cmluZw==\r\n)
Line 1 with insufficient delimiters (,) encountered. Trying to see if it encoded. length 68 line: dGhpcyxpcyxhLGNzdixzdHJpbmcNCnRoaXMsaXMsYWdWNoLHN0cmluZw==
2: dGhpcyxpcyxhLGNzdixzdHJpbmcNCnRoaXMsaXMsYW5vdGhlcixzdWNoLHN0cmluZw== (dGhpcyxpcyxhLGNzdixzdHJpbmcNCnRoaXMsaXMsYW5vdGhlcixzdWNoLHN0cmluZw==\r\n)
Line 2 with insufficient delimiters (,) encountered. Trying to see if it encoded. length 68 line: dGhpcyxpcyxhLGNzdixzdHJpbmcNCnRoaXMsaXMsYWdWNoLHN0cmluZw==
3: this,is,a,csv,string (this,is,a,csv,string\r\n)
5 item line 3 OK: this,is,a,csv,string
4: this,is,another,such,string (this,is,another,such,string\r\n)
5 item line 4 OK: this,is,another,such,string
5: this,is,a,csv,string (this,is,a,csv,string\r\n)
5 item line 5 OK: this,is,a,csv,string
6: this,is,another,such,string (this,is,another,such,string\r\n)
5 item line 6 OK: this,is,another,such,string

ответил(а) 2021-01-25T16:26:39+03:00 4 месяца, 4 недели назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема