我是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
..它完成了这项工作,但我不知道如何使用新解析"覆盖"文件。
最简单的方法,但不是最有效的(到目前为止,特别是对于长文件)是重写整个文件。
您可以通过打开第二个文件句柄并重写每一行来执行此操作,除了标头的情况,您将写入解析的标头。 例如
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()
附言欢迎:-)
正如其他人在这里所说的那样,您想打开一个文件并使用该文件对象的.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
原子地工作(命名的配置文件将保证指向旧文件或新文件;在任何情况下都不会指向部分写入/复制的文件)。
以下代码完成这项工作。
我的意思是它确实会覆盖自己的文件;这就是OP所要求的。这是可能的,因为转换只是删除字符,因此写入的文件指针始终位于读取的文件指针 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)
我认为写入不是由操作系统一次一行执行,而是通过缓冲区执行。因此,在写入转换后的行池之前,会读取几行。我就是这么想的。
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))
(旁注:当然,如果您正在处理大文件,您应该意识到所需的优化级别可能取决于您的情况。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
编辑:感谢杰夫的建议