使用模式范围/ start / / end /搜索String或StringBuilder

时间:2017-07-25 11:38:00

标签: c# performance

我想在C#中创建一个函数(如果需要,带有一组辅助函数),它将执行与awk '/start/,/end/' file类似的操作 - 除了它将包括所有最后的匹配,而不是终止于第一

让我们说:

# cat text
"13:08:30:5276604 Main: 41044 - 48.7617 M-- Other PIDS 2 - 79.1016 M"
"13:08:30:5736962 Main: 41044 - 48.7617 M-- Other PIDS 2 - 79.1016 M"
"13:08:30:6227343 Main: 41044 - 48.7617 M-- Other PIDS 2 - 79.1016 M"
"13:08:30:6757752 Main: 41044 - 48.7617 M-- Other PIDS 2 - 79.1016 M"
"13:08:30:7208103 Main: 41044 - 48.7617 M-- Other PIDS 2 - 79.1016 M"
"13:08:30:7668739 Main: 41044 - 48.7617 M-- Other PIDS 2 - 79.1016 M"
"13:08:30:8129079 Main: 41044 - 48.7617 M-- Other PIDS 2 - 79.1016 M"

预期:

"13:08:30:6227343 Main: 41044 - 48.7617 M-- Other PIDS 2 - 79.1016 M"
"13:08:30:6757752 Main: 41044 - 48.7617 M-- Other PIDS 2 - 79.1016 M"
"13:08:30:7208103 Main: 41044 - 48.7617 M-- Other PIDS 2 - 79.1016 M"
"13:08:30:7668739 Main: 41044 - 48.7617 M-- Other PIDS 2 - 79.1016 M"

AWK输出:

# awk '/13:08:30:62/,/13:08:30:7/' text
"13:08:30:6227343 Main: 41044 - 48.7617 M-- Other PIDS 2 - 79.1016 M"
"13:08:30:6757752 Main: 41044 - 48.7617 M-- Other PIDS 2 - 79.1016 M"
"13:08:30:7208103 Main: 41044 - 48.7617 M-- Other PIDS 2 - 79.1016 M"

我最初认为我可能只是通过两个条件pattern_1 | pattern_2获得正则表达式匹配,但是如果匹配值之间存在值,则这将不起作用。

我还发现C#StringBuilder类没有.indexOf().lastIndexOf()方法(我在JAVA方面有更多的经验,所以在考虑使用这些,直到我看到C#没有它们)。由于我没有这些方法并且需要可能实现它们,所以我想问一下这是否可行?如果需要进行大量搜索,本节甚至建议使用String:MSDN - 我当然也可以使用它。我选择使用StringBuilder,因为字符串连接是不断执行的,我应该在构建字符串时使用stringbuilder类型(很多连接),但在搜索时会转换为string类型吗?

我也希望这是高效的,听听如何制作它的建议真是太棒了。一般指导和实施细节表示赞赏。

1 个答案:

答案 0 :(得分:0)

如果您需要处理潜在的大型文件,最好使用StreamReader并使用ReadLine方法按行处理它。这可以防止你最终得到内存中的完整文件,就像你在使用StringBuilder时那样。通过在实现上使用抽象TextReader,您可以将字符串用作(文件)流。

要检查开始结束匹配,您可以使用Regex class。它的Match方法返回一个Success属性的实例,当找到匹配项时该属性为真。

为了达到你想到的逻辑,我认为有三种状态:在我们找到结束之前,在我们找到结束之前,我们仍然找到结束。我选择在迭代器中实现它,因为使用yield关键字,因为它几乎可以免费提供状态机。

以下是实施:

void Main()
{
    // use a streamreader to read characters
    // the .ctor accpets an Encoding as second parameter
    using(var sr = new StreamReader(@"sample.txt"))
    {
        ReadFromBeginToEnd("13:08:30:62","13:08:30:7",sr);
    }

    var text =@"
13:08:30:6227343 Main: 41044 - 48.7617 M-- Other PIDS 2 - 79.1016 M
13:08:30:6757752 Main: 41044 - 48.7617 M-- Other PIDS 2 - 79.1016 M
13:08:30:7208103 Main: 41044 - 48.7617 M-- Other PIDS 2 - 79.1016 M
13:08:30:7668739 Main: 41044 - 48.7617 M-- Other PIDS 2 - 79.1016 M";
    using(var sr = new StringReader(text))
    {
        ReadFromBeginToEnd("13:08:30:62","13:08:30:7", sr);
    }
}

// enumerate over the lines from the streamreader
// accepting two regexes, start and end
IEnumerable<string> FromBeginToEnd(TextReader rdr, Regex start, Regex end)
{
   // 1st state
   var line = rdr.ReadLine(); // initial read, null means we're done
   // read the lines until we hit our start match
   while(line != null && !start.Match(line).Success) 
   {
      // don't return these lines
      line = rdr.ReadLine();    
   }
   // 2nd state
   // read the lines while we didn't hit our end match
   while(line != null && !end.Match(line).Success) 
   {
      // return this line to the caller
      yield return line;
      line = rdr.ReadLine();    
   }
   // 3rd state
   // read the lines while we find our end match
   while(line != null && end.Match(line).Success) 
   {
      // return this line to the caller
      yield return line;
      line = rdr.ReadLine();    
   }
   // iterator is done
   yield break;
}

// take a start and end string that can be compiled to a regex
// and a file (fullpath)
void ReadFromBeginToEnd(string start, string end, TextReader reader) 
{
    // loop over the lines that mach the criteria
    // FromBeginToEnd is our custom enumerator
    foreach(var line in FromBeginToEnd(reader, new Regex(start), new Regex(end)))
    {
       // write to standard out
       // but this can be an StreamWriter.WriteLine as well.
       Console.WriteLine(line);
    }
}