当前位置: 首页 > 知识库问答 >
问题:

正则表达式可以独立工作,但在str分支中一起使用时则不行

连厉刚
2023-03-14

我正在尝试使用str分支和perl正则表达式在R中拆分字符串。该字符串由用句点或连字符分隔的各种字母数字标记组成,例如"WXYZ-AB-A4K7-01A-13B-J29Q-10"。我想拆分字符串:

  • 只要出现连字符
  • 无论何时出现句点
  • 令牌的第二个字符和第三个字符之间的长度正好为3个字符,由2个数字和1个大写字母组成,例如,产生[“01”,“a”](但不拆分“012A”“B1A”“0A1”、和“01A2”

例如,"WXYZ-AB-A4K7-01A-13B-J29Q-10"应该产生["WXYZ","AB","01","A","13","B","J29Q","10"]

我当前的正则表达式是((?

此外,备选方案的两部分,<代码>(?

#correctly splits on periods and hyphens
strsplit("WXYZ-AB-A4K7-01A-13B-J29Q-10", "[.-]", perl=T)
[[1]]
[1] "WXYZ" "AB"   "A4K7" "01A"  "13B"  "J29Q" "10"

#correctly splits tokens where a letter follows two digits
strsplit("WXYZ-AB-A4K7-01A-13B-J29Q-10", "((?<=[-.]\\d{2})(?=[A-Z][-.]))", perl=T)
[[1]]
[1] "WXYZ-AB-A4K7-01" "A-13"            "B-J29Q-10"

但当我尝试使用另一种方法组合它们时,第二个正则表达式停止工作,字符串仅在句点和连字符上拆分:

#only second alternative is used
strsplit("WXYZ-AB-A4K7-01A-13B-J29Q-10", "((?<=[-.]\\d{2})(?=[A-Z][-.]))|[.-]", perl=T)
[[1]]
[1] "WXYZ" "AB"   "A4K7" "01A"  "13B"  "J29Q" "10"

为什么会这样?这是我的正则表达式的问题还是strsplit的问题?我怎样才能达到预期的行为?

## [[1]]
## [1] "WXYZ" "AB"   "A4K7" "01"   "A"    "13"   "B"    "J29Q" "10"

共有3个答案

蒋茂材
2023-03-14

多亏了Rich Scriven和Jota,我才解决了这个问题。每次strsplit找到匹配项时,它都会删除匹配项及其左侧的所有内容,然后再查找下一个匹配项。这意味着,当lookbehind与之前的匹配重叠时,依赖lookbehind的正则表达式可能无法按预期工作。在我的例子中,令牌之间的连字符在匹配时被删除,这意味着第二个正则表达式无法使用它们来检测令牌的开头:

#first match found
"WXYZ-AB-A4K7-01A-13B-J29Q-10"
     ^

#match + left removed
"AB-A4K7-01A-13B-J29Q-10"

#further matches found and removed
"01A-13B-J29Q-10"

#second regex fails to match because of missing hyphen in lookbehind:
#((?<=[-.]\\d{2})(?=[A-Z][-.]))
# ^^^^^^^^
"01A-13B-J29Q-10"

#algorithm continues
"13B-J29Q-10"

根据Jota的建议,这是通过替换[.-]类来用边界锚点检测Lookback中令牌的边缘来修复的:

> strsplit("WXYZ-AB-A4K7-01A-13B-J29Q-10", "[-.]|(?<=\\b\\d{2})(?=[A-Z]\\b)", perl=T)
[[1]]
[1] "WXYZ" "AB"   "A4K7" "01"   "A"    "13"   "B"    "J29Q" "10"
陶柏
2023-03-14

您可以使用正向前瞻的消费版本(匹配重置运算符),以确保strsplit在R中正确工作,并避免在正向前瞻中使用反向前瞻的问题。

"(?<![^.-])\\d{2}\\K(?=[A-Z](?:[.-]|$))|[.-]"

在线查看R演示(以及此处的正则表达式演示)。

strsplit("XYZ-02-01C-33D-2285", "(?<![^.-])\\d{2}\\K(?=[A-Z](?:[.-]|$))|[.-]", perl=TRUE)
## => [[1]]
##    [1] "XYZ"  "02"   "01"   "C"    "33"   "D"    "2285"

strsplit("WXYZ-AB-A4K7-01A-13B-J29Q-10", "(?<![^.-])\\d{2}\\K(?=[A-Z](?:[.-]|$))|[.-]", perl=TRUE)
## => [[1]]
##    [1] "WXYZ" "AB"   "A4K7" "01"   "A"    "13"   "B"    "J29Q" "10" 

在这里,模式匹配:

梁明辉
2023-03-14

另一种避免您考虑strsplit算法如何工作的方法是,将原始正则表达式与gsub一起使用,在所有正确的位置插入一个简单的拆分字符,然后使用strsplit进行直接拆分。

strsplit(
    gsub("((?<=[-.]\\d{2})(?=[A-Z][-.]))|[.-]", "-", x, perl = TRUE),
    "-", 
    fixed = TRUE)
#[[1]]
#[1] "XYZ"  "02"   "01"   "C"    "33"   "D"    "2285"

当然,RichScriven的回答和Wiktor Stribiżew的评论可能更好,因为它们只有一个函数调用。

 类似资料:
  • 我要匹配以下字符串: 包含unicode空格(不要问我为什么)<代码>/,\s*,/u在regex101中工作正常。 但是(?u),\s*,“在clojure中不起作用: 为什么会失败?

  • 我有一个包含特殊字符的字符串列表 它像这样工作得很好 输出 安得拉邦 卡纳塔克邦 当我使用过滤器来做这件事时 它不起作用!我把名单原封不动地拿回来了。 我期待着这样一份清单['Andhra Pradesh','Karnataka'] 我希望从字符串列表中删除特殊字符,如,和

  • 密码不能匹配或包含姓氏。 密码必须至少包含1个特殊字符。 密码必须至少包含1个数字字符。 密码必须至少包含2个字母字符。 密码必须至少包含1个大写字母。 密码不能匹配或包含用户ID。 密码不能匹配或包含名字。 密码不能包含以下字符:! 密码不得超过25个字符。 密码长度必须至少为8个字符。 密码必须至少包含1个小写字母。 这些是

  • 问题内容: 我有一个正则表达式: 这应该与该字符串匹配并返回三个捕获(根据Rubular) 这是我的代码: 当有三个时,此打印输出1(组),所以我只能这样做,只会返回32。 问题答案: 调用查找匹配的 下一个 实例,如果没有更多实例,则返回false。尝试调用它三次,看看是否有所有预期的组。 为了澄清,正在尝试 在正则表达式中 找到第一个组 表达式 。您的正则表达式中只有一个这样的组表达式,因此永

  • 正则表达式非常简单: 这在Neo4j服务器web控制台中有效。我收到了预期的结果。 当我通过Java使用REST接口时,我必须将regex更改为:(添加了反斜杠)。我不明白为什么,但它确实有效(再次返回了预期结果)。 相同的正则表达式不适用于嵌入式Neo4j: 请注意 不会显示在错误日志中(至少在 Intellij 控制台上)。 更糟糕的是< code > "(?我)。* \ \ baaaaaaa

  • 问题内容: 我正在尝试使用以下程序使用正则表达式删除字符串中的某些单词。它可以正确删除,但只考虑大小写。如何使其不区分大小写。我坚持使用方法,但是没有用。 输出: 问题答案: 您需要将模式中要区分大小写的部分放在 前面 : 看见 我已将要删除的关键字周围的空格替换为单词边界()。之所以出现问题,是因为可能有两个关键字一个接一个地被一个空格隔开。 如果仅当关键字被 空格 包围时才想删除它们,则可以使