使用Python删除文件中的特定行


145

假设我有一个充满昵称的文本文件。如何使用Python从此文件中删除特定的昵称?


1
尝试fileinput按@ jf-sebastian的描述在此处。似乎可以通过一个临时文件逐行工作,所有这些操作都使用简单的for语法。
凯文

Answers:


205

首先,打开文件并从文件中获取所有行。然后以写模式重新打开文件并写回您的行,但要删除的行除外:

with open("yourfile.txt", "r") as f:
    lines = f.readlines()
with open("yourfile.txt", "w") as f:
    for line in lines:
        if line.strip("\n") != "nickname_to_delete":
            f.write(line)

您需要strip("\n")在比较中使用换行符,因为如果文件不以换行符结尾,则最后一个line也不行。


2
为什么我们必须打开和关闭它两次?
Ooker 2014年

3
@Ooker:您必须打开文件两次(并在两次之间关闭),因为在第一种模式下它是“只读的”,因为您只是在读取文件的当前行。然后,将其关闭,然后以“写入模式”将其重新打开,在该模式下文件是可写的,并且替换了文件内容,但没有要删除的行。
Devin 2014年

4
为什么Python不允许我们一行执行此操作?
Ooker 2014年

5
@Ooker,当您读取一行时,请尝试想象一个光标在读取时沿着该行移动。读取该行后,光标现在就可以通过它。当您尝试将文件写入当前光标所在的位置时。通过重新打开文件,可以重置光标。
Waddas 2014年

4
与配合使用!
Sceluswe

100

仅需一次打开即可解决此问题:

with open("target.txt", "r+") as f:
    d = f.readlines()
    f.seek(0)
    for i in d:
        if i != "line you want to remove...":
            f.write(i)
    f.truncate()

此解决方案以r / w模式(“ r +”)打开文件,并使用一次seek重置f指针,然后在上次写入后截断以删除所有内容。


2
这对我来说非常有效,因为我也必须使用lockfile(fcntl)。我找不到任何与fcntl一起使用fileinput的方法。
Easyrider 2015年

1
很高兴看到此解决方案的一些副作用。
user1767754 '17

3
我不会的 如果您在for循环中遇到错误,最终将得到部分覆盖的文件,其中重复的行或行的一半被截断。你可能想f.truncate()后立即f.seek(0)代替。这样,如果遇到错误,您将最终得到不完整的文件。但是真正的解决方案(如果有磁盘空间)是输出到一个临时文件,然后在一切成功之后使用os.replace()pathlib.Path(temp_filename).replace(original_filename)将其与原始文件交换。
鲍里斯(Boris)

您可以i.strip('\n') != "line you want to remove..."按照接受的答案中的说明添加,这样可以完全解决我的问题。因为i对我什么都没做
Mangohero1

31

在我看来,最好和最快的选择不是将所有内容存储在列表中并重新打开文件以将其写入,而是将文件重新写入其他位置。

with open("yourfile.txt", "r") as input:
    with open("newfile.txt", "w") as output: 
        for line in input:
            if line.strip("\n") != "nickname_to_delete":
                output.write(line)

而已!在一个循环中,只有一个循环您可以执行相同的操作。它将更快。


除了使用普通的for循环外,我们还可以使用Generator Expression。这样一来,程序将不会加载文件到内存的所有行,这对于大文件来说不是一个好主意。一次只能有一行。与生成器表达式的循环将看起来像,(output.write(line) for line in input if line!="nickname_to_delete"+"\n")
shrishinde

4
@ShriShinde循环文件对象时也不会将文件读入内存,因此此解决方案的工作原理与您的建议相同。
Steinar Lima

您可能想要删除原始文件,然后将第二个文件重命名为原始文件的名称,在Linux OS上使用Python时,其名称应如下所示:subprocess.call(['mv', 'newfile.txt', 'yourfile.txt'])
最高

6
os.replace(python v 3.3中的新增功能)比跨系统调用更具跨平台性mv
7yl4r

简单而伟大。
JuBaer AD

27

这是@Lother答案的“叉子” (我认为应该认为是正确的答案)。


对于这样的文件:

$ cat file.txt 
1: october rust
2: november rain
3: december snow

Lother解决方案中的这个fork可以正常工作:

#!/usr/bin/python3.4

with open("file.txt","r+") as f:
    new_f = f.readlines()
    f.seek(0)
    for line in new_f:
        if "snow" not in line:
            f.write(line)
    f.truncate()

改进之处:

  • with open,放弃使用 f.close()
  • 更清晰地if/else评估当前行中是否不存在字符串

是否需要f.seek(0)?
yifan '18

@yifan是的。否则,您无需覆盖文件,而是将文件附加到自身(没有要排除的行)。
鲍里斯(Boris)

5

在第一遍中读取行并在第二遍中进行更改(删除特定行)的问题是,如果文件大小很大,则会用完RAM。相反,一种更好的方法是逐行读取行,并将其写入单独的文件中,从而消除不需要的行。我使用的文件大小高达12-50 GB,并且RAM使用率几乎保持不变。只有CPU周期显示正在进行处理。


2

我喜欢此答案中所述的fileinput方法: 从文本文件(python)删除一行

举例来说,我有一个包含空行的文件,并且想要删除空行,这是我如何解决的方法:

import fileinput
import sys
for line_number, line in enumerate(fileinput.input('file1.txt', inplace=1)):
    if len(line) > 1:
            sys.stdout.write(line)

注意:我的空行长度为1


2

如果使用Linux,则可以尝试以下方法。
假设您有一个名为的文本文件animal.txt

$ cat animal.txt  
dog
pig
cat 
monkey         
elephant  

删除第一行:

>>> import subprocess
>>> subprocess.call(['sed','-i','/.*dog.*/d','animal.txt']) 

然后

$ cat animal.txt
pig
cat
monkey
elephant

7
该解决方案与操作系统无关,并且由于OP没有指定操作系统,因此没有理由发布特定于Linux的答案imo。
Steinar Lima

2
任何建议将子进程用于仅使用python即可完成的工作的人都会被淘汰!+1到@SteinarLima ...我同意
Jamie Lindsey

2

我认为,如果您将文件读入列表,则可以在列表上进行遍历以查找要删除的昵称。您可以高效地执行此操作,而无需创建其他文件,但是必须将结果写回到源文件中。

这是我可能的方法:

import, os, csv # and other imports you need
nicknames_to_delete = ['Nick', 'Stephen', 'Mark']

我假设nicknames.csv包含如下数据:

Nick
Maria
James
Chris
Mario
Stephen
Isabella
Ahmed
Julia
Mark
...

然后将文件加载到列表中:

 nicknames = None
 with open("nicknames.csv") as sourceFile:
     nicknames = sourceFile.read().splitlines()

接下来,迭代到列表以匹配要删除的输入:

for nick in nicknames_to_delete:
     try:
         if nick in nicknames:
             nicknames.pop(nicknames.index(nick))
         else:
             print(nick + " is not found in the file")
     except ValueError:
         pass

最后,将结果写回文件:

with open("nicknames.csv", "a") as nicknamesFile:
    nicknamesFile.seek(0)
    nicknamesFile.truncate()
    nicknamesWriter = csv.writer(nicknamesFile)
    for name in nicknames:
        nicknamesWriter.writeRow([str(name)])
nicknamesFile.close()

1

一般来说,您不能;您必须再次写入整个文件(至少从更改到结束为止)。

在某些特定情况下,您可以做得更好-

如果所有数据元素的长度相同且没有特定顺序,并且您知道要删除的元素的偏移量,则可以将最后一项复制到要删除的项上,并在最后一项之前截断文件;

或者,您也可以在已保存的数据元素中用“这是不良数据,跳过它”的值覆盖数据块,或者在已保存的数据元素中保留“此项目已被删除”标志,这样就可以将其标记为已删除,而无需另外修改文件。

对于简短的文档(小于100 KB的内容?)来说,这可能是多余的。


1

可能您已经得到了正确的答案,但这是我的。readlines()我使用了两个文件,而不是使用列表来收集未过滤的数据(方法做了什么)。一个用于保存主数据,第二个用于删除特定字符串时过滤数据。这是一个代码:

main_file = open('data_base.txt').read()    # your main dataBase file
filter_file = open('filter_base.txt', 'w')
filter_file.write(main_file)
filter_file.close()
main_file = open('data_base.txt', 'w')
for line in open('filter_base'):
    if 'your data to delete' not in line:    # remove a specific string
        main_file.write(line)                # put all strings back to your db except deleted
    else: pass
main_file.close()

希望您会发现这个有用!:)


0

将文件行保存在列表中,然后从列表中删除要删除的行,并将其余行写入新文件

with open("file_name.txt", "r") as f:
    lines = f.readlines() 
    lines.remove("Line you want to delete\n")
    with open("new_file.txt", "w") as new_f:
        for line in lines:        
            new_f.write(line)

给出答案时,最好对原因做出一些解释
斯蒂芬·劳奇

如果您的文件不以换行符结尾,则即使包含要删除的单词,此代码也不会删除最后一行。
鲍里斯(Boris)

0

这是从文件中删除某行的一些其他方法:

src_file = zzzz.txt
f = open(src_file, "r")
contents = f.readlines()
f.close()

contents.pop(idx) # remove the line item from list, by line number, starts from 0

f = open(src_file, "w")
contents = "".join(contents)
f.write(contents)
f.close()

0

我喜欢使用fileinput和'inplace'方法的此方法:

import fileinput
for line in fileinput.input(fname, inplace =1):
    line = line.strip()
    if not 'UnwantedWord' in line:
        print(line)

它比其他答案少罗word,并且足够快


0

您可以使用re图书馆

假设您能够加载完整的txt文件。然后,您定义不需要的昵称列表,然后将其替换为空字符串“”。

# Delete unwanted characters
import re

# Read, then decode for py2 compat.
path_to_file = 'data/nicknames.txt'
text = open(path_to_file, 'rb').read().decode(encoding='utf-8')

# Define unwanted nicknames and substitute them
unwanted_nickname_list = ['SourDough']
text = re.sub("|".join(unwanted_nickname_list), "", text)

-1

通过文件的行号删除文件的特​​定行

将变量filenameline_to_delete替换为文件名和要删除的行号。

filename = 'foo.txt'
line_to_delete = 3
initial_line = 1
file_lines = {}

with open(filename) as f:
    content = f.readlines() 

for line in content:
    file_lines[initial_line] = line.strip()
    initial_line += 1

f = open(filename, "w")
for line_number, line_content in file_lines.items():
    if line_number != line_to_delete:
        f.write('{}\n'.format(line_content))

f.close()
print('Deleted line: {}'.format(line_to_delete))

输出示例:

Deleted line: 3

无需构建字典,只需使用for nb, line in enumerate(f.readlines())
Dionys

-3

取文件内容,用换行符将其拆分为元组。然后,访问您的元组的行号,加入结果元组,然后覆盖该文件。


6
(1)你的意思是tuple(f.read().split('\n'))?(2)“访问元组的行号”和“加入结果元组”听起来很神秘;实际的Python代码可能更容易理解。
John Machin
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.