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/ 后跟标点符号),你需要一些更复杂的东西,因为您必须对斜杠进行特殊处理。这可以做到,但是,正如我在开头所说的,重要的是确切地知道你想要匹配什么。
我即将使用 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/ 后跟标点符号),你需要一些更复杂的东西,因为您必须对斜杠进行特殊处理。这可以做到,但是,正如我在开头所说的,重要的是确切地知道你想要匹配什么。