【问题标题】:How to delete a specific line in a file?如何删除文件中的特定行?
【发布时间】:2011-06-10 06:02:30
【问题描述】:

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

【问题讨论】:

  • 尝试fileinput,如@j-f-sebastian here 所述。它似乎允许您通过一个临时文件逐行工作,所有这些都使用简单的for 语法。

标签: python file input


【解决方案1】:

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

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 也不会。

【讨论】:

  • 为什么要打开和关闭两次?
  • @Ooker:您必须打开文件两次(并在其间关闭),因为在第一种模式下它是“只读”的,因为您只是在读取文件中的当前行。然后关闭它并以“写入模式”重新打开它,在这种模式下文件是可写的,并且您替换文件的内容而不是要删除的行。
  • 为什么Python不允许我们在一行中做到这一点?
  • @Ooker,当你阅读一行时,试着想象一个光标在阅读时沿着这条线移动。读取该行后,光标现在已越过它。当您尝试写入文件时,您会写入光标当前所在的位置。通过重新打开文件,您可以重置光标。
  • 这个任务只能打开一次文件...但它需要打开'r+',并且,你需要调用 flie.seek(0) (移动光标到开头)和 file.truncate() (使现有内容无效),然后再继续重写。
【解决方案2】:

解决这个问题只需要一次打开:

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 指针,然后在最后一次写入后截断以删除所有内容。

【讨论】:

  • 这对我来说效果很好,因为我还必须使用 lockfile (fcntl)。我找不到任何方法将 fileinput 与 fcntl 一起使用。
  • 很高兴看到这个解决方案的一些副作用。
  • 我不会这样做。如果您在for 循环中遇到错误,您将得到一个部分覆盖的文件,其中包含重复的行或半截断行。您可能想在f.seek(0) 之后直接使用f.truncate()。这样,如果您遇到错误,您最终会得到一个不完整的文件。但真正的解决方案(如果你有磁盘空间)是输出到一个临时文件,然后在一切成功后使用os.replace()pathlib.Path(temp_filename).replace(original_filename) 将其与原始文件交换。
  • 您是否可以像接受的答案中提到的那样添加i.strip('\n') != "line you want to remove...",这将完美地解决我的问题。因为只是i 并没有为我做任何事情
【解决方案3】:

我认为最好和最快的选择是在其他地方重新写入文件,而不是将所有内容存储在列表中并重新打开文件进行写入。

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

就是这样!在一个循环中,只有一个循环,你可以做同样的事情。它会快得多。

【讨论】:

  • 我们可以使用Generator Expression,而不是使用普通的for循环,这样程序不会将所有行从文件加载到内存,这对于大文件来说不是一个好主意。它一次在内存中只有一行。使用生成器表达式 for 循环看起来像,(output.write(line) for line in input if line!="nickname_to_delete"+"\n")
  • @ShriShinde 在循环文件对象时,您也没有将文件读入内存,因此此解决方案与您的建议相同。
  • 您可能想要删除原始文件并将第二个文件重命名为原始文件的名称,在 Linux 操作系统上使用 Python 看起来像这样,subprocess.call(['mv', 'newfile.txt', 'yourfile.txt'])
  • os.replace(python v 3.3 中的新功能)比对mv 的系统调用更具跨平台性。
【解决方案4】:

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


对于这样的文件:

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

来自 Lother 解决方案的这个分支工作正常:

#!/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 是的。否则,您不会覆盖文件,而是将文件附加到自身(不包括您排除的行)。
【解决方案5】:

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

【讨论】:

    【解决方案6】:

    我喜欢这个答案中解释的 fileinput 方法: Deleting a line from a text file (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

    【讨论】:

      【解决方案7】:

      您可能已经得到了正确答案,但这是我的答案。 我没有使用列表来收集未过滤的数据(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()
      

      希望你会发现这很有用! :)

      【讨论】:

        【解决方案8】:

        如果您使用 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
        

        【讨论】:

        • 此解决方案与操作系统无关,并且由于 OP 未指定操作系统,因此没有理由发布特定于 Linux 的答案 imo。
        • 任何建议使用 subprocess 来完成任何可以只用 python 完成的事情的人都会被否决!并向@SteinarLima +1 ... 我同意
        • -i 选项是非标准的,在 *BSD 平台(包括 macOS)上的工作方式与在 Linux 上的工作方式不同。 Python 的 fileinput 模块以透明、可移植和本机方式执行相同的操作。
        【解决方案9】:

        我认为,如果您将文件读入列表,那么您可以遍历列表以查找要删除的昵称。您可以在不创建额外文件的情况下更高效地完成此操作,但您必须将结果写回源文件。

        我可以这样做:

        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()
        

        【讨论】:

          【解决方案10】:

          一般来说,你不能;您必须重新编写整个文件(至少从更改点到结束)。

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

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

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

          这对于短文档(任何小于 100 KB 的文件?)来说可能是多余的。

          【讨论】:

            【解决方案11】:

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

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

            它比其他答案少了一点罗嗦,并且足够快

            【讨论】:

              【解决方案12】:

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

              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)
              

              【讨论】:

              • 在给出答案时,最好给出some explanation as to WHY your answer
              • 如果您的文件不以换行符结尾,则此代码不会删除最后一行,即使它包含您要删除的单词。
              【解决方案13】:

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

              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()
              

              【讨论】:

                【解决方案14】:

                您可以使用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)
                

                【讨论】:

                  【解决方案15】:

                  你想从文件中删除特定的行,所以使用这个 sn-p 简短的代码,你可以轻松地删除任何带有句子或前缀(符号)的行。

                  with open("file_name.txt", "r") as f:
                  lines = f.readlines() 
                  with open("new_file.txt", "w") as new_f:
                      for line in lines:
                          if not line.startswith("write any sentence or symbol to remove line"):
                              new_f.write(line)
                  

                  【讨论】:

                  • 相对于现有旧答案的唯一独特功能似乎是缩进错误。
                  【解决方案16】:

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

                  将变量 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())
                  【解决方案17】:

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

                  【讨论】:

                  • (1) 你的意思是tuple(f.read().split('\n'))?? (2)“访问你的元组的行号”和“加入你的结果元组”听起来很神秘;实际的 Python 代码可能更容易理解。
                  猜你喜欢
                  • 1970-01-01
                  • 2018-06-21
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2020-08-08
                  • 1970-01-01
                  相关资源
                  最近更新 更多