4

我有一个文件,其中每一行都以数字开头。用户可以通过输入用户想要删除的行号来删除行。

我遇到的问题是设置打开它的模式。当我使用a+时,原始内容仍然存在。但是,添加到文件末尾的是我想要保留的行。另一方面,当我使用 时w+,整个文件都会被删除。我确信有比使用w+模式打开它、删除所有内容、然后重新打开它并附加行更好的方法。

 def DeleteToDo(self):
    print "Which Item Do You Want To Delete?"
    DeleteItem = raw_input(">") #select a line number to delete
    print "Are You Sure You Want To Delete Number" + DeleteItem + "(y/n)"
    VerifyDelete = str.lower(raw_input(">"))
    if VerifyDelete == "y":
        FILE = open(ToDo.filename,"a+") #open the file (tried w+ as well, entire file is deleted)
        FileLines = FILE.readlines() #read and display the lines
        for line in FileLines:
            FILE.truncate()
            if line[0:1] != DeleteItem: #if the number (first character) of the current line doesn't equal the number to be deleted, re-write that line
                FILE.write(line)
    else:
        print "Nothing Deleted"

这是典型文件的样子

1. info here
2. more stuff here
3. even more stuff here
4

5 回答 5

2

当您打开一个文件进行写入时,您会破坏该文件(删除其当前内容并开始一个新文件)。您可以通过阅读该open()命令的文档来了解这一点。

当您打开一个文件进行追加时,您不会破坏该文件。但是你怎么能只删除一行呢?文件是存储在存储设备上的字节序列;您无法删除一行并让所有其他行自动“滑下”到存储设备上的新位置。

(如果您的数据存储在数据库中,您实际上只能从数据库中删除一个“行”;但文件不是数据库。)

因此,解决此问题的传统方法是:从原始文件中读取,然后将其复制到新的输出文件中。复制时,您可以进行任何所需的编辑;例如,您可以简单地通过不复制一行来删除该行;或者您可以通过将其写入新文件来插入一行。

然后,一旦您成功写入新文件并成功关闭它,如果没有错误,您继续将新文件重命名为与旧文件相同的名称(这会破坏旧文件)。

在 Python 中,您的代码应该是这样的:

import os

# "num_to_delete" was specified by the user earlier.

# I'm assuming that the number to delete is set off from
# the rest of the line with a space.

s_to_delete = str(num_to_delete) + ' '
def want_input_line(line):
    return not line.startswith(s_to_delete)

in_fname = "original_input_filename.txt"
out_fname = "temporary_filename.txt"

with open(in_fname) as in_f, open(out_fname, "w") as out_f:
    for line in in_f:
        if want_input_line(line):
            out_f.write(line)

os.rename(out_fname, in_fname)

请注意,如果您碰巧有一个文件被调用temporary_filename.txt,它将被此代码破坏。实际上我们并不关心文件名是什么,我们可以让 Python 使用该tempfile模块为我们组成一些唯一的文件名。

任何最新版本的 Python 都允许您在单个with语句中使用多个语句,但如果您碰巧使用的是 Python 2.6 或其他版本,您可以嵌套两个with语句以获得相同的效果:

with open(in_fname) as in_f:
    with open(out_fname, "w") as out_f:
        for line in in_f:
            ... # do the rest of the code

另外,请注意,我没有使用.readlines()方法来获取输入行,因为.readlines()一次将文件的全部内容读入内存,如果文件非常大,这将很慢,甚至可能无法正常工作。您可以简单地for使用从 ; 返回的“文件对象”编写一个循环open();这将一次给你一行,你的程序甚至可以处理非常大的文件。

编辑:请注意,我的回答是假设您只想执行一个编辑步骤。正如@jdi 在另一个答案的评论中指出的那样,如果您想允许“交互式”编辑,用户可以在其中删除多行或插入行或其他任何内容,那么最简单的方法实际上是使用将所有行读入内存.readlines(),在结果列表中插入/删除/更新/任何内容,然后仅在完成编辑后一次将列表写入文件。

于 2012-06-28T19:13:34.780 回答
1

不要将所有行一一写到文件中,而是从内存中删除该行(您使用 readlines() 将文件读取到该行),然后一次将内存写回磁盘。这样您将获得您想要的结果,并且您不必阻塞 I/O。

于 2012-06-28T17:39:25.620 回答
1
def DeleteToDo():
    print ("Which Item Do You Want To Delete?")
    DeleteItem = raw_input(">") #select a line number to delete
    print ("Are You Sure You Want To Delete Number" + DeleteItem + "(y/n)")
    DeleteItem=int(DeleteItem) 
    VerifyDelete = str.lower(raw_input(">"))
    if VerifyDelete == "y":
        FILE = open('data.txt',"r") #open the file (tried w+ as well, entire file is deleted)
        lines=[x.strip() for x in FILE if int(x[:x.index('.')])!=DeleteItem] #read all the lines first except the line which matches the line number to be deleted
        FILE.close()
        FILE = open('data.txt',"w")#open the file again
        for x in lines:FILE.write(x+'\n')    #write the data to the file

    else:
        print ("Nothing Deleted")
DeleteToDo()
于 2012-06-28T17:49:46.597 回答
0

您不需要检查文件中的行号,您可以执行以下操作:

def DeleteToDo(self):
    print "Which Item Do You Want To Delete?"
    DeleteItem = int(raw_input(">")) - 1
    print "Are You Sure You Want To Delete Number" + str(DeleteItem) + "(y/n)"
    VerifyDelete = str.lower(raw_input(">"))
    if VerifyDelete == "y":
        with open(ToDo.filename,"r") as f:
            lines = ''.join([a for i,a in enumerate(f) if i != DeleteItem])

        with open(ToDo.filename, "w") as f:
            f.write(lines)
    else:
        print "Nothing Deleted"
于 2012-06-28T19:32:07.963 回答
0

您可以mmap将文件...在阅读了合适的文档之后...

于 2012-06-28T21:08:50.950 回答