【问题标题】:Searching String or StringBuilder with a pattern range /start/ /end/使用模式范围 /start/ /end/ 搜索 String 或 StringBuilder
【发布时间】:2017-07-25 11:38:00
【问题描述】:

我想在 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# 没有它们之前一直在考虑使用它们)。由于我没有这些方法并且可能需要实现它们,所以我想问一下这是否是可行的方法?如果需要大量搜索,本节甚至建议使用字符串:MSDN - 我当然也可以使用它。我选择使用StringBuilder是因为字符串拼接是不断进行的,是不是应该在构建字符串的时候使用stringbuilder类型(拼接很多),然后在搜索的时候转换成string类型?

我也希望它具有高性能,并且很高兴听到有关如何实现它的建议。一般指导和实施细节表示赞赏。

【问题讨论】:

  • 您的问题有多具体?这应该只适用于这种特殊情况还是应该在与 awk 相同的范围内工作?
  • StringBuilder 用于构建字符串。首先构建它,然后生成整个字符串并使用String函数进行搜索。
  • @MetaColon - 需要更多的通用性,而不仅仅是一个特定的案例。我会将其指定为:“给定 utf8 字符串中存在的两个模式,找到以第一个模式开头并以第二个模式结束的文本。”可以添加包含 /end/ 模式的整行的附加要求。 @D Stanley - 确认您在正确的轨道上总是好的,谢谢。
  • @MindaugasBernatavičius RegEx 应该适合该任务。

标签: c# performance


【解决方案1】:

如果您需要处理一个潜在的大文件,您最好使用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);
    }
}

【讨论】:

    猜你喜欢
    • 2017-10-29
    • 2014-07-19
    • 2016-05-31
    • 2019-08-12
    • 2011-09-06
    • 1970-01-01
    • 1970-01-01
    • 2012-12-13
    • 2018-01-23
    相关资源
    最近更新 更多