从文本文件中删除行的有效方法

时间:2009-02-10 13:01:55

标签: c# performance file-io

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

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

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);
    }
}

8 个答案:

答案 0 :(得分:10)

最直接的做法可能是最好的,将整个文件写入新文件,写下除了你不想要的那些之外的所有行。

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

读到您要“删除”该行的位置。 跳过要删除的行,并读取该字节数(包括CR + LF - 如果需要),在删除的行上写入该字节数,按字节数提前两个位置并重复直到文件结束。

希望这有帮助。

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

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

无法使用,如果您不想要的话,您仍然想要阅读只是不写。上面的代码既不会读取也不会写入。新文件将与旧文件完全相同。

你想要像

这样的东西
string line = reader.ReadLine();
if (!_deletedLines.Contains(counter)) 
{                            
    writer.WriteLine(line);                        
}

答案 1 :(得分:3)

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

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

答案 2 :(得分:2)

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

答案 3 :(得分:0)

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

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

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

答案 4 :(得分:0)

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

答案 5 :(得分:0)

根据确切的“删除”内容,您的最佳解决方案可能是用空格覆盖违规行。出于多种目的(包括人类消费),这相当于完​​全删除该行。如果生成的空白行有问题,并且您确定永远不会删除第一行,则可以通过用两个空格覆盖CRLF来将空格附加到上一行。

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

答案 6 :(得分:0)

在我的博客中,我对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

Bob Bryan MCSD

答案 7 :(得分:-1)

在非删除行上将文件读入字典,将int设置为0 在线你需要标记为已删除的set 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());

就是这样。