Oracle和Mysql中的regexp_substr

wwtsj6pe  于 2023-10-16  发布在  Oracle
关注(0)|答案(1)|浏览(144)

我在MySQL和Oracle中执行了相同的语句,但它们返回了不同的结果。正则表达式“/?“的意思是返回零个或多个“/",但为什么MySQL会返回一个“products”,是MySQL实现错误吗?

SELECT
  REGEXP_SUBSTR('http://www.example.com/products',
                'http://([[:alnum:]]+\.?){3,4}/?') "REGEXP_SUBSTR"
  FROM DUAL;

Oracle Output:
REGEXP_SUBSTR
http://www.example.com/

MySQL Output:
REGEXP_SUBSTR
http://www.example.com/products

我查询了Oracle和MySQL的文档,他们感兴趣的是“?“的定义是匹配零个或一个字符。MySQL说他们实现了对ICU库的常规支持。但ICU的输出是“http://www.example.com/“。

int main(int argc, char *argv[]) {
    uint32_t flags = 0;
    flags |= UREGEX_UNIX_LINES;
    flags |= UREGEX_CASE_INSENSITIVE;
    const char *c_source_char = "http://www.example.com/products";
    const char *c_pat_char = "http://([[:alnum:]]+\\.?){3}/?";;
    UErrorCode status = U_ZERO_ERROR;
    URegularExpression *regexp = uregex_openC(c_pat_char, 0, NULL, &status);
    UChar ustr[100];
    u_uastrcpy(ustr, c_source_char);
    uregex_setText(regexp, ustr, -1, &status);
    if (uregex_find(regexp, 0, &status)) {
        int32_t start = uregex_start(regexp, 0, &status);
        int32_t end = uregex_end(regexp, 0, &status);
        if(end - start > 0) {
            std::string res(c_source_char+start, c_source_char+end);
            std::cout << res << std::endl;
        }
        
        printf("Found match at %d-%d\n", start, end);
    }
    uregex_close(regexp);
    return 0;
}
83qze16e

83qze16e1#

问题不在于/?,而在于前面的\.?
在MySQL中,正则表达式语法文档指出:
若要在正则表达式中使用特殊字符的文本示例,请在其前面加上两个反斜杠(\)字符。MySQL解析器解释其中一个反斜杠,正则表达式库解释另一个反斜杠。例如,要匹配包含特殊+字符的字符串1+2,只有以下正则表达式中的最后一个才是正确的:

mysql> SELECT REGEXP_LIKE('1+2', '1+2');                       -> 0
mysql> SELECT REGEXP_LIKE('1+2', '1\+2');                      -> 0
mysql> SELECT REGEXP_LIKE('1+2', '1\\+2');                     -> 1

所以:

SELECT REGEXP_SUBSTR(
         'http://www.example.com/products',
         'http://([[:alnum:]]+\.?){3,4}/?'
       ) AS single_slash,
       REGEXP_SUBSTR(
         'http://www.example.com/products',
         'http://([[:alnum:]]+\\.?){3,4}/?'
       ) AS double_slash,
       REGEXP_SUBSTR(
         'http://www.example.com/products',
         'http://([[:alnum:]]+[.]?){3,4}/?'
       ) AS character_class
FROM   DUAL;

输出:
| 单斜线|双斜线|字符类|
| --|--|--|
| http://www.example.com/products | http://www.example.com/ | http://www.example.com/ |
在第一个模式中,\..相同,并且匹配任何字符,因此[[:alnum:]]+\.?匹配www.,然后example.,然后com/,然后products第4次出现。要只匹配.字符,您需要\\.[.]
MySQL fiddle
在Oracle中,您希望使用\.[.]而不是\\.来匹配.字符。(因此,如果您想在两种SQL方言中使用相同的表达式,请使用http://([[:alnum:]]+[.]?){3,4}/?
Oracle fiddle

相关问题