C#Regex,解析符号所包含的字符串的更有效方法吗?

时间:2015-08-30 03:22:15

标签: c# regex string

我不确定是否可以问......但是这里有。

我实现了一个使用正则表达式解析字符串的方法,每个匹配都通过带有顺序的委托进行解析(实际上,顺序并不重要 - 我想,等等,是吗?......但我这样写了,它没有经过全面测试):

  • Pattern Regex.Replace:@"(?<!\\)\$.+?\$" then String.Replace:@"\$", @"$";替换美元符号包围的字符串。忽略反斜杠,然后删除反斜杠。例如:&#34; $全球名称$&#34; - &GT; &#34; motherofglobalvar&#34;,&#34; Money \ $ 9000&#34; - &GT; &#34;金钱$ 9000&#34;
  • Pattern Regex.Replace @"(?<!\\)%.+?%" then String.Replace @"\%", @"%";替换百分号包围的字符串。忽略反斜杠,然后删除反斜杠。与前面的示例相同:&#34;%local var%&#34; - &GT; &#34; lordoflocalvar&#34;,&#34;超过9000 \%&#34; - &GT; &#34;超过9000%&#34;
  • Pattern Regex.Replace @"(?<!\\)@" then String.Replace @"\@", @"@";替换char&#39; @&#39;用空格,&#39; &#39 ;.但忽略反斜杠,然后删除反斜杠。例如:&#34;我@ @ @ @ @ @ @ @&#34; - &GT; &#34;我太过努力了#34;,&#34; qw \ @ op&#34; - &GT; &#34; QW @ OP&#34;

在没有太多经验的情况下我做了什么(我认为):

//parse variable
    public static string ParseVariable(string text)
    {
        return Regex.Replace(Regex.Replace(Regex.Replace(text, @"(?<!\\)\$.+?\$", match =>
        {
            string trim = match.Value.Trim('$');
            string trimUpper = trim.ToUpper();
            return variableGlobal.ContainsKey(trim) ? variableGlobal[trim] : match.Value;
        }).Replace(@"\$", @"$"), @"(?<!\\)%.+?%", match =>
        {
            string trim = match.Value.Trim('%');
            string trimUpper = trim.ToUpper();
            return variableLocal.ContainsKey(trim) ? variableLocal[trim] : match.Value;
        }).Replace(@"\%", @"%"), @"(?<!\\)@", " ").Replace(@"\@", @"@");
    }

简而言之,我使用的是:Regex.Replace().Replace()

由于我需要解析3种符号,因此将其链接为:Regex.Replace(Regex.Replace(Regex.Replace().Replace()).Replace()).Replace()

有没有比这更有效的方法?我的意思是,不需要经历6次文本? (3次regex.replace,3次string.replace,其中每次替换修改下一次替换时使用的文本)

或者它是最好的方式吗?

感谢。

1 个答案:

答案 0 :(得分:0)

我认为,这是对问题的独特看法。您可以构建一个用于逐个构建整体模式的类。此类将负责生成将MatchEvaluator传递给Replace的{​​{1}}代理。

class RegexReplacer
{
    public string Pattern { get; private set; }
    public string Replacement { get; private set; }
    public string GroupName { get; private set; }
    public RegexReplacer NextReplacer { get; private set; }

    public RegexReplacer(string pattern, string replacement, string groupName, RegexReplacer nextReplacer = null)
    {
        this.Pattern = pattern;
        this.Replacement = replacement;
        this.GroupName = groupName;
        this.NextReplacer = nextReplacer;
    }

    public string GetAggregatedPattern()
    {
        string constructedPattern = this.Pattern;
        string alternation = (this.NextReplacer == null ? string.Empty : "|" + this.NextReplacer.GetAggregatedPattern());   // If there isn't another replacer, then we won't have an alternation; otherwise, we build an alternation between this pattern and the next replacer's "full" pattern

        constructedPattern = string.Format("(?<{0}>{1}){2}", this.GroupName, this.Pattern, alternation);    // The (?<XXX>) syntax builds a named capture group. This is used by our GetReplacementDelegate metho.

        return constructedPattern;
    }

    public MatchEvaluator GetReplaceDelegate()
    {
        return (match) =>
        {
            if (match.Groups[this.GroupName] != null && match.Groups[this.GroupName].Length > 0)    // Did we get a hit on the group name?
            {
                return this.Replacement;
            }
            else if (this.NextReplacer != null)                                                     // No? Then is there another replacer to inspect?
            {
                MatchEvaluator next = this.NextReplacer.GetReplaceDelegate();

                return next(match);
            }
            else
            {
                return match.Value;                                                                 // No? Then simply return the value
            }
        };
    }
}

PatternReplacement代表什么应该是显而易见的。 GroupName是一种让替换评估者知道哪个RegexReplacer片段导致匹配的黑客攻击。 NextReplacer指向另一个包含不同模式片段的替换器实例(等)。

这里的想法是拥有一种代表整体模式的对象链接列表。您可以在最外层的替换器上调用GetAggregatedPattern以获得完整的模式 - 每个替换器调用下一个替换器GetAggregatedPattern来获取替换器的模式片段,连接自己的片段。 GetReplacementDelegate生成MatchEvaluator。此MatchEvaluator会将自己的GroupNameMatch个已捕获的群组进行比较。如果捕获了组名,那么我们会有一个匹配,并且我们会返回此替换器的Replacement值。否则,我们进入下一个替换器(如果有的话)并重复组名称比较。如果任何替换者没有命中,那么我们只是回报原始值(即模式匹配的内容;这应该是罕见的)。

这种用法可能如下所示:

string target = @"$global name$ Money \$9000 %local var% It's over 9000\% I@hit@the@ground@too@hard qw\@op";
RegexReplacer dollarWrapped = new RegexReplacer(@"(?<!\\)\$[^$]+\$", "motherofglobalvar", "dollarWrapped");
RegexReplacer slashDollar = new RegexReplacer(@"\\\$", string.Empty, "slashDollar", dollarWrapped);
RegexReplacer percentWrapped = new RegexReplacer(@"(?<!\\)%[^%]+%", "lordoflocalvar", "percentWrapped", slashDollar);
RegexReplacer slashPercent = new RegexReplacer(@"\\%", string.Empty, "slashPercent", percentWrapped);
RegexReplacer singleAt = new RegexReplacer(@"(?<!\\)@", " ", "singleAt", slashPercent);
RegexReplacer slashAt = new RegexReplacer(@"\\@", "@", "slashAt", singleAt);
RegexReplacer replacer = slashAt;
string pattern = replacer.GetAggregatedPattern();
MatchEvaluator evaluator = replacer.GetReplaceDelegate();
string result = Regex.Replace(target, pattern, evaluator);

因为您希望每个替换者知道它是否受到攻击,并且因为我们通过使用组名来攻击它,所以您希望确保每个组名都是不​​同的。确保这一点的一种简单方法是使用与变量名称相同的名称,因为在同一范围内不能有两个同名的变量。

你可以看到我正在单独构建模式的每个部分,但是在构建时,我将前一个替换器作为第4个参数传递给当前的替换器。这构建了替代品链。构建之后,我使用构造的最后一个替换器来生成整体模式和评估器。如果你使用任何东西,那么你将只有整体模式的一部分。最后,它只是将生成的模式和求值程序传递给Replace方法。

请记住,此方法更多地针对所述问题。它可能适用于更一般的场景,但我只能使用您所呈现的内容。此外,由于这更像是一个解析问题,解析器可能是正确的路径 - 虽然学习曲线会更高。

另请注意,我还没有对此代码进行分析。它当然不会多次遍历目标字符串,但它确实涉及替换期间的其他方法调用。你当然希望在你的环境中测试它。