2

我想在读取文件(主机文件)时找到删除字符串 1 和字符串 2 的最有效方法,并删除包含字符串 1 或字符串 2 的整行。

目前我有,而且显然很迟钝。有什么更好的方法?

using(StreamReader sr = File.OpenText(path)){
    while ((stringToRemove = sr.ReadLine()) != null)
    {
        if (!stringToRemove.Contains("string1"))
        {
            if (!stringToRemove.Contains("string2"))
            {
                emptyreplace += stringToRemove + Environment.NewLine;
            }
        }
    }
    sr.Close();
    File.WriteAllText(path, emptyreplace);
    hostFileConfigured = false;
    UInt32 result = DnsFlushResolverCache();
    MessageBox.Show(removeSuccess, windowOffline);
}
4

6 回答 6

3

您遇到的主要问题是您经常使用大型常规字符串并将数据附加到末尾。这是每次都重新创建字符串并消耗大量时间,尤其是内存。通过使用string.Join它将避免创建(非常大量的)中间字符串值。

您还可以通过使用File.ReadLines而不是直接使用流来缩短代码以获取文本行。它并没有更好或更坏,只是更漂亮。

var lines = File.ReadLines(path)
    .Where(line => !line.Contains("string1") && !line.Contains("string2"));

File.WriteAllText(path, string.Join(Environment.NewLine, lines));

另一种选择是流式传输输出的写入。由于没有好的库方法可以在IEnumerable<string>不急切地评估输入的情况下写出 a ,因此我们必须编写自己的(这很简单):

public static void WriteLines(string path, IEnumerable<string> lines)
{
    using (var stream = File.CreateText(path))
    {
        foreach (var line in lines)
            stream.WriteLine(line);
    }
}

另请注意,如果我们正在流式传输我们的输出,那么我们将需要一个临时文件,因为我们不想同时读取和写入同一个文件。

//same code as before
var lines = File.ReadLines(path)
    .Where(line => !line.Contains("string1") && !line.Contains("string2"));

//get a temp file path that won't conflict with any other files
string tempPath = Path.GetTempFileName();
//use the method from above to write the lines to the temp file
WriteLines(tempPath, lines);
//rename the temp file to the real file we want to replace, 
//both deleting the temp file and the old file at the same time
File.Move(tempPath, path);

与第一个选项相比,此选项的主要优点是消耗的内存要少得多实际上,它一次只需要在内存中保存文件的行,而不是整个文件。不过,它确实(暂时)占用了一些额外的磁盘空间。

于 2013-03-11T16:07:43.373 回答
1

让我眼前一亮的第一件事是,string在 while 循环 ( emptyreplace) 中错误地使用类型变量(效率不高),使用StrinBuilder类型,这样会大大提高内存效率。

例如:

 StringBuilder emptyreplace = new StringBuilder(); 

using(StreamReader sr = File.OpenText(path)){
    while ((stringToRemove = sr.ReadLine()) != null)
    {
        if (!stringToRemove.Contains("string1"))
        {
            if (!stringToRemove.Contains("string2"))
            {
                //USE StringBuilder.Append, and NOT string concatenation
                emptyreplace.AppendLine(stringToRemove + Environment.NewLine);
            }
        }
    }
   ...
}

其余的似乎足够好。

于 2013-03-11T16:03:07.800 回答
0

两个建议:

  1. 创建一个要检测的字符串数组(我称它们为stopWords)并使用 Linq 的Any扩展方法。

  2. 与其构建文件并一次全部写入,不如在读取源文件时将每一行写入输出文件,并在完成后替换源文件。

结果代码:

string[] stopWords = new string[]
{
    "string1",
    "string2"
}

using(StreamReader sr = File.OpenText(srcPath))
using(StreamWriter sw = new StreamWriter(outPath))
{
    while ((stringToRemove = sr.ReadLine()) != null)
    {
        if (!stopWords.Any(s => stringToRemove.Contains(s))
        {
            sw.WriteLine(stringToRemove);
        }
    }
}

File.Move(outPath, srcPath);
于 2013-03-11T16:02:13.020 回答
0

有很多方法可以改善这一点:

  • 将您要搜索的单词数组编译成正则表达式(例如,word1|word2注意特殊字符),这样您只需遍历字符串一次。(这也将允许您使用\b仅匹配单词)

  • 通过 a 将每一行写入StreamWriter一个新文件,这样您就不需要在构建时将整个内容存储在内存中。(完成后,删除原文件并重命名新文件)

于 2013-03-11T16:05:12.227 回答
0

您的主机文件真的那么大,以至于您需要逐行阅读它吗?为什么不简单地这样做呢?

var lines = File.ReadAllLines(path);
var lines = lines.Where(x => !badWords.Any(y => x.Contains(y))).ToArray();
File.WriteAllLines(path, lines);
于 2013-03-11T16:05:23.317 回答
0

更新:我刚刚意识到您实际上是在谈论“主机文件”。假设您的意思%windir%\system32\drivers\etc\hosts是,该文件不太可能具有真正显着的大小(例如超过几个 KB)。所以就个人而言,我会采用最易读的方法。例如,@servy的那个。

最后,您将不得不阅读每一行并写入与您的标准不符的每一行。因此,您将始终拥有无法避免的基本 IO 开销。根据文件的实际(平均)大小,这可能会掩盖您在代码中用于实际过滤行的所有其他优化技术。

话虽如此,但是通过不在缓冲区中收集所有输出行,而是在读取它们时直接将它们写入输出文件,您可以在内存方面减少一点浪费(同样,如果你的文件不是很大)。

using (var reader = new StreamReader(inputfile))
{
  using (var writer = new StreamWriter(outputfile))
  {
    string line;
    while ((line = reader.ReadLine()) != null)
    {
       if (line.IndexOf("string1") == -1 && line.IndexOf("string2") == -1)
       {
          writer.WriteLine(line);
       }
    }
  }
}

File.Move(outputFile, inputFile);
于 2013-03-11T16:11:00.790 回答