正如 Alexis 所解释的,文本文件不是随机访问或修改的。要将新数据插入文本文件的中间,您必须编写一个全新的文件。
但这真的有问题吗?你只这样做了5次。而且,由于现代计算机非常擅长将大量顺序数据发送到硬盘驱动器,而不擅长随机查找和写入,因此浪费的时间可能不会那么多。这很简单。例如:
bakpath = path+'.bak'
os.rename(path, bakpath)
with open(path, 'rb') as infile, open(bakpath, 'wb') as outfile:
writer = csv.writer(outfile)
for row, newvalue in zip(csv.reader(infile), newvalues):
row.append(newvalue)
writer.writerow(row)
如果是这样,有几种方法可以改进。
最明显的是,您可以使用数据库(如sqlite3
)或表系统(如pandas
或pytables
)代替 CSV 文件。除了已经编写好且易于使用之外,它们还可能比您想出的任何东西都得到更好的优化。
或者只是为每一列使用一个单独的文件。您仍然可以访问它们,就像它们是一个文件一样,如下所示:
with closing_all([open(path, 'rb') for path in paths]) as files):
for row in zip(*files):
# each row is a tuple of columns
这closing_all
不是标准库中内置的,但您可以简单地编写它:
@contextmanager
def closing_all(things):
try:
yield things
finally:
for thing in things:
thing.close()
如果您需要在最后将它们全部合并到一个文件中,这很容易做到,这意味着您将整个内容重写 1 次而不是 N 次。
您也可以自己构建一个随机访问文件。如果您事先知道最大列长度和列数,您可以用空格填充每一列:
COLUMN_LENGTHS = 20, 15, 41, 12, 19
COLUMN_STARTS = [0] + list(itertools.accumulate(COLUMN_LENGTHS))
ROW_LENGTH = COLUMN_STARTS[-1] + 1
def read_cell(f, row, column):
f.seek(row * ROW_LENGTH + COLUMN_STARTS[column])
return f.read(COLUMN_LENGTHS[column]).rstrip()
def write_cell(f, row, column, value):
f.seek(row * ROW_LENGTH + COLUMN_STARTS[column])
padded = value.ljust(COLUMN_LENGTHS[column])
f.write(padded)
如果你事先不知道它们,但可以粗略估计,你总是可以使用list
和类似类使用的相同技巧:高估,每当你被写出来时,乘以某个常数并将旧的东西复制到一个新扩展的版本。这意味着您只重写文件日志 N 次而不是 N 次。
另一种选择是将文件保持为转置格式,因此您只需添加新行而不是新列。您只需以'a'
模式打开文件并写入即可。
如有必要,您始终可以在最后将其转回,这意味着您正在重写文件一次而不是 N 次。