2

それぞれ150MBのサイズの複数の .txt ファイルがあります。C# を使用して、各ファイルから文字列パターンを含むすべての行を取得し、それらの行を新しく作成したファイルに書き込む必要があります。

私はすでに同様の質問を調べましたが、提案された回答のいずれも、結果を取得する最速の方法を提供できませんでした. 正規表現、linq query、contains メソッド、バイト配列での検索を試しましたが、ファイルの内容を読み取って比較するのに 30 分以上かかっています。

私のテスト ファイルには特定の形式はありません。区切り文字に基づいて分割したり、DataViews に基づいてフィルター処理したりできない生データのようなものです。以下は、そのファイルの各行のサンプル形式です。

サンプル.txt

LTYY;;0,0,;123456789;;;;;;;20121002 02:00;;
ptgh;;0,0,;123456789;;;;;;;20121002 02:00;;
HYTF;;0,0,;846234863;;;;;;;20121002 02:00;;
Multiple records......

マイコード

using (StreamWriter SW = new StreamWriter(newFile))
            {
                using(StreamReader sr = new StreamReader(sourceFilePath))
                {
                while (sr.Peek() >= 0) 
                {
                   if (sr.ReadLine().Contains(stringToSearch))
                     SW.WriteLine(sr.ReadLine().ToString());
                 }
}
}

Sample.txt から123456789を検索するのに 1 分もかからないサンプル コードが必要です。私の要件が明確でない場合はお知らせください。前もって感謝します!

編集

根本的な原因は、ファイルがリモート サーバーに存在するため、ファイルの読み取りに時間がかかることであることがわかりました。これは、ファイルをローカル マシンにコピーしたときに、すべての比較方法が非常に迅速に完了したため、読み取り方法には問題がないためです。またはコンテンツを比較すると、多かれ少なかれ同じ時間がかかりました。

しかし、この問題にどのように対処すればよいでしょうか。これらのファイルをすべて比較のために自分のマシンにコピーできず、OutOfMemory 例外が発生します。

4

7 に答える 7

3

検索する最速の方法は、Boyer-Moore 文字列検索アルゴリズムを使用することです。この方法では、ファイルからすべてのバイトを読み取る必要はありませんが、バイトへのランダム アクセスが必要になるか、 Rabin Karp アルゴリズムを使用して試すことができます。

または、この回答から、次のコードのようなことを試すことができます:

  public static int FindInFile(string fileName, string value)
  {   // returns complement of number of characters in file if not found
    // else returns index where value found
  int index = 0;
   using (System.IO.StreamReader reader = new System.IO.StreamReader(fileName))
   {
    if (String.IsNullOrEmpty(value))
        return 0;
    StringSearch valueSearch = new StringSearch(value);
    int readChar;
    while ((readChar = reader.Read()) >= 0)
    {
        ++index;
        if (valueSearch.Found(readChar))
            return index - value.Length;
    }
}
return ~index;
}
 public class StringSearch
 {   // Call Found one character at a time until string found
private readonly string value;
private readonly List<int> indexList = new List<int>();
public StringSearch(string value)
{
    this.value = value;
}
public bool Found(int nextChar)
{
    for (int index = 0; index < indexList.Count; )
    {
        int valueIndex = indexList[index];
        if (value[valueIndex] == nextChar)
        {
            ++valueIndex;
            if (valueIndex == value.Length)
            {
                indexList[index] = indexList[indexList.Count - 1];
                indexList.RemoveAt(indexList.Count - 1);
                return true;
            }
            else
            {
                indexList[index] = valueIndex;
                ++index;
            }
        }
        else
        {   // next char does not match
            indexList[index] = indexList[indexList.Count - 1];
            indexList.RemoveAt(indexList.Count - 1);
        }
    }
    if (value[0] == nextChar)
    {
        if (value.Length == 1)
            return true;
        indexList.Add(1);
    }
    return false;
}
public void Reset()
{
    indexList.Clear();
}
}
于 2012-11-26T18:10:28.487 に答える
1

すでに述べたように、データベースが必要ですが、何でも構いません。

それを行うための最速、最短、そして最も良い方法(1行でも)は次のとおりです。

File.AppendAllLines("b.txt", File.ReadLines("a.txt")
                                 .Where(x => x.Contains("123456789")));

でも速い?150MBは150MBです。しばらく時間がかかります。比較を高速化するために、メソッドを独自のメソッドに置き換えることができContainsますが、それはまったく別の問題です。

他の可能な解決策...

var sb = new StringBuilder();

foreach (var x in File.ReadLines("a.txt").Where(x => x.Contains("123456789")))
{
    sb.AppendLine(x);
}

File.WriteAllText("b.txt", sb.ToString()); // That is one heavy operation there...

ファイル サイズ 150MB でテストしたところ、すべての結果が 3 秒以内に見つかりました。時間がかかるのは、結果を2番目のファイルに書き込むことです(結果が多い場合)。

于 2012-11-26T18:22:38.320 に答える
1

実行にどれくらい時間がかかるかはわかりませんが、いくつかの改善点があります。

using (StreamWriter SW = new StreamWriter(newFile))
{
    using (StreamReader sr = new StreamReader(sourceFilePath))
    {
        while (!sr.EndOfStream)
        {
            var line = sr.ReadLine();
            if (line.Contains(stringToSearch))
                SW.WriteLine(line);
        }
    }
}

は必要ないことに注意してください。必要PeekEndOfStreamものが得られます。あなたはReadLine2 回電話をかけていました (おそらく、あなたが意図したものではありませんでした)。を呼び出す必要はありませToString()string

于 2012-11-26T18:08:55.657 に答える
1

150MBは150MBです。150MB 全体を 1 行ずつ処理するスレッドが 1 つある場合 (改行文字/グループまたは EOF によって「行」が終了する)、プロセスは 150MB のデータすべてを読み込んでスピンする必要があります (すべてではない)。同時にすべてを保持する必要はありません)。157,286,400 文字を直線的に検索するには、非常に単純に時間がかかります。そのようなファイルがたくさんあるとあなたは言います。

初めにすること; ストリームから行を 2 回読み取っています。これにより、ほとんどの場合、実際には一致するたびに 2 行が読み取られます。新しいファイルに書き込まれるのは、検索文字列を含む行の後の行になります。これはおそらくあなたが望んでいるものではありません(繰り返しますが、そうかもしれません)。実際に検索文字列を含む行を書きたい場合は、Contains チェックを実行する前にそれを変数に読み込みます。

次に、必要に応じて、String.Contains() は線形検索を実行します。あなたの場合、動作は実際には N^2 に近づきます。これは、文字列内の文字列を検索するときに最初の文字を見つける必要があり、その場所で、各文字が後続の文字と 1 つずつ一致するためです。検索文字列が一致したか、一致しない文字が見つかりました。不一致が発生した場合、アルゴリズムは最初の一致の後に文字に戻って、一致する可能性のあるものをスキップしないようにする必要があります。つまり、長い文字列を部分一致が多い長い文字列と比較してチェックするときに、同じ文字を何度もテストできます。したがって、この戦略は技術的には「ブルート フォース」ソリューションです。残念ながら、(ソートされていないデータ ファイルなど) どこを調べればよいかわからない場合、これ以上効率的な解決策はありません。

ファイルのデータを並べ替えてからインデックス検索を実行できる以外に、私が提案できる唯一の高速化は、ソリューションをマルチスレッド化することです。すべてのファイルを調べる 1 つのスレッドでのみこのメソッドを実行している場合、ジョブを実行するスレッドは 1 つだけでなく、そのスレッドはハード ドライブが必要なデータを提供するのを常に待機しています。一度に 1 つのファイルを処理する 5 つまたは 10 のスレッドを持つことで、最新のマルチコア CPU の真の能力をより効率的に活用できるだけでなく、1 つのスレッドがハード ドライブで待機している間に、データが読み込まれた別のスレッドを実行できます。このアプローチの効率をさらに高めます。データが CPU から離れているほど、CPU がデータを取得するのに時間がかかります。また、CPU が 1 秒あたり 20 億から 40 億の処理を実行できる場合、

于 2012-11-26T18:10:24.400 に答える
0

読み取りと書き込みを同時に行わないでください。最初に検索し、一致する行のリストを保存し、最後にファイルに書き込みます。

using System;
using System.Collections.Generic;
using System.IO;
...
List<string> list = new List<string>();
using (StreamReader reader = new StreamReader("input.txt")) {
  string line;
  while ((line = reader.ReadLine()) != null) {
    if (line.Contains(stringToSearch)) {
      list.Add(line); // Add to list.
    }
  }
}
using (StreamWriter writer = new StreamWriter("output.txt")) {
  foreach (string line in list) {
    writer.WriteLine(line);
  }
}
于 2012-11-26T18:09:33.093 に答える