为什么stringr/ICU和Perl中无法将行尾符(\\b)识别为单词边界?

6

我使用 stringr 尝试检测字符串末尾的 符号,代码如下:

str_detect("my text €", "€\\b") # FALSE

为什么这个不起作用?在以下情况下它是有效的:
str_detect("my text a", "a\\b") # TRUE - letter instead of €
grepl("€\\b", "2009in €") # TRUE - base R solution

但在 Perl 模式下它也会失败:

perl 是一种编程语言。

grepl("€\\b", "2009in €", perl=TRUE) # FALSE

那么€\\b正则表达式有什么问题吗?€$正则表达式在所有情况下都有效...

2个回答

4

当您使用不带perl=TRUE的基本R正则表达式函数时,会使用TRE regex flavor

似乎TRE单词边界:

  • 在非单词字符后使用时匹配字符串结束位置,
  • 在非单词字符之前使用时匹配字符串开始位置。

请参阅R测试:

> gsub("\\b\\)", "HERE", ") 2009in )")
[1] "HERE 2009in )"
> gsub("\\)\\b", "HERE", ") 2009in )")
[1] ") 2009in HERE"
> 

这不是PCRE和ICU正则表达式中单词边界的常见行为,其中在非单词字符之前的单词边界仅在该字符之前有一个单词字符时匹配,不包括字符串起始位置(并且在非单词字符之后使用时需要单词边界右侧紧跟单词字符):

有三个不同的位置可以作为单词边界:

- 如果第一个字符是单词字符,则字符串中第一个字符之前。
- 如果最后一个字符是单词字符,则字符串中最后一个字符之后。
- 在字符串中的两个字符之间,其中一个是单词字符,另一个不是单词字符。


3
\b

等同于

(?:(?<!\w)(?=\w)|(?<=\w)(?!\w))

也就是说,它匹配以下三种情况:

  • 单词字符与非单词字符之间的位置
  • 单词字符与字符串开头之间的位置
  • 单词字符与字符串结尾之间的位置

是一个符号,符号不属于单词字符。

$ uniprops €
U+20AC <€> \N{EURO SIGN}
    \pS \p{Sc}
    All Any Assigned Common Zyyy Currency_Symbol Sc Currency_Symbols S Gr_Base Grapheme_Base Graph X_POSIX_Graph GrBase Print X_POSIX_Print Symbol Unicode

如果您的语言支持前后查找,您可以使用以下内容来查找空格和非空格之间的边界(将开头和结尾视为空格)。
(?:(?<!\S)(?=\S)|(?<=\S)(?!\S))

1
"(?:(?<!\s)(?=\s)|(?<=\s)(?!\s))" 在TRE正则表达式中不受支持,因为它不支持环视。至于匹配字符串末尾的a € sign,可以使用"$"。 - Wiktor Stribiżew
@Wiktor Stribiżew,谢谢。我不想完全删除这个模式,因为它可能对其他人有用,但我重新表述了这个语句,以考虑到它对OP没有用的事实。 - ikegami
是的,它将与 ICU(stringr 函数)和 perl=TRUE 的“强化”基本 R 函数一起工作。 - Wiktor Stribiżew
啊,我看到它使用了不同的引擎进行匹配,而我没有解释。话虽如此,我提供了另一个答案中没有的两个重要信息。 - ikegami

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接