【问题标题】:Efficient way to delete a line from a text file从文本文件中删除一行的有效方法
【发布时间】:2010-10-06 15:25:15
【问题描述】:

我需要从文本文件中删除某行。这样做最有效的方法是什么?文件可能很大(超过百万条记录)。

更新: 下面是我目前正在使用的代码,但我不确定它是否好用。

internal void DeleteMarkedEntries() {
    string tempPath=Path.GetTempFileName();
    using (var reader = new StreamReader(logPath)) {
        using (var writer = new StreamWriter(File.OpenWrite(tempPath))) {
            int counter = 0;
            while (!reader.EndOfStream) {
                if (!_deletedLines.Contains(counter)) {
                    writer.WriteLine(reader.ReadLine());
                }
                ++counter;
            }
        }
    }
    if (File.Exists(tempPath)) {
        File.Delete(logPath);
        File.Move(tempPath, logPath);
    }
}

【问题讨论】:

  • 如果你有这么大的数据存储,为什么不使用“真正的”数据库?是否对您可用的工具、您当前的技能或项目规范有限制?
  • 这是“上面”的要求。使用真正的数据库对我来说会更容易,但不幸的是,我不能使用它。
  • 这不好,有一个错误 - 抱歉 :( - 请参阅下面的答案

标签: c# performance file-io


【解决方案1】:

执行此操作的最直接的方法可能是最好的,将整个文件写入一个新文件,写入除您不想要的行之外的所有行。

或者,打开文件进行随机访问。

阅读到要“删除”该行的位置。 跳过要删除的行,并读取该字节数(包括 CR + LF - 如有必要),在已删除的行上写入该字节数,将两个位置推进该字节数并重复直到文件结尾。

希望这会有所帮助。

编辑 - 现在我可以看到你的代码了

if (!_deletedLines.Contains(counter)) 
{                            
    writer.WriteLine(reader.ReadLine());                        
}

行不通,如果它是你不想要的那行,你还想读它别写了。上面的代码既不会读也不会写。新文件将与旧文件完全相同。

你想要类似的东西

string line = reader.ReadLine();
if (!_deletedLines.Contains(counter)) 
{                            
    writer.WriteLine(line);                        
}

【讨论】:

    【解决方案2】:

    文本文件是连续的,因此在删除一行时,您必须将以下所有行向上移动。 您可以使用文件映射(您可以通过 PInvoke 调用的 win32 api)来减少此操作的痛苦,但您当然应该考虑为您的文件使用非顺序结构,以便您可以将一行标记为已删除而无需真正删除它从文件中...特别是如果它应该经常发生。

    如果我记得应该将文件映射 Api 添加到 .Net 4。

    【讨论】:

      【解决方案3】:
           try{
           Scanner reader = new Scanner(new File("D:/seenu.txt")); 
           System.out.println("Enter serial number:");
           String sl1=bufRead.readLine();
           System.out.print("Please Enter The ServerName:");
           String name=bufRead.readLine();
           System.out.println("Please Enter The IPAddress");
           String ipa=bufRead.readLine();
      
          System.out.println("Line Deleted.");
           PrintWriter writer = new PrintWriter(new FileWriter(new File("D:/user.txt")),true); 
           //for(int w=0; w<n; w++)
             writer.write(reader.nextLine()); 
           reader.nextLine(); 
           while(reader.hasNextLine())
             writer.write(reader.nextLine());
           } catch(Exception e){
             System.err.println("Enjoy the stack trace!");
             e.printStackTrace();
           }
      

      【讨论】:

      • 您的答案可以通过简短描述您的程序与问题中发布的代码的不同之处来改进。
      【解决方案4】:

      如果您绝对必须使用文本文件并且无法切换到数据库,那么您可能想在行首指定一个奇怪的符号来表示“行已删除”。只需让您的解析器忽略这些行,例如配置文件中的注释行等。

      然后有一个像 Outlook 一样的定期“紧凑”例程,大多数数据库系统都会这样做,它会重写整个文件,不包括已删除的行。

      我强烈推荐 Think Before Coding 的回答,推荐数据库或其他结构化文件。

      【讨论】:

      • 是的,要求是能够拥有人类可读的文件(但我不确定任何人如何能够浏览一百万行!)。我对此要求无能为力。
      【解决方案5】:

      根据确切算作“删除”的内容,您最好的解决方案可能是用空格覆盖有问题的行。对于许多目的(包括人类消费),这相当于彻底删除该行。如果生成的空行有问题,并且您确定永远不会删除第一行,则可以通过用两个空格覆盖 CRLF 来将空格附加到前一行。

      (基于对 Bork Blatt 回答的评论)

      【讨论】:

        【解决方案6】:

        使用文件映射将文件移动到内存,就像 Think Before Coding 所做的那样,并在内存上和写入磁盘后进行删除。
        阅读此File Read Benchmarks - C#
        C# accessing memory map file

        【讨论】:

          【解决方案7】:

          在我的博客中,我对 C# 中的各种 I/O 方法进行了基准测试,以确定执行文件 I/O 的最有效方法。通常,最好使用 Windows ReadFile 和 WriteFile 函数。读取文件的下一个最快方法是通过 FileStream。要获得良好的性能,请一次读取文件,而不是一次读取一行,然后进行自己的解析。您可以从我的博客下载的代码为您提供了如何执行此操作的示例。还有一个 C# 类封装了 Windows ReadFile / WriteFile 功能并且非常易于使用。有关详细信息,请参阅我的博客:

          http://designingefficientsoftware.wordpress.com/2011/03/03/efficient-file-io-from-csharp

          鲍勃·布莱恩 MCSD

          【讨论】:

            【解决方案8】:

            将您的文件读入字典中的非删除行将 int 设置为 0 在线您需要将 int 标记为已删除设置为 1。使用 KeyValuePair 提取 不需要删除的行并将它们写入新文件。

            Dictionary<string, int> output = new Dictionary<string, int>();
            
            // read line from file
            
            ...
            
            // if need to delete line then set int value to 1
            
            // otherwise set int value to 0
            if (deleteLine)
            {
                output[line] = 1;
            }
            else
            {
                output[line] = 0;
            }
            
            // define the no delete List
            List<string> nonDeleteList = new List<string>();
            
            // use foreach to loop through each item in nonDeleteList and add each key
            // who's value is equal to zero (0) to the nonDeleteList.
            foreach (KeyValuePair<string, int> kvp in output)
            {
            
                if (kvp.Value == 0)
            
                {
            
                    nonDeleteList.Add(kvp.Key);
            
                }
            }
            
            // write the nondeletelist to the output file
            File.WriteAllLines("OUTPUT_FILE_NAME", nonDeleteList.ToArray());
            

            就是这样。

            【讨论】:

            • 使用字典根本不是一种有效的方法。
            猜你喜欢
            • 1970-01-01
            • 2013-05-13
            • 2011-01-28
            • 1970-01-01
            • 1970-01-01
            • 2023-02-08
            • 1970-01-01
            • 2018-09-21
            • 1970-01-01
            相关资源
            最近更新 更多