正则表达式在逗号上拆分,但不在任何括号内考虑递归

本文关键字:任何括 递归 拆分 正则表达式 | 更新日期: 2023-09-27 18:32:41

我有一个示例字符串:

string myString = "value,value,(value,(value, value, value, (value), value),value)";

目标是循环访问它并将其反序列化为类对象的层次结构。

大多数其他示例在这里提出类似问题不起作用的原因是由于递归,向前(或向后(查看偶数个括号将不起作用。

我已经考虑过将其存储为 JSON,但是值的对象类型会有所不同,恕不另行通知,这在过去甚至 json.net 证明也令人困惑,特别是因为这些类型可能都通过继承相关。

因此,给定示例字符串,目标是在逗号","上拆分,但忽略括号中的所有内容,直到我的递归循环深入研究该子集,然后使用相同的正则表达式拆分其内容。

我还没有代码,因为我仍在集思广益这种方法。

另请注意,子列表不一定是父列表中的最后一个元素,正如我最后示例中的几个挥之不去的value所证明的那样。

请不要在没有完全阅读问题并理解为什么它与此类问题不同的情况下标记为重复

正则表达式在逗号上拆分,但不在任何括号内考虑递归

尽管 C# 正则表达式具有允许您匹配递归括号组的功能(

有关示例,请参阅此问答(,但为正则格(即"匹配单词或整个括号组"(与拆分所需的负情况(即"匹配逗号,除非它在括号组中"(定义此类正则表达式要容易得多。

此外,在您想要递归应用相同正则表达式的情况下,构建一个简单的递归下降解析器具有优势。

解析器的核心是拆分逻辑,该逻辑在搜索逗号时计算括号,并在括号级别为零时拆分:

var parts = new List<string>();
var parenLevel = 0;
var lastPos = 0;
for (var i = 0 ; i != s.Length ; i++) {
    switch (s[i]) {
        case '(':
            parenLevel++;
            break;
        case ')':
            parenLevel--;
            if (parenLevel < 0) {
                throw new ArgumentException();
            }
            break;
        case ',':
            if (parenLevel == 0) {
                parts.Add(s.Substring(lastPos, i-lastPos));
                lastPos = i + 1;
            }
            break;
    }
}
if (lastPos != s.Length) {
    parts.Add(s.Substring(lastPos, s.Length - lastPos));
}

演示。

试试这个模式:

,(?<!'((?>(?:[^()]|(?<p>'))|(?<-p>'())*))

请注意,这仅适用于 C#/.NET.
Java/JavaScript/Python/Perl/etc 的正则表达式引擎不支持允许此模式处理嵌套括号的平衡组功能。

在这里测试一下:
http://regexstorm.net/tester?p=%2c%28%3f%3c!%5c%28%28%3f%3e%28%3f%3a%5b%5e%28%29%5d%7c%28%3f%3cp%3e%5c%29%29%7c%28%3f%3c-p%3e%5c%28%29%29*%29%29&i=value%2cvalue%2c%28value%2c+value%2c+value%2c+value%2c+%28value%29%2c+value%29%2cvalue%29

下面是该模式的说明(由 .NET 7 的正则表达式源生成器生成(:

/// <remarks>
/// Pattern explanation:<br/>
/// <code>
/// ○ Match ','.<br/>
/// ○ Zero-width negative lookbehind.<br/>
///     ○ Loop greedily and atomically any number of times right-to-left.<br/>
///         ○ Match with 3 alternative expressions.<br/>
///             ○ Match a character in the set [^()] right-to-left.<br/>
///             ○ "p" capture group.<br/>
///                 ○ Match ')' right-to-left.<br/>
///             ○ Non-capturing balancing group. Uncaptures the "p" capture group.<br/>
///                 ○ Match '(' right-to-left.<br/>
///     ○ Match '(' right-to-left.<br/>
/// </code>
/// </remarks>