正则表达式递归替换

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");几乎涵盖了所有三种情况。

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

这是否可能??


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


根据三年前的这篇文章,.NET不支持递归正则表达式:https://dev59.com/iILba4cB1Zd3GeqPdl03#25239211 显然所使用的引擎没有支持它(还没有?)http://www.rexegg.com/regex-recursion.html - Christopher
{{2}(?:(!?)(\w*)(\|{2}|&{2})?|(.?))(!?)(\w*)}{2} - Ferhat Sayan
3个回答

1
我建议在此处使用更通用的解决方案,使用较小、易于阅读和维护的正则表达式: 其中一个(最长的)将用于查找所需的子字符串(最长的一个),然后使用简单的\w+模式添加my_string.部分,另一个则在逻辑运算符周围添加空格。较小的正则表达式将在匹配评估器中使用,以操作最长正则表达式找到的值。
Regex.Replace(input, @"{{!?\w+(?:\s*(?:&&|\|\|)\s*!?\w+)*}}", m =>
    Regex.Replace(
        Regex.Replace(m.Value, @"\s*(&&|\|\|)\s*", " $1 "),
         @"\w+",
         "mystring.$&"
    )
)

请查看C#演示

主要的正则表达式匹配:

  • {{ - 匹配{{子字符串
  • !? - 可选的!符号
  • \w+ - 1个或更多个单词字符
  • (?:\s*(?:&&|\|\|)\s*!?\w+)* - 0个或多个序列:
    • \s* - 0个或多个空格字符
    • (?:&&|\|\|) - &&||子字符串
    • \s* - 0个或多个空格
    • !? - 可选的!
    • \w+ - 1个或更多个单词字符
  • }} - 匹配}}子字符串。

我采用了你的解决方案,只是在结尾加上了.Replace("{{", "").Replace("}}", "") :) - Doc
@Doc 如果您不需要保留 {{}},只需将模式包装在捕获组中并操作 Group 1 值即可,参见此更新的 C# 演示 - Wiktor Stribiżew

0

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

正则表达式演示

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

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);
    }
}

这个想法是无条件地读取每组的第一个单词,然后可能作为单独的组带有子捕获读取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());
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接