Flex Lexer 模式匹配句子分隔符/标点符号作为 URL 路径部分

Flex Lexer pattern matching sentence separator / punctuation as URL path part

我即将使用 RE-Flex(与 flex 兼容的词法分析器)重构文本片段的空白分词器

我的词法分析器文件中有以下模式,我只列出与此问题有关的模式:

// ...

WHITESPACE  \r\n|[ \r\n\t\f]
DOMAIN      "mil"|"info"|"gov"|"edu"|"biz"|"com"|"org"|"net"|"arpa"|"de"|[a-z]{2}
DIGIT       [0-9]
LETTER      [a-zA-Z]
SYMBOL      ({LETTER}|{DIGIT})({LETTER}|{DIGIT}|"_"|"-")*
BARE_URL    {SYMBOL}("."{SYMBOL})*"."{DOMAIN}
URL_PATH    ([!*'();:@&=+$,/?%#_.~]|"-"|"["|"]"|{LETTER}|{DIGIT})+

%%

("." | "?" | "!" | ";")+ { 
     return tokenizer_base::TK_PUNCTUATION; 
}

/* ... other patterns ... */

{BARE_URL} { 
    return tokenizer_base::TK_BARE_URL;
}
(("http"|"https"|"ftp")"://")?{BARE_URL}{URL_PATH}? { 
    return tokenizer_base::TK_FULL_URL;
}    

/* ... */

/** Ignore the rest */
.|{WHITESPACE} { 
    ;
}

%%

这基本上工作正常,但考虑这种输入情况:

Please visit http://www.google.de.

上面字符串中的最后一个.是一个句子分隔符,应该return作为TK_PUNCTUATION标记类型。不幸的是它没有,它被解释为 TK_FULL_URL 令牌和 returns 的一部分作为 http://www.google.de..

考虑到正常的正则表达式,我尝试将 [^!;.] 附加到 TK_FULL_URL 模式,但这不起作用。

另一个——在我看来有点老套——解决方案是分析 returned 令牌的最后一个 character 和 unput 如果匹配标点符号,则字符返回输入流。我可以这样做:

size_t last = YY_SCANNER.ptr_matcher()->size() - 1; // similar to YYleng
std::string last_str = YY_SCANNER.ptr_matcher()->text(); // similar to YYtext

try {
    // Check if last character is a '.' and second-last char of type alpha
    if (last_str.at(last) == '.' && ::isalpha(last_str.at(last - 1))) {
        YY_SCANNER.ptr_matcher()->unput(last_str[last]); 
        YY_SCANNER.ptr_matcher()->less(last); // similar to YYless
    }
} catch(const std::out_of_range& e) {
    // we keep silent 
}

到目前为止这是可行的,但我认为这不是很优雅且容易出错。

所以我的基本问题是我是否可以以某种方式调整 urlpath 模式,以便最后一个 . 不被视为 URL 路径的一部分?我知道 http://www.domain.tld/foo/bar/. 有效,但 http://www.domain.tld/foo/bar. 无效。

也许有一个简单的解决办法。欢迎提出任何建议。感谢您的努力!

绝对清楚你想接受什么是非常重要的。否则,你无法编写一个正则表达式来接受它,也没有人试图帮助你。

请注意:以下段落中的(破损)URL是故意打成这样的,以便Markdown的识别算法显而易见。

两者都是 http://www.domain.tld/foo/bar/. and http://www.domain.tld/foo/bar. are valid URLs. But it's common for URL recognizers to avoid matching the trailing . (as you can see, Markdown won't match it) because of the common practice of writing a URL at the end of a sentence, even like this http://www.domain.tld/foo? (But with http://www.domain.tld/foo?search,Markdown 将 ? 识别为 URL 的一部分。)

括号和引号也很棘手。 Markdown 继续 运行 示例,如果它们是平衡的 (http://foo.es/?q=(main())),将在 URL 中接受括号,但如您所见,仍然可以将 URL 括号内。这种行为不可能用正则表达式模拟,因为正则表达式不能计数。

但让我们保持简单。我们可以只接受 URL,但如果最后一个字符在标点符号列表中,则将其排除。所以这可能会以这样的方式结束:

URL_CHAR  [][a-zA-Z0-9*@&=+$/?%#_~|()'"!:;.,-]
URL_FINAL [][a-zA-Z0-9*@&=+$/?%#_~|-]
URL_PATH  {URL_CHAR}*{URL_FINAL}

关于字符 classes 的注释:在字符 class 中,如果将 ] 放在开始。所以 [][…] 是字符 classes 带括号的常规写法。 - 可以写成第一个或最后一个字符,所以你可以写 [-…][…-] 来包含破折号,但是如果你还有一个 ],你需要把破折号放在最后,因为开头已经被占用了。所以你最终得到 [][…-] 这就是我编写上述模式的方式。除了 -]\ 之外,字符 [=52= 中没有特殊字符].因此,您可以自由地包含本来是正则表达式元字符的字符,例如 |。除此之外,我尝试编写 classes 以便很明显第二个 class.

中缺少哪些字符

如果你想将 http://www.domain.tld/foo/. 识别为 URL(而不是更可能的 http://www.domain.tld/foo/ 后跟标点符号),你需要一些更复杂的东西,因为您必须对斜杠进行特殊处理。这可以做到,但是,正如我在开头所说的,重要的是确切地知道你想要匹配什么。