用std :: regex分割路径

时间:2018-01-09 11:40:58

标签: regex c++11

我试图将路径拆分为元素。这是电路描述分层路径。元素由点分隔。有3种可能的元素类型(完整的细节远比这复杂,但这些细节并不重要,我可以假设路径形成良好)。

  1. 普通标识符
  2. 通常的东西,字母,数字,美元,下划线。

    1. 扩展标识符
    2. 这些是由反斜杠分隔的。它们可以包括几乎任何东西,包括空格和圆点。它们还可以包括反斜杠,必须加倍。

      1. 转义标识符
      2. 这些由起始反斜杠和结尾空格分隔(准确地说空格,制表符或换行符,但实际上空间几乎是唯一使用过的空格)。它们可以包括反斜杠和点,但没有空格。

        压倒性的主要路径仅由类型1的元素组成。病态扩展的扩展/转义标识符(包含另一个终止分隔符后跟点)也不太可能在野外发生。

        我遇到的问题是1.和2.可以包含彼此终止分隔符。

        我到目前为止提出的正则表达式是

        std::string re = "^("\
                         "[^ \\\\][^.]*"\
                         "|\\\\(?:\\\\\\\\|[^\\\\])+\\\\(?=\\.|$)"\
                         "|\\\\[^ ]+ (?=\\.|$)"\
                         ")\\.?";
        

        [我不确定带有添加括号的原始字符串是否会更清晰]

        第一行和最后一行包含捕获括号和可选的点分隔符

        "[^ \\\\][^.]*"
        

        这是普通的标识符。以非空格/非反斜杠开始,后跟零个或多个非点。

        "|\\\\(?:\\\\\\\\|[^\\\\])+\\\\(?=\\.|$)"
        

        用于扩展标识符。反斜杠后跟一个或多个双反斜杠或非反斜杠,以反斜杠后跟一个点或字符串结尾终止。

        "|\\\\[^ ]+ (?=\\.|$)"
        

        最后是转义的标识符。反斜杠后跟一个或多个非空格。以空格后跟点或字符串结尾终止。

        这似乎适用于大多数示例,除了看起来像这样的情况

        R"(a.\b\. .c)"
        

        这应该捕获

        'a' '\b\. ' 'c'
        

        但是因为转义的标识符替代在扩展标识符之前,所以捕获是

        '一个' ' \ B \'与无与伦比的' .C'遗留下来

        交换转义和扩展标识符替代的顺序不是解决方案。这只会导致像

        这样的路径失败
        R"(a.\b .\.c)"
        

        目前我的解决方案是在每次匹配后检查后缀是否匹配。在伪代码中

        // re is the regex as above, re2 is the same with extended/escaped swapped
        while (std::regex_search(test_str, match, re))
        {
            get suffix
            if (suffix empty or suffix matches)
            {
                process capture
                set test_str to suffix
            }
            else
            {
                if (std::regex_search(test_str, match, re2))
                {
                    get suffix
                    if (suffix empty or suffix matches)
                    {
                        process capture
                        set test_str to suffix
                    }
                }
                else
                {
                    we have a problem
                }
            }
        }
        

        那么,除了如上所述在匹配级别进行回溯之外,是否有一种合理的方式使用前瞻来匹配每个元素只需一次尝试?

        编辑: 我咬了一下前瞻的子弹。简而言之,我将整个正则表达式的额外副本(没有前瞻性)添加到转义/扩展的替代方案中。我对普通的标识符也有点过于懒惰和松懈。我不想匹配空格或反斜杠。这给了我

        std::string re = "^("\
                         // 1st alternative ordinary identifier
                         // starts with underscore or letter followed by zero or more brackets, underscores, letters, digits, dollars
                         "[_a-zA-Z][[\\]_a-zA-Z0-9$]*"\
                         // 2nd alternative extended identifier
                         // starts with a backslash followed by one or more double backslashes or non-backslashes and ends with a backslash
                         "|\\\\(?:\\\\\\\\|[^\\\\])+\\\\"\
                         // 3rd alternative escaped identifier
                         // starts with a backslash followed by one or more non-spaces and ends with a space
                         "|\\\\[^ ]+ )"\
                         // followed by a positive lookahead for either end of string or dot followed by the alternation above
                         "(?=$|\\.(?:[_a-zA-Z][[\\]_a-zA-Z0-9$]*|\\\\(?:\\\\\\\\|[^\\\\])+\\\\|\\\\[^ ]+ )"\
                         // end of capture
                         ")"\
                         // if there is a dot, consume it
                         "\\.?";
        

        我一直在使用regular expressions 101来分析和调试此RE。它只支持PCRE / javascript / python / golang,但使用公共子集相当容易。我发现这个解释很有用,可以作为RE的分层细分。然后,我可以检查组件是否符合我的预期,并捕获了一些错误,例如使用[]个字符列表。我还发现正则表达式调试器非常有用(仅限PCRE模式)。特别是你可以看到它首先没有匹配的点,或者它匹配,然后回溯到你预期的匹配之后。

0 个答案:

没有答案