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

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

本文关键字:URL 路径部 标点符号 Lexer 模式匹配 句子 分隔符 Flex      更新时间:2023-10-16

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

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

// ...
WHITESPACE  rn|[ rntf]
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.

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

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

另一个 - 在我看来是黑客 - 解决方案是分析返回的令牌的最后一个 字符,并将字符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/. 和 http://www.domain.tld/foo/bar. 都是有效的 URL。但是 URL 识别器通常会避免匹配尾随.(如您所见,Markdown 不会匹配它),因为在句子末尾编写 URL 的常见做法,即使像这样 http://www.domain.tld/foo?(但对于 http://www.domain.tld/foo?search,Markdown 会将?识别为 URL 的一部分。

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

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

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

关于字符类的说明:在字符类中,如果将其放在开头,则可以将]用作常规字符。因此[][…]是用括号编写字符类的传统方法。-可以写成第一个或最后一个字符,所以你可以写[-…][…-]来包含破折号,但如果你也有],你需要把破折号放在最后,因为开头已经被占用了。所以你最终会得到[][…-]这就是我写上述模式的方式。除了-]\之外,字符类中没有特殊字符。因此,您可以自由包含原本是正则表达式元字符的字符,例如|。除此之外,我尝试编写类,以便很明显第二个类中缺少哪些字符。

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