【问题标题】:Remove all lines (comments) starting with "**" by using Regex (.NET Framework, C#)使用正则表达式(.NET Framework,C#)删除所有以“**”开头的行(注释)
【发布时间】:2020-12-13 13:50:55
【问题描述】:

我正在开发一个可以读取和处理文本文件的应用程序。这些文本文件具有以下结构:

** A comment
* A command
Data, data, data
** Some other comment
* Another command
1, 2, 3
4, 5, 6

我使用string text = File.ReadAllText(file); 将整个文本文件存储在内存中。但是,我想删除所有作为注释的行,即所有以 "**" 开头的行。

这可以通过以下方法实现:

// this method also removes any white-spaces (this is intended)
string RemoveComments(string textWithComments)
{
    string textWithoutComments = null;

    string[] split = Regex.Split(text.Replace(" ", null), "\r\n|\r|\n").ToArray();
    foreach (string line in split)
        if (line.Length >= 2 && line[0] == '*' && line[1] == '*') continue;
        else textWithoutComments += line + "\r\n";

    return textWithoutComments;
}

然而,这对于大文件来说实际上是非常慢的。我还认为可以用一行代码(可能使用正则表达式)替换整个方法。我怎样才能做到这一点(我也从未使用过正则表达式)。

PS:我也想避开StreamReaders。

编辑

示例文件如下所示:

** Initial comment
*Command-0
** Some Comment: Header: Text
** Some text: text
*Command-1
**
** Some comment or text
**
*Command-2
*Command-3
      1,            2,            3
      2,            2,            4
      3,            2,            5
** END COMMENT

【问题讨论】:

  • 虽然它不会使解析本身更快,但您应该使用异步 IO。我也不清楚为什么你会使用Regex 而不是text.Split('\r', 'n'),而你的ToArray 调用毫无意义,而且可能代价高昂。
  • 文件有多大?
  • 为什么要避免使用 StreamReader?如果您希望这更快,使用 StreamReader 处理文件就是您想要的。
  • @AluanHaddad 处理超过 100,000 行的文件,将 string[] split = Regex.Split(text.Replace(" ", null), "\r\n|\r|\n").ToArray(); 替换为 string[] split = text.Replace(" ", null).Split('\r', '\n'); 执行时间从大约 100 毫秒到大约 60 毫秒。问题出在foreach 循环中(执行需要几分钟)。
  • @Enigmativity 啊,我明白你的意思了,我正在考虑使用不同的文件 API。

标签: c# .net regex .net-framework-4.8


【解决方案1】:

连接一个字符串会在每次字符串大小改变时重新分配内存。

StringBuilder 不会经常重新分配,并且会显着减少*运行时间

string RemoveComments(string textWithComments)
{
    StringBuilder textWithoutComments = new StringBuilder();

    string[] split = text.Replace(" ", null).Split('\r', '\n');
    foreach (string line in split)
        if (line.Length >= 2 && line[0] == '*' && line[1] == '*') continue;
        else textWithoutComments.Append(line + "\r\n");

    return textWithoutComments.ToString();
}

根据 Aluan 的建议编辑

【讨论】:

  • 使用StringBuilder 可以大大提高执行时间,但是使用这种方法我会得到额外的“\r\n”。通过使用else if (!string.IsNullOrEmpty(line)) textWithoutComments.Append(line + "\r\n"); 重做else 行来解决此问题
【解决方案2】:

为什么不只是:

var text = @"** A comment
* A command
Data, data, data
** Some other comment
* Another command
1, 2, 3
4, 5, 6";

var textWithoutComments = Regex.Replace(text, @"(^|\n)\*\*.*(?=\n)", string.Empty); //this version will leave a \n at the beginning of the string if the text starts with a comment.
var textWithoutComments = Regex.Replace(text, @"(^\*\*.*\r\n)|((\r\n)\*\*.*($|(?=\r\n)))", string.Empty); //this versioh deals with that problem, for a longer regex that treats the first line differently than the other lines (consumes the \n rather than leaving it in the text)

不了解性能,我没有准备好测试数据...

PS:我也倾向于相信,如果你想要最高性能,一些流式传输可能是理想的,你总是可以从方法中返回一个字符串,如果这样可以更容易地进行后续处理。我认为该线程中的大多数人都建议将 StreamReader 用于迭代/读取/解释部分,无论您决定构建哪种返回类型。

【讨论】:

  • 你能解释一下正则表达式模式吗?在我的文件中,这会删除一些带有 cmets 的行,但不是全部(即使它们都以 "**" 开头)。
  • 你能举个例子吗?我确实注意到了一个我正在尝试解决的错误。
  • 我试图弄清楚如何将其视为单行正则表达式,因为我无法找到同时删除行的多行版本。 (^|\n) 部分匹配换行符字符串的开头。 \*\* 匹配“**”,其余的只是尝试定位下一个换行符,但我的逻辑有问题。更糟糕的是,Regexr.com 上的引擎的行为似乎与 C# 中的引擎不同。 @卡洛斯
  • 我添加了另一个示例。
  • 您的方法似乎删除了所有其他评论。
【解决方案3】:

我知道你说过你不想使用 StreamReader,但是下面的代码在我的电脑上可以在不到半秒的时间内处理 400,000 行。它简单、直接且快速。

static void RemoveCommentsAndWhitespace(string filePath)
{
    if (!File.Exists(filePath))
    {
        Console.WriteLine($"ERR: The file '{filePath}' does not exist.", nameof(filePath));
    }

    string outfile = filePath + ".out";

    using StreamReader sr = new StreamReader(filePath);
    using StreamWriter sw = new StreamWriter(outfile);
    string line;

    while ((line = sr.ReadLine()) != null)
    {
        string tmp = line.Replace(" ", string.Empty);
        if (tmp.StartsWith("**"))
        {
            continue;
        }

        sw.WriteLine(tmp);
    }

    Console.WriteLine($"Wrote to {outfile}.");
}

【讨论】:

    猜你喜欢
    • 2014-11-30
    • 2011-04-01
    • 1970-01-01
    • 2015-02-25
    • 2011-01-28
    • 1970-01-01
    • 1970-01-01
    • 2012-04-08
    相关资源
    最近更新 更多