c正则表达式中的多个十六进制范围无法按预期工作

时间:2017-09-22 16:25:21

标签: c regex

我正在尝试使用C来匹配以十六进制格式的以下ASCII字符范围:\x21 / \x23-\x2B / \x2D-\x39 / \x3C-\x5B / \x5D-\x7E < / p>

我发现此函数在another stack overflow answer上的C中与正则表达式匹配:

int match(const char *string, char *pattern) {
    int status;
    regex_t regex;
    int d;
    if ((d = regcomp(&regex, pattern, REG_EXTENDED | REG_NOSUB)) != 0) {
      return 0;
    }

    status = regexec(&regex, string, (size_t)0, NULL, 0);
    regfree(&regex);
    if (status != 0) {
      return 0;
    }
    return (1);
}

如果字符串与给定模式不匹配,则此函数将返回0;如果字符串不匹配,则返回1.

除了我尝试匹配多个十六进制值范围时,此函数适用于大多数模式。

例如:

int main(int argc, char const *argv[]) {
    printf("%d\n", match("HELLO!", "^[\x21 \x23-\x2B \x2D-\x39 \x3C-\x5B]+$")); // Matches as expected
    printf("%d\n", match("hello!", "^[\x21 \x23-\x2B \x2D-\x39 \x3C-\x5B]+$")); // Fails as expected because we are not including lower case letters
    printf("%d\n", match("HELLO!", "^[\x21 \x23-\x2B \x2D-\x39 \x3C-\x5B \x5D-\x7E]+$")); // Fails unexpectedly after adding 5D-7E
    printf("%d\n", match("hello!", "^[\x21 \x23-\x2B \x2D-\x39 \x3C-\x5B \x5D-\x7E]+$")); // Fails unexpectedly but should pass because we have added 5D-7E which includes lower case letters
    printf("%d\n", match("hello", "^[\x5D-\x7E]+$")); // Matches as expected because we have included lower case range
}

将输出:

1 // Expected
0 // Expected
0 // Unexpected 
0 // Unexpected
1 // Expected

似乎将5D-7E范围添加到任何其他范围会打破正则表达式。

我错过了什么或者这看起来很奇怪吗?

可以看到一个供参考的ASCII表here

1 个答案:

答案 0 :(得分:2)

\x5D是一个小括号(] )。也就是说,它确实是一个紧密的支柱;在程序文本被解析之前完成替换,就像所有其余的反斜杠转义一样。所以你的字符串

"^[\x21 \x23-\x2B \x2D-\x39 \x3C-\x5B \x5D-\x7E]+$"

实际上是字符串

"^[! #-+ --9 <-[ ]-~]+$"

这就是regcomp编译的内容。 [注1]

该模式有三个问题,其中一个应该是显而易见的:字符类由第一个] 终止,所以匹配的是匹配字符类的单个字符[! #-+ --9 <-[ ],后面依次为 - ,然后是一个或多个] 字符。显然,HELLO!与此模式不匹配。

其次,字符类中的空格字符不是特殊的,这意味着它不会被忽略(我相信你期望)。因此,字符类中读取--9的部分实际上意味着“空格(0x20)和短划线(0x2D)之间的任何字符或字符 9 ”。

与此相关,即使您删除了空格(您肯定需要这样做),--9也不合法,因为作为文字字符的 - 只能出现在字符类的开头或结尾。 [注2]

最后,角色类[\x5D-\x7E] 你做了什么,因为如果它是第一个字符,那么允许] 作为文字字符显示字符类,在这种情况下是([]-~]

这些规则记录在regex manpage中,并在Posix Base Definitions Section 9中详细说明。

注意:

  1. 您可以通过反斜杠转义("\\x5D")来提供反斜杠作为正则表达式字符串的一部分,但这不适用于Posix正则表达式,因为Posix规范不包含十六进制转义序列,也不是标准的C转义符,例如\n。如联机帮助页中所述,Gnu C库正则表达式实现将\x理解为文字x并将\n理解为文字n(与反斜杠后跟任何字母相同)字符)。但是,Posix没有定义这些用法;在Posix扩展正则表达式中,反斜杠只能用于转义特殊字符,并且只能用于字符类之外。

  2. 虽然标准未定义用法,但正则表达式库可能会将-识别为文字字符,因为它会立即显示在字符范围之后。但是,依赖这样的扩展是不好的做法,即使它们碰巧在特定的Posix库实现上工作。