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

10

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

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

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

如果您有如此大的数据存储,为什么不使用“真正的”数据库呢?这是因为您可用的工具有限,您当前的技能或项目规格的限制吗? - Tomas Aschan
这是一个来自“上级”的要求。使用真实的数据库对我来说会更容易,但不幸的是,我不能使用它。 - Valentin V
不太好,有一个bug - 很抱歉:( - 请看下面的回答 - Binary Worrier
8个回答

10

最直接的方法可能是最好的,将整个文件写入新文件中,写入除了不想要的行以外的所有行。

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

读取到想要“删除”行的位置。跳过要删除的行,并阅读该行的字节数(如果必要,包括CR + LF),将该字节数写在被删除的行上,同时将两个位置向前推进相应字节数并重复此过程直到文件末尾。

希望这可以帮助你。

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

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

如果你不想要某一行,但仍然希望读取它,那么上述代码既不会读取也不会写入该行。新文件与旧文件完全相同。

你需要的是类似于:

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

3

文本文件是顺序读取的,因此删除一行时,您必须将所有随后的行向上移动。

您可以使用文件映射(通过PInvoke调用的Win32 API)使此操作稍微容易些,但您应该考虑使用非连续结构的文件,以便可以将一行标记为已删除,而无需真正从文件中删除它...特别是如果这种情况经常发生。

如果我没错,File Mapping Api应该已经添加到.Net 4中了。


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

2
你的回答可以通过简短的描述来改进,说明你的程序与问题中发布的代码有何不同。 - Henrik Aasted Sørensen

0
如果你绝对必须使用文本文件而无法切换到数据库,也许你想在每行开头指定一个奇怪的符号来表示“已删除的行”。只需让解析器忽略这些行,就像配置文件中的注释行一样。
然后,像Outlook和大多数数据库系统一样,设置一个定期的“压缩”程序,重新编写整个文件,排除已删除的行。
我强烈建议采用Think Before Coding的答案,推荐使用数据库或其他结构化文件。

是的,要求是能够拥有可读性强的文件(但我不确定任何人如何可能浏览一百万行!)。我对这个需求无能为力。 - Valentin V

0

0

根据“删除”的确切定义,您的最佳解决方案可能是用空格覆盖有问题的行。对于许多目的(包括人类阅读),这等同于直接删除该行。如果结果的空白行是一个问题,并且您确定永远不会删除第一行,则可以通过用两个空格覆盖CRLF来将空格附加到前一行。

(基于Bork Blatt答案的评论)


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


-1

将文件读入字典,对于不需要删除的行将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());

就是这样。


使用字典并不是一种高效的方式。 - Shrivallabh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接