【问题标题】:Python Overwriting files after parsing解析后Python覆盖文件
【发布时间】:2011-08-29 02:19:32
【问题描述】:

我是 Python 新手,我需要做一个解析练习。我有一个文件,我需要解析它(只是标题),但在这个过程之后,我需要保持文件相同的格式、相同的扩展名和磁盘中的相同位置,但只有不同之处新标题..

我试过这段代码...

for line in open ('/home/name/db/str/dir/numbers/str.phy'):
    if line.startswith('ENS'):
        linepars = re.sub ('ENS([A-Z]+)0+([0-9]{6})','\\1\\2',line)
        print linepars

..它完成了这项工作,但我不知道如何用新的解析“覆盖”文件。

【问题讨论】:

  • 不能输出到临时文件,然后把原来的删掉换成临时文件?
  • 您可能希望说出您希望此类文件的大小/行数。

标签: python parsing file overwrite


【解决方案1】:

最简单但不是最有效的方法(到目前为止,尤其是对于长文件)是重写整个文件。

您可以通过打开第二个文件句柄并重写每一行来做到这一点,除了在标题的情况下,您将编写已解析的标题。例如,

fr = open('/home/name/db/str/dir/numbers/str.phy')
fw = open('/home/name/db/str/dir/numbers/str.phy.parsed', 'w') # Name this whatever makes sense

for line in fr:
    if line.startswith('ENS'):
        linepars = re.sub ('ENS([A-Z]+)0+([0-9]{6})','\\1\\2',line)
        fw.write(linepars)
    else:
        fw.write(line)

fw.close()
fr.close()

编辑:请注意,这不使用readlines(),因此它的内存效率更高。它也不会存储每个输出行,而是一次只存储一个,立即将其写入文件。

作为一个很酷的技巧,您可以在输入文件上使用 with 语句来避免关闭它(Python 2.5+):

fw = open('/home/name/db/str/dir/numbers/str.phy.parsed', 'w') # Name this whatever makes sense

with open('/home/name/db/str/dir/numbers/str.phy') as fr:
    for line in fr:
        if line.startswith('ENS'):
            linepars = re.sub ('ENS([A-Z]+)0+([0-9]{6})','\\1\\2',line)
            fw.write(linepars)
        else:
             fw.write(line)

fw.close()

附:欢迎:-)

【讨论】:

  • 并不是说这个很酷的技巧有什么问题,但我通常更喜欢显式文件关闭。
  • with 不仅仅是一个很酷的技巧,它还是 Python 2.5 以后的一个新习惯用法。如果引发异常并且没有处理程序或 finally 子句,则您的显式文件关闭可能永远不会被调用。 with 负责所有这些。而且它不仅仅用于文件——这种称为上下文管理器的模式可用于线程锁定、数据库事务提交/回滚或任何其他您需要确保在代码完成后返回到基本状态的资源。是时候学习新技巧了。
  • 我想它在套接字中也会有很大的用处——感谢您的说明。
  • 感谢大家的建议...!!我会使用这个解决方案.. :D
  • @jedwards "最简单的方法是重写整个文件" 这不是最简单的方法,它是唯一的方法,因为不可能在硬盘上切割位,让孔替换要“删除”的字符。删除字符意味着必须重写文件,同时将要删除的字符分开。然后重点是执行重写的方式。您选择 "通过打开第二个文件句柄并重写每一行来执行此操作" ,但这不是 OP 想要的:他要求在同一位置重写文件 "磁盘”。看看我的回答会覆盖
【解决方案2】:

正如其他人所说,您想打开一个文件并使用该文件对象的.write() 方法。

最好的方法是打开一个额外的文件进行写入:

import os

current_cfg = open(...)
parsed_cfg  = open(..., 'w')
for line in current_cfg:
    new_line = parse(line)
    print new_line
    parsed.cfg.write(new_line + '\n')
current_cfg.close()
parsed_cfg.close()

os.rename(....) # Rename old file to backup name
os.rename(....) # Rename new file into place

另外,我建议查看tempfile 模块并使用其中一种方法来命名新文件或打开/创建它。就我个人而言,我倾向于将新文件放在与现有文件相同的目录中,以确保 os.rename 将自动工作(命名的配置文件将保证指向旧文件或新文件;在任何情况下都不会它指向一个部分写入/复制的文件)。

【讨论】:

    【解决方案3】:

    以下代码完成了这项工作。
    我的意思是它确实会覆盖自己的文件;这就是OP所要求的。这是可能的,因为转换只是删除字符,所以写入的文件指针 fo 总是在读取的文件指针 fi 后面。

    import re
    
    regx = re.compile('\AENS([A-Z]+)0+([0-9]{6})')
    
    with open('bomo.phy','rb+') as fi, open('bomo.phy','rb+') as fo:
        fo.writelines(regx.sub('\\1\\2',line) for line in fi)
    

    我认为写入不是由操作系统一次一行执行,而是通过缓冲区执行。因此,在写入转换后的行池之前会读取几行。我就是这么想的。

    【讨论】:

      【解决方案4】:
      newlines = []
      for line in open ('/home/name/db/str/dir/numbers/str.phy').readlines():
          if line.startswith('ENS'):
              linepars = re.sub ('ENS([A-Z]+)0+([0-9]{6})','\\1\\2',line)
              newlines.append( linepars )
      open ('/home/name/db/str/dir/numbers/str.phy', 'w').write('\n'.join(newlines))
      

      【讨论】:

      • 这应该可以,但是对于长文件,它需要您在内存中有两个文件副本,这可能太多了。
      • 我认为人们不会关心大小超过 1-4GB 的文本文件,除非您正在处理文本数据库转储(可能是这种情况),但是看起来很傻,我从来没有见过超过一百兆的。永远不要在需要之前进行优化。
      • 具体来说,对于长文件,readlines()join() 内存效率不高。你最好一次读一行,一次写一行。
      • “在需要之前不要优化。” ——那是(c)微软吗? :-)
      • @jedwards - 不,是 Donald Knuth,虽然你是在解释。
      【解决方案5】:

      (旁注:当然,如果您正在处理大文件,您应该知道所需的优化级别可能取决于您的情况。Python 本质上是非常不延迟评估的。以下解决方案不是一个好的如果您正在解析大型文件,例如数据库转储或日志,则可以选择,但一些调整(例如嵌套 with 子句和使用惰性生成器或逐行算法可以允许 O(1) 内存行为。)

      targetFile = '/home/name/db/str/dir/numbers/str.phy'
      
      def replaceIfHeader(line):
          if line.startswith('ENS'):
              return re.sub('ENS([A-Z]+)0+([0-9]{6})','\\1\\2',line)
          else:
              return line
      
      with open(targetFile, 'r') as f:
          newText = '\n'.join(replaceIfHeader(line) for line in f)
      
      try:
          # make backup of targetFile
          with open(targetFile, 'w') as f:
              f.write(newText)
      except:
          # error encountered, do something to inform user where backup of targetFile is
      

      编辑:感谢杰夫的建议

      【讨论】:

      • 对于长文件,readlines()join() 内存效率不高。
      • 这绝对是一个荒谬的反对意见,上一次看到大小超过 1GB 的文本文件是什么时候?也许如果您正在处理文本数据库转储,但这些非常罕见(而且通常会做错事)为什么要打扰。永远不要在需要之前优化,至少在你绝对不需要的情况下是这样。其他人会用 C 编写所有内容。除非程序运行速度慢得多或占用太多内存,否则代码的可读性、优雅和清晰是优先考虑的。此外,一次只会解析一个文件。我可以说您的解决方案使用了不必要的磁盘空间。
      • 实际上,我在工作中经常处理 500 MB+ 的文本文件。它们通常是从运行几天的脚本中输出的。问题不在于您不必要地使用了所有 内存,而在于它所花费的 时间。我投了反对票,因为您的解决方案不必要地低效,您出于恶意对我的答案投了反对票...
      • 不,我对您的答案投了反对票,原因与您对我的投反对票的原因相同:效率低下。您还重写了整个文件并使用(正如您声称我们关心的那样)额外假设的 500MB 不必要的磁盘写入时间来替换几行头文件;如果我们真的关心,如果 inode 结构允许就地收缩,我们会使用文件系统技巧就地修改它。 (你也没有使用with 声明,我觉得这很不优雅,但似乎你已经添加了它。如果发生一些错误以防止损坏数据,这还不如不抛出错误那么糟糕。)
      • 与其将整个文件读入一个字符串,然后再将其拆分然后重新加入它们,为什么不通过在读入文件时构建newText 来节省一些步骤呢?即newText = '\n'.join(replaceIfHeader(line) for line in f)
      猜你喜欢
      • 1970-01-01
      • 2016-02-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-20
      • 2014-11-30
      • 2016-06-11
      • 2016-04-05
      相关资源
      最近更新 更多