2016-11-23 59 views
14

我想用数学比较来分割表达式,例如,在数学比较中的分割字符向量R

unlist(strsplit("var<3", "(?=[=<>])", perl = TRUE)) 
unlist(strsplit("var==5", "(?=[=<>])", perl = TRUE)) 
unlist(strsplit("var>2", "(?=[=<>])", perl = TRUE)) 

的结果是:

[1] "var" "<" "3" 
[1] "var" "=" "=" "5" 
[1] "var" ">" "2" 

对于上面的第二个例子,我想获得[1] "var" "==" "5",所以这两个=应返回作为一个单一的元素。我如何需要改变我的正则表达式来实现这一点? (我已经尝试过分组和量词为“==”,但是毫无效果 - 正则表达式是不是我的朋友...)

+0

你只想限制为'''','<'和'=='吗? –

+0

@Wiktor,是的,我只想限制>,<和==的分割。也许也是!=。 – Daniel

+1

顺便说一句,你可以使用'sub(“(。*?)([= <>]。)(。*)”,“\\ 2”,“var == 55”,perl = TRUE)或类似的东西。你也可以用它来分割'strsplit(sub(“(。*?)([= <>]。)(。*)”,“\\ 1 \\ 2 \\ 3”,“var == 55” ,perl = TRUE),“”)'但是Wiktors解决方案可能更好 –

回答

9

你可以使用一个PCRE正则表达式来个匹配子串,你需要:

==|[<>]|(?:(?!==)[^<>])+ 

也支持!=,修改为

[!=]=|[<>]|(?:(?![=!]=)[^<>])+ 

regex demo

详细

  • == - 2个=迹象
  • | - 或
  • [<>] - 一个<>
  • | - 或
  • (?:(?!==)[^<>])+ - 其它1个或多个字符比<>[^<>])不开始== char序列(回火贪婪标记)。

注意:这是通过增加更多的选择和调整的磨砺贪婪令牌容易扩展。

R test

> text <- "Text1==text2<text3><More here" 
> res <- regmatches(text, gregexpr("==|[<>]|(?:(?!==)[^<>])+", text, perl=TRUE)) 
> res 
[[1]] 
[1] "Text1"  "=="  "text2"  "<"   "text3"  ">"   
[7] "<"   "More here" 
+0

我把'([a-zA-Z0-9 _] +)([^ a-zA-Z0-9 _] +)([a-zA-Z0-9 _] +)'作为正则表达式应用于操作符chars不会出现在他们的任何一方。 – Tensibai

+1

@Tensibai:你的意思是你必须检查这些操作员的双方是否有字符?您可以使用['“\\ b(?:[!=] = | [<>])\\ b”'](https://regex101.com/r/0khTUq/1) –

+1

要使regmatches返回3捕获组我认为指定它们会更好,使用perl像\ w会更好,但我觉得这样更容易理解。一世。e:'regmatches(tests,regexec(“([a-zA-Z0-9 _] +)([^ a-zA-Z0-9 _] +)([a-zA-Z0-9 _] +)) ))'在哪里测试是一个向量会给每个部分在它自己的捕获组的初始。 (这只是一个替代) – Tensibai

5

使用单词边界(\\b)并指定环视两个可能性:

unlist(strsplit("var==5", "(?=(\\b[^a-zA-Z0-9])|(\\b[a-zA-Z0-9]\\b))", perl = TRUE)) 
[1] "var" "==" "5" 

unlist(strsplit("var<3", "(?=(\\b[^a-zA-Z0-9])|(\\b[a-zA-Z0-9]\\b))", perl = TRUE)) 
[1] "var" "<" "3" 
unlist(strsplit("var>2", "(?=(\\b[^a-zA-Z0-9])|(\\b[a-zA-Z0-9]\\b))", perl = TRUE)) 
[1] "var" ">" "2" 

说明:

拆分在“字”,结束时,之后,有或者是一个非字母数字字符\\b[^a-zA-Z0-9]或它是“词”和结束时,之后,有一个字母数字字符。

编辑:

实际上上面的代码将具有意想不到的结果,如果在端部的数量为10以上。
另一种选择是使用lookbehind和分裂时,前,有要么非alphanum字符后跟一个字边缘,或一个alphanum字符后跟一个字边缘:

strsplit("var<20", "(?<=(([^a-zA-Z0-9]\\b)|([a-zA-Z0-9]\\b)))", perl = TRUE)[[1]] 
#[1] "var" "<" "20" 
strsplit("var==20", "(?<=(([^a-zA-Z0-9]\\b)|([a-zA-Z0-9]\\b)))", perl = TRUE)[[1]] 
#[1] "var" "==" "20" 
strsplit("var!=5", "(?<=(([^a-zA-Z0-9]\\b)|([a-zA-Z0-9]\\b)))", perl = TRUE)[[1]] 
#[1] "var" "!=" "5" 

EDIT2:

共窃取@Tensibai的方式来定义alphanum(+下划线)/非alphanum字符,上述regex可以简化到:"(?<=((\\W\\b)|(\\w\\b)))"

6

从我的意见想法扩大,只是格式:

tests=c("var==5","var<3","var.name>5") 
regmatches(tests,regexec("([a-zA-Z0-9_.]+)(\\W+)([a-zA-Z0-9_.]+)",tests)) 

\w[a-zA-Z0-9_]\W它的对面[^a-zA-Z0-9_],我扩大之后的评论,包括。在字符类中,因为R不支持基本正则表达式中的字符类中的\ w(需要使用perl = TRUE)。

因此,正则表达式搜索\ w和。,然后至少1不在\ w(匹配运算符),然后至少1 \ w和点。

每一步都被捕获,这给予:

[[1]] 
[1] "var==5" "var" "=="  "5"  

[[2]] 
[1] "var<3" "var" "<"  "3"  

[[3]] 
[1] "var.name>5" "var.name" ">"   "5"  

你可以捕捉每个组之间添加*如果你的项目可有周围操作空间,如果不是经营者捕获会得到他们。

+1

不错的一个(我会采取它,你不介意我用你来简化我的正则表达式);-p – Cath

+0

谢谢你,很好和短的解决方案 - 但是,这一个不使用带点的变量名称,例如'regmatches( “var.name == 5”,regexec( “(\\ W +)(\\ W +)(\\ W +)”, “var.name == 5”))'。我试过类似regmatches(“var.name == 5”,regexec(“(\\ w | [。] +)(\\ W +)(\\ w +)”,“var.name == 5”)) )',但那个人吃了字符向量的“==”部分。 – Daniel

+0

@Daniel纠正了,只是使用一个字符类将'.'添加到允许的字符 – Tensibai