在标点之间提取字符串(如果存在)

时间:2019-04-13 14:12:59

标签: r regex

我正在尝试提取:;之后,如果存在第二个标点,则在;之前提取字符串,然后删除;之后的所有内容如果存在。目标结果是一个数字。

当前代码可以在:;之间执行,也可以在:之后执行,但是不能单独处理;或单独处理:

此外,gsub(|(OF 100); SEE NOTE)无法正常工作,我不确定为什么不排除最初的:并完全需要gsub的原因。

test<-c("Score (ABC): 2 (of 100); see note","Amount of ABC; 30%","Presence of ABC: negative","ABC not tested") 
#works for :/;
toupper((regmatches(toupper(test), gregexpr(":\\s* \\K.*?(?=;)", toupper(test), perl=TRUE))))
#works for :
test<-toupper((regmatches(toupper(test), gregexpr(":\\s* (.*)", toupper(test), perl=TRUE))))
#removes extra characters:
test<-gsub(": |(OF 100); SEE NOTE|%|; ","",test)
#Negative to numeric:
test[grepl("NEGATIVE|<1",test)]<-0
test

预期结果:2 30 0

3 个答案:

答案 0 :(得分:2)

以下是一些解决方案。

前两个是基数。第一个仅使用非常简单的正则表达式。第二个更短,正则表达式稍微复杂一点。在这两种情况下,如果没有匹配项,我们都会返回NA,但是如果对您很重要,那么之后您可以将NA替换为0(使用ifelse(is.na(x), 0, x),其中x是NA的答案)。

第三个与第二个几乎相同,但是在gsubfn中使用了捆扎。它返回0而不是NA。

1)read.table 用分号替换所有冒号,并以分号分隔的字段形式读取。选择第二个此类字段,并删除第一个非数字及其后面的所有内容。然后将剩下的转换为数字。

DF <- read.table(text = gsub(":", ";", test), 
  as.is = TRUE, fill = TRUE, sep = ";", strip.white = TRUE)
as.numeric(sub("\\D.*", "", DF$V2))
##[1]  2 30 NA

2)捕获从开头的非冒号或分号字符开始匹配,然后匹配冒号或分号,然后匹配空格,最后捕获数字。返回捕获的数字转换为数字。

strcapture("^[^:;]+[;:] (\\d+)", test, list(num = numeric(0)))$num
##[1]  2 30 NA

3)紧紧使用与(2)中相同的模式将匹配项转换为数字,如果匹配项为空,则返回0。

library(gsubfn)
strapply(test, "^[^:;]+[;:] (\\d+)", as.numeric, simplify = TRUE, empty = 0)
## [1]  2 30  0

答案 1 :(得分:0)

另一种方法:

out <- gsub('(^.+?[;:][^0-9]+)(\\d+)(.*$)|^.+', '\\2', test)
out[out == ''] <- 0
as.numeric(out)

## [1] 2  30  0

答案 2 :(得分:0)

按照OP的描述(斜体是我的):

  

在:或;之后提取字符串。并且在之前;如果存在第二个标点符号,请然后删除;之后的所有内容。 (如果存在)。目标结果是一个数字。

我认为其他一些建议可能会忽略该斜体标准。因此,这是OP的测试集,最后带有一个额外条件来测试该测试集:

test<-c( "Score (ABC): 2 (of 100); see note",
         "Amount of ABC; 30%",
         "Presence of ABC: negative",
         "...and before a ; if the second punctuation is present, then remove everything after a ; if present [so 666 should not be returned]") 

单线返回请求的结果:

sub( pattern='.+?[:;]\\D*?[^;](\\d*).*?;*.*', 
     replacement='\\1', 
     x=test, perl=TRUE)

与OP的请求匹配的结果:

[1] "2"  "30" ""   "" 

如果OP确实想要一个没有找到匹配项的零整数,则设置sub()替换= '0\\1'并用as.integer()包装,如下所示:

as.integer( gsub( pattern='.+?[:;]\\D*?[^;](\\d*).*?;*.*', 
                  replacement='0\\1', 
                   x=test, perl=TRUE) )

结果:

[1]  2 30  0  0

完全在线R(R 3.3.2)示例:

https://ideone.com/TTuKzG

正则表达式说明

OP只希望在字符串中找到一个匹配项,因此sub()函数可以正常工作。

使用sub()的技术是制作一个匹配所有字符串的模式,但如果满足条件,则使用中间的捕获组捕获零个或多个数字。

模式.+?[:;]\\D*?[^;](\\d*).*?;*.*的读取方式如下

  • .+?在一个和无限次+之间匹配任意字符(行终止符除外)?越少越好,并根据需要扩展(惰性)
  • [:;]在列表中的方括号之间匹配单个字符,在这种情况下为:;
  • \\D匹配任何非数字字符(等于[^ 0-9])
  • *?量词*尽可能在零和无限次数?之间进行匹配,并根据需要扩展(延迟)
  • [^;] ^帽子作为方括号之间的第一个字符表示:匹配方括号之间的列表中不存在的单个字符,在这种情况下,匹配不包含任何字符的; < / li>
  • (\d*)尖括号之间的所有内容都是一个捕获组-这是第一个捕获组:\\d*匹配零到无限次之间的一个数字(等于[0-9]),次数不计其数尽可能(贪婪)
  • ;*匹配;字符* 零次至无限次 [so; 不一定要存在是否存在:这是在第二个定界符之后按OP请求排除任何内容的关键]]
  • .*在零个和无限次之间匹配任意一个字符*,并尽可能多地匹配(贪婪)[因此将所有内容提取到行尾]

替换= \\1指的是我们模式中的第一个捕获组。我们用捕获组中找到的替换所有与模式匹配的内容。 \\d*不能匹配任何数字,因此如果在我们期望的位置找不到数字,则会返回一个空字符串。