1

我正在尝试在属于某个目录的某些文件中搜索特定出现的字符串。(搜索也在子目录中进行。目前,我想出了一个类似这样的解决方案。

  1. 获取目录及其子目录中的所有文件名。
  2. 一个一个地打开文件。
  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;
            }
        }
    }
    

我认为还有其他方法/方法比这更有效和更快吗?使用批处理文件?好的。另一种解决方案是使用 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()and 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包含与搜索字符串在内存中包含的一样多的 s,并在每个文件中使用滚动缓冲区。从理论上讲,该文件可能不包含新行并占用您的整个磁盘,或者您的搜索字符串可能包含一个新行。


作为进一步的工作,我会将算法的每个文件部分转换为函数并研究多线程方法。

所以这将是内部函数,

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 回答
1

这很好用。我在 0.5 毫秒内搜索了 230 个文件中的大约 500 个术语。这是非常占用内存的;它将每个文件加载到内存中

public class FindInDirectory
{
    public class Match
    {
        public string Pattern { get; set; }
        public string Directory { get; set; }
        public MatchCollection Matches { get; set; }
    }

    public static List<FindInDirectory.Match> Search(string directory, string searchPattern, List<string> patterns)
    {
        //find all file locations
        IEnumerable<string> files = System.IO.Directory.EnumerateFiles(directory, searchPattern, System.IO.SearchOption.AllDirectories);

        //load all text into memory for MULTI-PATERN
        //this greatly increases speed, but it requires a ton of memory!
        Dictionary<string, string> contents = files.ToDictionary(f => f, f => System.IO.File.ReadAllText(f));

        List<FindInDirectory.Match> directoryMatches = new List<Match>();

        foreach (string pattern in patterns)
        {
            directoryMatches.AddRange
            (
                contents.Select(c => new Match
                {
                    Pattern = pattern,
                    Directory = c.Key,
                    Matches = Regex.Matches(c.Value, pattern, RegexOptions.IgnoreCase | RegexOptions.Multiline)
                })
                .Where(c => c.Matches.Count > 0)//switch to > 1 when program directory is same or child of search
            );
        };

        return directoryMatches;
    }

}

采用:

    static void Main(string[] args)
    {
        List<string> patterns = new List<string>
        {
            "class",
            "foreach",
            "main",
        };
        string searchPattern = "*.cs";
        string directory = "C:\\SearchDirectory";

        DateTime start = DateTime.UtcNow;

        FindInDirectory.Search(directory, searchPattern, patterns);

        Console.WriteLine((DateTime.UtcNow - start).TotalMilliseconds);
        Console.ReadLine();
    }
于 2016-03-30T17:29:39.963 回答
0

Tasks.Dataflow您可以使用(此 .dll 当前不是 .NET 4.5 的一部分,但您可以从此处下载它)创建一个“管道”来使用所有文件并搜索显式字符串。看看这个参考实现

于 2012-08-29T08:45:22.783 回答