4

我是 Python 新手,一般是编程。

我想从文本文件的每一行中删除第一个字符并将更改写回文件。例如,我有 36 行的文件,每行中的第一个字符包含一个符号或一个数字,我希望将其删除。

我在这里做了一个小代码,但它没有按预期工作,它只复制了整个留置权。任何帮助将不胜感激!

from sys import argv

run, filename = argv

f = open(filename, 'a+')
f.seek(0)
lines = f.readlines()
for line in lines:
    f.write(line[1:])
f.close()
4

6 回答 6

6

您的代码已经删除了第一个字符。我将您的代码完全保存为dupy.pyand dupy.txt,然后运行python dupy.py dupy.txt​​,结果是:

from sys import argv

run, filename = argv

f = open(filename, 'a+')
f.seek(0)
lines = f.readlines()
for line in lines:
    f.write(line[1:])
f.close()
rom sys import argv
un, filename = argv
 = open(filename, 'a+')
.seek(0)
ines = f.readlines()
or line in lines:
   f.write(line[1:])
.close()

它不是复制整行;它正在复制行,而它们的第一个字符被剥离。


但是从问题的最初陈述来看,听起来您想覆盖这些行,而不是附加新副本。为此,请不要使用append模式。读取文件,然后写入:

from sys import argv

run, filename = argv

f = open(filename)
lines = f.readlines()
f.close()
f = open(filename, 'w')
for line in lines:
    f.write(line[1:])
f.close()

或者,或者,编写一个新文件,然后在完成后将其移动到原始文件之上:

import os
from sys import argv

run, filename = argv

fin = open(filename)
fout = open(filename + '.tmp', 'w')
lines = f.readlines()
for line in lines:
    fout.write(line[1:])
fout.close()
fin.close()
os.rename(filename + '.tmp', filename)

(请注意,此版本在 Windows 上无法按原样运行,但它比实际的跨平台版本更简单;如果您需要 Windows,我可以解释如何执行此操作。)


with您可以通过使用语句、直接循环文件而不是调用来使代码更简单、更健壮和更高效readlines,并使用tempfile

import tempfile
from sys import argv

run, filename = argv

with open(filename) as fin, tempfile.NamedTemporaryFile(delete=False) as fout:
    for line in fin:
        fout.write(line[1:])
    os.rename(fout.name, filename)

在大多数平台上,这保证了“原子写入”——当你的脚本完成时,或者即使有人在它运行的过程中拔掉了插件,文件最终要么被新版本替换,要么原封不动;它不可能在中途被覆盖成不可恢复的垃圾。

同样,此版本无法在 Windows 上运行。如果没有大量工作,就无法在 Windows 上实现这种“写临时重命名”算法。但是您只需做一些额外的工作就可以接近:

with open(filename) as fin, tempfile.NamedTemporaryFile(delete=False) as fout:
    for line in fin:
        fout.write(line[1:])
    outname = fout.name
os.remove(filename)
os.rename(outname, filename)

这确实可以防止您半覆盖文件,但它会留下一个漏洞,您可能已经删除了原始文件,并将新文件留在了您必须搜索的临时位置。您可以通过将文件放在更容易找到的地方来使它更好一点(请参阅NamedTemporaryFile文档以了解如何)。或者将原始文件重命名为临时名称,然后写入原始文件名,然后删除原始文件。或其他各种可能性。但是要真正获得与其他平台相同的行为是非常困难的。

于 2013-09-20T19:55:30.410 回答
3

您可以读取内存中的所有行然后重新创建文件,

from sys import argv

run, filename = argv

with open(filename, 'r') as f:
    data = [i[1:] for i in f
with open(filename, 'w') as f:
    f.writelines(i+'\n' for i in data) # this is for linux. for win use \r\n

或者您可以创建其他文件并将数据从第一个文件逐行移动到第二个文件。然后你可以重命名它如果你愿意

from sys import argv

run, filename = argv

new_name = filename + '.tmp'
with open(filename, 'r') as f_in, open(new_name, 'w') as f_out:
    for line in f_in:
        f_out.write(line[1:])

os.rename(new_name, filename)
于 2013-09-20T19:56:17.540 回答
3

最基本的问题是,在将文件的完整内容读入数组seek,您需要回到文件的开头。由于您正在使文件更短,因此您还需要在完成后使用 来调整文件的官方长度。此外,打开模式(a 用于append)覆盖并强制所有写入到文件末尾。所以你的代码应该是这样的:ftruncatea+seek

import sys

def main(argv):
    filename = argv[1]
    with open(filename, 'r+') as f:
        lines = f.readlines()
        f.seek(0)
        for line in lines:
            f.write(line[1:])
        f.truncate()

if __name__ == '__main__': main(sys.argv)

做这样的事情时,最好将更改写入文件,然后在完成后将其重命名为旧文件。这会导致更新“原子地”发生 - 并发阅读器看到旧文件或新文件,而不是两者的某种错位组合。看起来像这样:

import os
import sys
import tempfile

def main(argv):
    filename = argv[1]
    with open(filename, 'r') as inf:
        with tempfile.NamedTemporaryFile(dir=".", delete=False) as outf:
            tname = outf.name
            for line in inf:
                outf.write(line[1:])
    os.rename(tname, filename)

if __name__ == '__main__': main(sys.argv)

(注意:通过原子替换文件rename在 Windows 上不起作用;您必须先os.remove使用旧名称。不幸的是,这确实意味着有一个简短的窗口(不是双关语),并发阅读器会发现该文件不存在。如据我所知,没有办法避免这种情况。)

于 2013-09-20T20:00:22.103 回答
2

老实说,我真的不确定嵌套的想法有多好/坏with open(),但你可以做这样的事情。

with open(filename_you_reading_lines_FROM, 'r') as f0:
    with open(filename_you_appending_modified_lines_TO, 'a') as f1:
        for line in f0:
            f1.write(line[1:])
于 2013-09-20T21:59:44.653 回答
2
import re

with open(filename,'r+') as f:
    modified = re.sub('^.','',f.read(),flags=re.MULTILINE)
    f.seek(0,0)
    f.write(modified)

在正则表达式模式中:
^表示“字符串开头”
^,标志re.MULTILINE表示“行首”

^.表示“行首的唯一一个字符”

行的开头是字符串的开头或换行符之后的任何位置(换行符是\n
因此,我们可能担心序列中的某些换行符\n\n\n\n\n\n\n可能与正则表达式模式匹配。
但是点表示除换行符之外的任何字符,那么所有换行符都与此正则表达式模式不匹配。

在由 触发的文件读取过程中f.read(),文件的指针一直移动到文件末尾。

f.seek(0,0)将文件的指针移回文件的开头

f.truncate()在写入停止的位置放置一个新的 EOF = end of file。这是必要的,因为修改后的文本比原始文本短。
比较它与没有此行的代码的作用

于 2013-09-20T21:30:58.847 回答
0

虽然似乎有一些关于最佳实践以及它是否可以在 Windows 上运行的讨论,但作为 Python 新手,我能够运行第一个有效的示例,并让它在我的 Win 环境中运行,该环境中有 cygwin 二进制文件环境变量路径并删除前 3 个字符(它们是示例文件中的行号):

import os
from sys import argv

run, filename = argv

fin = open(filename)
fout = open(filename + '.tmp', 'w')
lines = fin.readlines()
for line in lines:
    fout.write(line[3:])
fout.close()
fin.close()

我选择不自动覆盖,因为我希望能够看到输出。

python c:\bin\remove1st3.py sampleCode.txt
于 2015-09-18T18:01:38.480 回答