1

ディレクトリに属する​​いくつかのファイルで特定の文字列を検索しようとしています。(検索はサブディレクトリでも実行されます。現在、私はこのような解決策を思いつきました。

  1. ディレクトリとそのサブディレクトリ内のすべてのファイル名を取得します。
  2. ファイルを 1 つずつ開きます。
  3. 特定の文字列を検索する
  4. 含まれている場合は、ファイル名を配列に格納します。
  5. これを最後のファイルまで続けます。

    string[] fileNames = Directory.GetFiles(@"d:\test", "*.txt", SearchOption.AllDirectories);
    foreach (string sTem in fileNames)
    {
        foreach (string line in File.ReadAllLines(sTem))
        {
            if (line.Contains(SearchString))
            {
                MessageBox.Show("Found search string!");
                break;
            }
        }
    }
    

これよりも効率的で高速な他の方法/アプローチがあると思いますか? バッチファイルを使用していますか? わかった。もう 1 つの解決策は、findstr を使用することです (ただし、バッチ ファイルを使用せずに C# プログラムで直接使用するにはどうすればよいでしょうか? 最も効率的な (または私が行ったものよりも効率的な方法は?) コード例は大歓迎です!

別の解決策を見つけました。

Process myproc = new Process();
myproc.StartInfo.FileName = "findstr";
myproc.StartInfo.Arguments = "/m /s /d:\"c:\\REQs\" \"madhuresh\" *.req";
myproc.StartInfo.RedirectStandardOutput = true;
myproc.StartInfo.UseShellExecute = false;


myproc.Start();
string output = myproc.StandardOutput.ReadToEnd();
myproc.WaitForExit();

このプロセスの実行は適切ですか? こちらのコメントも大歓迎です!

@AbitChevの方法によると、なめらかです(効率的かどうかはわかりません!)。とにかく、このように続きます。これは、すべてのディレクトリとサブディレクトリを検索します!

IEnumerable<string> s = from file in Directory.EnumerateFiles("c:\\directorypath", "*.req", SearchOption.AllDirectories)
                   from str in File.ReadLines(file)
                   //where str.Contains("Text@tosearched2")
                   where str.IndexOf(sSearchItem, StringComparison.OrdinalIgnoreCase) >= 0
                   select file;

        foreach (string sa in s)
            MessageBox.Show(sa);

(大文字と小文字を区別しない検索の場合。誰かを助けるかもしれません。)コメントしてください!ありがとう。

4

4 に答える 4

3

Directory.EnumerateFiles()と-を使用すると、File.ReadLines()どちらもデータの遅延読み込みを提供します。

from file in Directory.EnumerateFiles(path)
from arr in File.ReadLines(file)
from str in arr
where str.Contains(pattern)
select new 
{
    FileName = file, // file containing matched string
    Line = str // matched string
};

また

foreach (var file in Directory.EnumerateFiles(path).AsParallel())
{
    try
    {
        foreach (var arr in File.ReadLines(file).AsParallel())
        {
            // one more try here?
            foreach (var str in arr)
            {
                if (str.Contains(pattern))
                {
                    yield return new 
                    {
                        FileName = file, // file containing matched string
                        Line = str // matched string
                    };
                }
            }
        }
    }
    catch (SecurityException)
    {
        // swallow or log
    }
}
于 2012-08-29T08:20:44.503 に答える
2

このようなものはどうですか

var found = false;
string file;

foreach (file in Directory.EnumerateFiles(
            "d:\\tes\\",
            "*.txt",
            SearchOption.AllDirectories))
{
    foreach(var line in File.ReadLines(file))
    {
        if (line.Contains(searchString))
        {
            found = ture;
            break;
        }
    }

    if (found)
    {
            break;
    }
}

if (found)
{
    var message = string.Format("Search string found in \"{0}\".", file)
    MessageBox.Show(file);
}

これには、すべてのファイルの名前と各ファイルの内容ではなく、必要なものだけをメモリにロードできるという利点があります。


String.Contains私はあなたが使用していることに注意してください

序数 (大文字と小文字を区別し、カルチャを区別しない) 比較を実行します

これにより、単純な文字単位の比較を行うことができます。

私は小さなヘルパー関数から始めます

private static bool CompareCharBuffers(
    char[] buffer,
    int headPosition,
    char[] stringChars)
{
    // null checking and length comparison ommitted

    var same = true;
    var bufferPos = headPosition;
    for (var i = 0; i < stringChars.Length; i++)
    {
        if (!stringChars[i].Equals(buffer[bufferPos]))
        {
            same = false;
            break;
        }

        bufferPos = ++bufferPos % (buffer.Length - 1);
    }

    return same;
}

次に、前のアルゴリズムを変更して、このような関数を使用します。

var stringChars = searchString.ToCharArray();
var found = false;
string file;


foreach (file in Directory.EnumerateFiles(
            "d:\\tes\\",
            "*.txt",
            SearchOption.AllDirectories))
{
    using (var reader = File.OpenText(file))
    {
        var buffer = new char[stringChars.Length];
        if (reader.ReadBlock(buffer, 0, buffer.Length - 1) 
                < stringChars.Length - 1)
        {
            continue;
        }

        var head = 0;
        var nextPos = buffer.Length - 1;
        var nextChar = reader.Read();
        while (nextChar != -1)
        {
            buffer[nextPos] = (char)nextChar;

            if (CompareCharBuffers(buffer, head, stringChars))
            {
               found = ture;
               break;
            }

            head = ++head % (buffer.Length - 1);
            if (head == 0)
            {
                nextPos = buffer.Length - 1;
            }
            else
            {
                nextPos = head - 1;
            } 

            nextChar = reader.Read();
        }

        if (found)
        {
            break;
        }
    }
}

if (found)
{
    var message = string.Format("Search string found in \"{0}\".", file)
    MessageBox.Show(file);
}

これはchar、検索文字列がメモリに含むのと同じ数の のみを保持し、各ファイルでローリング バッファを使用します。理論的には、ファイルに新しい行が含まれず、ディスク全体を消費するか、検索文字列に新しい行が含まれる可能性があります。


さらなる作業として、アルゴリズムのファイルごとの部分を関数に変換し、マルチスレッド アプローチを調査します。

したがって、これは内部関数になります。

static bool FileContains(string file, char[] stringChars)
{
    using (var reader = File.OpenText(file))
    {
        var buffer = new char[stringChars.Length];
        if (reader.ReadBlock(buffer, 0, buffer.Length - 1) 
                < stringChars.Length - 1)
        {
            return false;
        }

        var head = 0;
        var nextPos = buffer.Length - 1;
        var nextChar = reader.Read();
        while (nextChar != -1)
        {
            buffer[nextPos] = (char)nextChar;

            if (CompareCharBuffers(buffer, head, stringChars))
            {
               return true;
            }

            head = ++head % (buffer.Length - 1);
            if (head == 0)
            {
                nextPos = buffer.Length - 1;
            }
            else
            {
                nextPos = head - 1;
            } 

            nextChar = reader.Read();
        }

        return false;
    }
}

次に、このようにファイルを並行して処理できます

var stringChars = searchString.ToCharArray();

if (Directory.EnumerateFiles(
            "d:\\tes\\",
            "*.txt",
            SearchOption.AllDirectories)
    .AsParallel()
    .Any(file => FileContains(file, stringChars)))
{
    MessageBox.Show("Found search string!");
}
于 2012-08-29T08:18:30.563 に答える
0

Tasks.Dataflow(この .dll は現在 .NET 4.5 の一部ではありませんが、ここからダウンロードできます) を使用して「パイプライン」を作成し、すべてのファイルを使用して明示的な文字列を検索することができます。このリファレンス実装を見てください。

于 2012-08-29T08:45:22.783 に答える