正则表达式是自己工作的,但在strsplit中一起使用时则不然

时间:2017-01-25 21:58:23

标签: r regex pcre regex-lookarounds strsplit

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

  • 连字符出现的任何地方。
  • 无论何时出现。
  • 在令牌的第二个和第三个字符之间,正好是3个字符长,由2个数字后跟1个大写字母组成,例如"01A"生成["01", "A"](但是"012A",{ {1}},"B1A""0A1"未分割。)

例如,"01A2"应生成"WXYZ-AB-A4K7-01A-13B-J29Q-10"

我当前的正则表达式是["WXYZ", "AB", "01", "A", "13", "B", "J29Q", "10"],它在this online regex tester中完美运行。

此外,备选方案的两个部分((?<=[-.]\\d{2})(?=[A-Z][-.]))|[.-]((?<=[-.]\\d{2})(?=[A-Z][-.])),当它们单独使用时,都可以按照R中的预期分割字符串:

[.-]

但是当我尝试使用替代方法组合它们时,第二个正则表达式停止工作,并且字符串仅在句点和连字符上分开:

#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

3 个答案:

答案 0 :(得分:2)

阻止您不必考虑onCreateView算法如何工作的替代方法是使用原始正则表达式strsplit在所有正确的位置插入一个简单的拆分字符,然后使用{ {1}}做直截了当的分裂。

gsub

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

答案 1 :(得分:1)

您可以使用消费版本的正向前瞻(匹配重置运算符\K)来确保strsplit在R中正常工作,并避免使用在积极的一面内看消极的看法。

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

请参阅R demo online(以及regex demo here)。

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" 

此处,模式匹配:

  • (?<![^.-])\d{2}\K(?=[A-Z](?:[.-]|$)) - 一系列:
    • (?<![^.-])\d{2} - 2个数字(\d{2}),前面没有.-以外的字符(即前面有.-或字符串的开头,这是避免在一个环境中进行交替的常见技巧)
    • \K - 匹配重置运算符,使正则表达式引擎丢弃到目前为止匹配的文本,并继续匹配后续子模式(如果有的话)
  • | - 或
  • [.-] - 匹配.-

答案 2 :(得分:0)

感谢Rich Scriven和Jota,我能够解决问题。每次strsplit找到匹配项时,它会在查找下一个匹配项之前删除匹配项及其左侧的所有内容。这意味着当外观与前一个匹配重叠时,依赖于后视的正则表达式可能无法按预期运行。在我的情况下,令牌之间的连字符在匹配时被删除,这意味着第二个正则表达式无法使用它们来检测令牌的开头:

#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的建议,通过替换[.-]类以使用boundary anchor检测外观中令牌的边缘来解决此问题:

> 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"