【问题标题】:How to modify a text file?如何修改文本文件?
【发布时间】:2010-09-12 15:31:33
【问题描述】:

我正在使用 Python,并且想在不删除或复制文件的情况下将字符串插入文本文件。我该怎么做?

【问题讨论】:

标签: python file text


【解决方案1】:

取决于你想做什么。要追加,您可以使用“a”打开它:

 with open("foo.txt", "a") as f:
     f.write("new line\n")

如果你想预先从文件中读取一些东西:

with open("foo.txt", "r+") as f:
     old = f.read() # read everything in the file
     f.seek(0) # rewind
     f.write("new line\n" + old) # write the new line before

【讨论】:

  • 只是一个小补充,要在 Python 2.5 中使用with 语句,您需要添加“from future import with_statement”。除此之外,使用with 语句打开文件肯定比手动关闭更具可读性且不易出错。
  • 在使用 inline=True 参数时,您可能会考虑使用 fileinput 帮助程序库很好地处理脏的打开/读取/修改/写入/替换例程。此处示例:stackoverflow.com/a/2363893/47390
  • 别忘了关闭文件。 f.Close()
  • 这不是我使用的样式,D.Rosado,但是使用with样式时,我认为您不需要手动关闭。 with 跟踪它创建的资源。
  • 不需要手动关闭文件。这就是在这里使用“with”的全部意义所在。 (好吧,实际上,Python 会在文件对象被垃圾回收后立即执行此操作,在 CPython 中,当绑定到它的名称超出范围时会发生这种情况......但其他实现不会,并且 CPython 可能有一天会停止这样做, 所以推荐使用"with")
【解决方案2】:

不幸的是,没有办法在不重写文件的情况下插入文件的中间。正如之前的海报所指出的那样,您可以使用 seek 附加到文件或覆盖文件的一部分,但如果您想在开头或中间添加内容,则必须重写它。

这是操作系统的事情,而不是 Python 的事情。在所有语言中都是一样的。

我通常做的是从文件中读取,进行修改并将其写入一个名为 myfile.txt.tmp 或类似文件的新文件。这比将整个文件读入内存要好,因为文件可能太大了。临时文件完成后,我将其重命名为与原始文件相同。

这是一种很好、安全的方法,因为如果文件写入因任何原因崩溃或中止,您仍然拥有未触及的原始文件。

【讨论】:

  • 像 awk/sed 这样的 unix 工具在它们的代码中做类似的事情吗?
  • 并非所有语言都一样。在 ActionScript 中:fileStream.openAsync(filename,FileMode.UPDATE);然后我可以去文件中我想要的任何地方并更改任何内容。
  • @AndrewBenjamin 你知道 ActionScript 正在制作什么系统调用吗? openAsync 是否有可能在调用后读取文件并写入新文件?
  • @Rawrgulmuffins 我没有。但是,我知道它不会将整个文件读入内存,因为我用它来处理几 GB 的文件大小。我怀疑这与使用 C# streamwriter 编写相同。我认为 python 是一种快速做小事的工具,而不是大规模开发和文件操作。
  • @AndrewBenjamin,用户并没有询问是否在文件中四处寻找并更改它(我知道的每种语言都可以做到这一点);他在询问插入文本,这与简单地更改/覆盖文件中已有的内容不同。也许在实际应用中它是不同的,但我在ActionScript API 中找不到任何东西表明它在这方面与任何其他语言的行为有任何不同。
【解决方案3】:

在原地重写文件通常是通过使用修改后的名称保存旧副本来完成的。 Unix 人添加一个~ 来标记旧的。 Windows 人员会做各种各样的事情——添加 .bak 或 .old——或完全重命名文件或将 ~ 放在名称的前面。

import shutil
shutil.move( afile, afile+"~" )

destination= open( aFile, "w" )
source= open( aFile+"~", "r" )
for line in source:
    destination.write( line )
    if <some condition>:
        destination.write( >some additional line> + "\n" )
source.close()
destination.close()

您可以使用以下地址代替shutil

import os
os.rename( aFile, aFile+"~" )

【讨论】:

  • 看起来不错。想知道 .readlines() 是否比迭代源代码更好?
  • @bozdoz:迭代更好,因为 readlines 读取整个文件。不适合大文件。当然,这假设您可以以这种本地化的方式进行修改。有时你不能,或者你的代码变得更加复杂。
  • @S.Lott: os.rename(aFile, aFile + "~") 将修改源文件的名称,而不是创建副本。
【解决方案4】:

Python 的 mmap 模块将允许您插入文件。以下示例显示了如何在 Unix 中完成它(Windows mmap 可能不同)。请注意,这并不能处理所有错误情况,您可能会损坏或丢失原始文件。此外,这不会处理 unicode 字符串。

import os
from mmap import mmap

def insert(filename, str, pos):
    if len(str) < 1:
        # nothing to insert
        return

    f = open(filename, 'r+')
    m = mmap(f.fileno(), os.path.getsize(filename))
    origSize = m.size()

    # or this could be an error
    if pos > origSize:
        pos = origSize
    elif pos < 0:
        pos = 0

    m.resize(origSize + len(str))
    m[pos+len(str):] = m[pos:origSize]
    m[pos:pos+len(str)] = str
    m.close()
    f.close()

也可以在没有 mmap 的情况下使用以 'r+' 模式打开的文件来执行此操作,但它不太方便且效率较低,因为您必须从插入位置读取文件内容并将其临时存储到 EOF - 这可能是巨大的。

【讨论】:

    【解决方案5】:

    如果使用 inplace=1 参数,Python 标准库的fileinput 模块将就地重写文件:

    import sys
    import fileinput
    
    # replace all occurrences of 'sit' with 'SIT' and insert a line after the 5th
    for i, line in enumerate(fileinput.input('lorem_ipsum.txt', inplace=1)):
        sys.stdout.write(line.replace('sit', 'SIT'))  # replace 'sit' and write
        if i == 4: sys.stdout.write('\n')  # write a blank line after the 5th line
    

    【讨论】:

    • 这在 python3 中如何工作?我刚刚将一个具有类似代码的应用程序从 python 移植到 python3,但我根本无法让它正常工作。 'line' 变量是字节类型,我尝试将其解码为 un​​icode,然后对其进行修改,然后将其编码回字节,但它无法正常工作。它引发了一些我不记得的异常。人们在 python3 中使用 fileinput inplace=1 是否成功?
    • @Robru:这里是Python 3 code
    • 但这没问题,因为你先在一个不重要的文件上测试它吧?
    【解决方案6】:

    正如 Adam 所提到的,您必须先考虑系统限制,然后才能决定是否有足够的内存将其全部读入内存替换部分并重新写入。

    如果您正在处理一个小文件或没有内存问题,这可能会有所帮助:

    选项 1) 将整个文件读入内存,对整个或部分行进行正则表达式替换,并将其替换为该行加上额外的行。您需要确保“中间行”在文件中是唯一的,或者如果每行都有时间戳,这应该是非常可靠的。

    # open file with r+b (allow write and binary mode)
    f = open("file.log", 'r+b')   
    # read entire content of file into memory
    f_content = f.read()
    # basically match middle line and replace it with itself and the extra line
    f_content = re.sub(r'(middle line)', r'\1\nnew line', f_content)
    # return pointer to top of file so we can re-write the content with replaced string
    f.seek(0)
    # clear file content 
    f.truncate()
    # re-write the content with the updated content
    f.write(f_content)
    # close file
    f.close()
    

    选项 2) 找出中间线,并将其替换为该线加上额外的线。

    # open file with r+b (allow write and binary mode)
    f = open("file.log" , 'r+b')   
    # get array of lines
    f_content = f.readlines()
    # get middle line
    middle_line = len(f_content)/2
    # overwrite middle line
    f_content[middle_line] += "\nnew line"
    # return pointer to top of file so we can re-write the content with replaced string
    f.seek(0)
    # clear file content 
    f.truncate()
    # re-write the content with the updated content
    f.write(''.join(f_content))
    # close file
    f.close()
    

    【讨论】:

      【解决方案7】:

      为干净利落地写了一个小班。

      import tempfile
      
      class FileModifierError(Exception):
          pass
      
      class FileModifier(object):
      
          def __init__(self, fname):
              self.__write_dict = {}
              self.__filename = fname
              self.__tempfile = tempfile.TemporaryFile()
              with open(fname, 'rb') as fp:
                  for line in fp:
                      self.__tempfile.write(line)
              self.__tempfile.seek(0)
      
          def write(self, s, line_number = 'END'):
              if line_number != 'END' and not isinstance(line_number, (int, float)):
                  raise FileModifierError("Line number %s is not a valid number" % line_number)
              try:
                  self.__write_dict[line_number].append(s)
              except KeyError:
                  self.__write_dict[line_number] = [s]
      
          def writeline(self, s, line_number = 'END'):
              self.write('%s\n' % s, line_number)
      
          def writelines(self, s, line_number = 'END'):
              for ln in s:
                  self.writeline(s, line_number)
      
          def __popline(self, index, fp):
              try:
                  ilines = self.__write_dict.pop(index)
                  for line in ilines:
                      fp.write(line)
              except KeyError:
                  pass
      
          def close(self):
              self.__exit__(None, None, None)
      
          def __enter__(self):
              return self
      
          def __exit__(self, type, value, traceback):
              with open(self.__filename,'w') as fp:
                  for index, line in enumerate(self.__tempfile.readlines()):
                      self.__popline(index, fp)
                      fp.write(line)
                  for index in sorted(self.__write_dict):
                      for line in self.__write_dict[index]:
                          fp.write(line)
              self.__tempfile.close()
      

      那么你可以这样使用它:

      with FileModifier(filename) as fp:
          fp.writeline("String 1", 0)
          fp.writeline("String 2", 20)
          fp.writeline("String 3")  # To write at the end of the file
      

      【讨论】:

      • 这对我个人来说不起作用,它确实会在文件中添加文本,但会先删除所有内容!
      • 确实,这根本行不通。很遗憾,因为这似乎是个好主意。
      【解决方案8】:

      如果你知道一些 unix,你可以尝试以下方法:

      注意:$表示命令提示符

      假设您有一个文件 my_data.txt,其内容如下:

      $ cat my_data.txt
      This is a data file
      with all of my data in it.
      

      然后使用os 模块,您可以使用通常的sed 命令

      import os
      
      # Identifiers used are:
      my_data_file = "my_data.txt"
      command = "sed -i 's/all/none/' my_data.txt"
      
      # Execute the command
      os.system(command)
      

      如果您不了解 sed,请查看它,它非常有用。

      【讨论】:

      • 这根本不是 Pythonic
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-16
      • 2016-11-10
      • 1970-01-01
      相关资源
      最近更新 更多