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

最初に気になったのは、while ループ ( )string内での型変数の間違った (効率的ではない) 使用です。StrinBuilder型を使用すると、メモリ効率が大幅に向上します。emptyreplace

例えば:

 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

更新:あなたが実際に「hostsファイル」について話していることに気づきました。つまり%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 に答える
0

2 つの提案:

  1. 検出する文字列の配列を作成し (私はそれらを と呼びますstopWords)、Linq のAny拡張メソッドを使用します。

  2. ファイルを作成して一度にすべて書き込むのではなく、ソース ファイルの読み取り中に各行を一度に 1 つずつ出力ファイルに書き込み、完了したらソース ファイルを置き換えます。

結果のコード:

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; 特殊文字に注意) にコンパイルして、文字列を 1 回だけループする必要があるようにします。\b(これにより、単語のみを一致させるために使用することもできます)

  • 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 に答える