正则表达式递归替换

时间:2018-01-15 14:46:15

标签: c# regex recursion

我有3例数据:

{{test_data}}
{{!test_data}}
{{test_data1&&!test_data2}} // test_data2 might not have the !

我需要翻译这些字符串:

mystring.test_data
!mystring.test_data
mystring.test_data1 && !mystring.test_data2

我正在摆弄超级有用的regex101.com,我设法用Regex.Replace(str, "{{2}(?:(!?)(\w*)(\|{2}|&{2})?)}{2}", "$1mystring.$2 $3");覆盖几乎所有3个案例

我无法弄清楚如何使用正则表达式递归重新应用(?: )部分直到}}并使用指定的替换模式将所有匹配连接在一起

这甚至可能吗?

编辑:这里是regex101页面 - > https://regex101.com/r/vIBVkQ/2

3 个答案:

答案 0 :(得分:1)

我建议在这里使用更通用的解决方案,在这里使用更小,更容易阅读和维护的regexp:一个(最长的)将用于查找所需的子串(最长的),然后是一个简单的{{ 1}}模式将用于添加\w+部分,另一个将在逻辑运算符周围添加空格。较小的正则表达式将在匹配评估器中使用,以操纵最长正则表达式找到的值:

my_string.

请参阅C# demo

主正则表达式匹配:

  • Regex.Replace(input, @"{{!?\w+(?:\s*(?:&&|\|\|)\s*!?\w+)*}}", m => Regex.Replace( Regex.Replace(m.Value, @"\s*(&&|\|\|)\s*", " $1 "), @"\w+", "mystring.$&" ) ) - {{子字符串
  • {{ - 可选的!?标志
  • ! - 一个或多个单词字符
  • \w+ - 0+序列:
    • (?:\s*(?:&&|\|\|)\s*!?\w+)* - 0+空白字符
    • \s* - (?:&&|\|\|)&&子字符串
    • || - 0+ whitespaces
    • \s* - 可选的!?
    • ! - 一个或多个单词字符
  • \w+ - }}子字符串。

答案 1 :(得分:0)

正则表达式(?:{{2}|[^|]{2}|[^&]{2})\!?(\w+)(?:}{2})?

Regex demo

C#代码

List<string> list = new List<string>() { "{{test_data}}", "{{!test_data}}", "{{test_data1&&!test_data2}}" };

foreach(string s in list)
{
    string t = Regex.Replace(s, @"(?:{{2}|[^|]{2}|[^&]{2})\!?(\w+)(?:}{2})?",
           o => o.Value.Contains("!") ? "!mystring." + o.Groups[1].Value : "mystring." + o.Groups[1].Value);

    Console.WriteLine(t);
}
Console.ReadLine();

<强>输出

mystring.test_data
!mystring.test_data
mystring.test_data1&&!mystring.test_data2

答案 2 :(得分:0)

我认为你不能使用递归,但是使用不同的输入模式表示,你可以使用子组。注意我使用命名捕获来略微限制此示例中的混淆:

var test = @"{{test_data}}
{{!test_data}}
{{test_data1&&!test_data2&&test_data3}}
{{test_data1&&!test_data2 fail test_data3}}
{{test_data1&&test_data2||!test_data3}}";

// (1:!)(2:word)(3:||&&)(4:repeat)
var matches = Regex.Matches(test, @"\{{2}(?:(?<exc>!?)(?<word>\w+))(?:(?<op>\|{2}|&{2})(?<exc2>!?)(?<word2>\w+))*}{2}");

foreach (Match match in matches)
{
    Console.WriteLine("Match: {0}", match.Value);
    Console.WriteLine("  exc: {0}", match.Groups["exc"].Value);
    Console.WriteLine(" word: {0}", match.Groups["word"].Value);
    for (int i = 0; i < match.Groups["op"].Captures.Count; i++)
    {
        Console.WriteLine("   op: {0}", match.Groups["op"].Captures[i].Value);
        Console.WriteLine(" exc2: {0}", match.Groups["exc2"].Captures[i].Value);
        Console.WriteLine("word2: {0}", match.Groups["word2"].Captures[i].Value);
    }
}

这个想法是无条件地读取每个组中的第一个单词,然后可能将(|| or &&)(optional !)(word)的N个组合作为具有子捕获的单独组读取。

示例输出:

Match: {{test_data}}
  exc:
 word: test_data
Match: {{!test_data}}
  exc: !
 word: test_data
Match: {{test_data1&&!test_data2&&test_data3}}
  exc:
 word: test_data1
   op: &&
 exc2: !
word2: test_data2
   op: &&
 exc2:
word2: test_data3
Match: {{test_data1&&test_data2||!test_data3}}
  exc:
 word: test_data1
   op: &&
 exc2:
word2: test_data2
   op: ||
 exc2: !
word2: test_data3

请注意,行{{test_data1&&!test_data2 fail test_data3}}不属于结果组,因为它不符合语法规则。

所以你可以从匹配结构中以相同的方式构建你想要的结果:

foreach (Match match in matches)
{
    var sb = new StringBuilder();
    sb.Append(match.Groups["exc"].Value).Append("mystring.").Append(match.Groups["word"].Value);

    for (int i = 0; i < match.Groups["op"].Captures.Count; i++)
    {
        sb.Append(' ').Append(match.Groups["op"].Captures[i].Value).Append(' ');
        sb.Append(match.Groups["exc2"].Value).Append("mystring.").Append(match.Groups["word2"].Value);
    }
    Console.WriteLine("Result: {0}", sb.ToString());
}