【问题标题】:Insert string at the beginning of each line在每行的开头插入字符串
【发布时间】:2011-11-29 19:33:01
【问题描述】:

如何在文本文件的每一行的开头插入一个字符串,我有以下代码:

f = open('./ampo.txt', 'r+')
with open('./ampo.txt') as infile:
    for line in infile:
        f.insert(0, 'EDF ')
f.close

我收到以下错误:

"'file' 对象没有属性 'insert'"

请注意,我是一个完整的编程 n00b。

【问题讨论】:

    标签: python string file


    【解决方案1】:

    您不能像这样就地修改文件。文件不支持插入。你必须把它全部读完,然后再把它全部写出来。

    如果您愿意,您可以逐行执行此操作。但在这种情况下,您需要写入一个临时文件,然后替换原始文件。因此,对于足够小的文件,像这样一次性完成会更简单:

    with open('./ampo.txt', 'r') as f:
        lines = f.readlines()
    lines = ['EDF '+line for line in lines]
    with open('./ampo.txt', 'w') as f:
        f.writelines(lines)
    

    【讨论】:

    • 我建议使用惰性理解,或者甚至更好地将字符串添加为读取或写入操作的一部分。这样文件内容将只在内存中一次,而不是两次。
    • @Jan 你能详细说明一下吗?我不是 100% 精通 Python,所以我有兴趣从比我了解更多的人那里学到更多。
    • [x for y in z] 表单创建了一个新的列表对象,并且在构造新的列表对象之前不能对旧的列表对象进行垃圾收集,因此在某一时刻,所有原始字符串和所有修改过的字符串都存在内存中的字符串。但也有(x for y in z) 表单,它创建了一个生成器。生成器是懒惰的;他们一次对一个输入值执行表达式并返回它,因此可以在需要下一个输入值之前收集它,并且它们永远不会同时在内存中。在这种情况下,你必须小心不要让它变得太懒,因为文件也支持懒读取。
    • 那有两个选项:['EDF ' + line for line in open('./ampo.txt', 'r')] 使用生成器懒惰地读取文件,但在重写之前急切地评估列表以使文件完全在内存中。 ('EDF ' + line for line in f.readlines()) 使用急读函数来获取内存中的文件,而不是生成器急切地构造修改后的文本。当然,如果你对两者都使用生成器,你会严重烧伤自己,因为你会在从文件中取出所有数据之前开始重写文件。
    • 当然还有第三种选择,在这两个步骤中都使用生成器,但是写入一个新文件,而不是在原始文件上重命名该文件。这样做的好处是可以处理大文件(磁盘上的空间通常比内存中的空间大),并且在进程中断时不会丢失信息。
    【解决方案2】:
    f = open('./ampo.txt', 'r')
    lines = map(lambda l : 'EDF ' + l, f.readlines())
    f.close()
    f = open('./ampo.txt', 'w')
    map(lambda l : f.write(l), lines)
    f.close()
    

    【讨论】:

    • -1:您错过了需要回写文件的部分!
    • Still -1: 使用map,你应该在一种情况下使用理解而在另一种情况下使用循环不是好的python风格。
    【解决方案3】:

    对于不太大的文件:

    with open('./ampo.txt', 'rb+') as f:
        x = f.read()
        f.seek(0,0)
        f.writelines(('EDF ', x.replace('\n','\nEDF ')))
        f.truncate()
    

    请注意,理论上,在这种情况下(内容被扩充),f.truncate() 可能并不是真正需要的。因为with语句应该是正确关闭文件的,也就是说在关闭前在末尾写一个EOF(end of file)。
    这就是我在示例中观察到的。
    但我很谨慎:我认为无论如何还是把这条指令写下来比较好。因为当内容减少时,with 语句不会写入一个 EOF 来正确关闭文件,而不是前面的初始 EOF,因此尾随的初始字符会保留在文件中。
    那么如果with语句在内容减少的时候不写EOF,那为什么要在内容增加的时候写呢?

    对于一个大文件,避免一次把文件的所有内容都放到内存中:

    import os
    
    def addsomething(filepath, ss):
        if filepath.rfind('.') > filepath.rfind(os.sep):
            a,_,c = filepath.rpartition('.')
            tempi = a + 'temp.' + c
        else:
            tempi = filepath + 'temp'
    
        with open(filepath, 'rb') as f, open(tempi,'wb') as g:
            g.writelines(ss + line for line in f)
    
        os.remove(filepath)
        os.rename(tempi,filepath)
    
    
    addsomething('./ampo.txt','WZE')
    

    【讨论】:

    • 请注意,这将就地覆盖您的文件。此外,在开始写作之前,您必须确保了解要写的所有内容;写入过程中的异常将产生损坏的文件。 (这个答案确实有这个问题,因为 python 被贪婪地评估。)
    • @ninjagecko:python 中有很多很多东西没有急切地评估。
    • @eyquem:f.truncate()的目的是什么?
    • @unutbu 在这里,f.truncate() 没用,因为生成的内容比文件中的前一个内容长。但无论如何我还是要让读者意识到在其他情况下是必要的,也就是说,当内容减少时。我很惊讶 with 语句在关闭文件之前没有写 EOF(文件结束)字符,曾经在我写过的程序中:在处理后的前 EOF 之前,仍然有初始内容的尾随字符。
    • @ninjagecko 好主意。但是我不明白最后一句话:什么是贪婪评估? (它不是 Python) 有什么后果?没看明白
    【解决方案4】:

    这是一个解决方案,您可以写入临时文件并将其移动到位。如果您要重写的文件非常大,您可能更喜欢这个版本,因为它避免了将文件的内容保留在内存中,因为涉及.read().readlines() 的版本会这样做。另外,如果读取或写入出现任何错误,您的原始文件将是安全的:

    from shutil import move
    from tempfile import NamedTemporaryFile
    
    filename = './ampo.txt'
    tmp = NamedTemporaryFile(delete=False)
    with open(filename) as finput:
        with open(tmp.name, 'w') as ftmp:
            for line in finput:
                ftmp.write('EDF '+line)
    move(tmp.name, filename)
    

    【讨论】:

      【解决方案5】:

      Python 自带batteries included:

      import fileinput
      import sys
      
      for line in fileinput.input(['./ampo.txt'], inplace=True):
          sys.stdout.write('EDF {l}'.format(l=line))
      

      与已经发布的解决方案不同,这也保留了文件权限。

      【讨论】:

      • “这也保留了文件权限” 我不知道。 +1
      • 这看起来好多了,谢谢。你能解释一下发生了什么吗?
      • for-loop 一次一行地遍历ampo.txt 中的行。 sys.stdout.writeprint 几乎相同。我选择使用sys.stdout.write 而不是print 的原因是print 添加了一个额外的换行符,而sys.stdout.write 没有。由于inplace=True 参数,fileinput 将原始 ampo.txt 移动到一个临时文件,然后将 stdout 重定向到一个名为 ampo.txt 的新文件。然后它复制权限并删除临时文件。还有更多选择,例如保留备份文件;有关详细信息,请参阅文档。
      • @unutbu ,这可以适应只写到指定的行吗?比如第3行。
      • @dantdj:是的,您可以使用if fileinput.filelineno() == 3: 根据您所在的线路更改行为。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-08-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-11
      • 2019-09-22
      相关资源
      最近更新 更多