在R中分割并提取字符串的一部分(在“。”和数字之间)

时间:2019-02-19 04:47:35

标签: r regex stringr

我有一个字符变量(companies),其观察结果如下:

  1. “ 612。Am。Mgt。&Inv。7.33 Grt。Am。Mgt。&Inv。7。
  2. “ 77。威克斯4.61”
  3. “ 265。Wang Labs 8.75”
  4. “ 9。CrossLand节省6.32”
  5. “ 228。JPS Textile Group 2.00”

我正在尝试将这些字符串分成3部分:

  1. 第一个"."之前的所有数字
  2. 第一个"."和下一个数字之间的所有字符 (格式一致的#.##)和
  3. 最后一个数字本身 (格式#.##)。

以第一个obs为例,我想要:“ 612”,“ Grt。Am。Mgt&Inv”,“ 5.01”

我尝试在rebus中定义模式并使用str_match,但是下面的代码仅适用于Obs#2和#3。它不能反映出字符串中间部分的所有变化来捕获其他对象。

pattern2 <- capture(one_or_more(DGT)) %R% DOT %R% SPC %R% 
            capture(or(one_or_more(WRD), one_or_more(WRD) %R% SPC 
            %R% one_or_more(WRD))) %R% SPC %R% capture(DGT %R% DOT 
            %R% one_or_more(DGT))

str_match(companies, pattern = pattern2)

是否有更好的方法将字符串分成这三个部分?

我对regex不熟悉,但是我在这里看到了很多建议(我是R和Stack Overflow的新手)

5 个答案:

答案 0 :(得分:2)

您可以使用正则表达式来分隔字符串,然后将其拆分以获取结果:

delimitedString = gsub( "^([0-9]+). (.*) ([0-9.]+)$", "\\1,\\2,\\3", companies  )

do.call( 'rbind', strsplit(split = ",", x = delimitedString) )
#      [,1]  [,2]                   [,3]  
#[1,] "612" "Grt. Am. Mgt. & Inv." "7.33"
#[2,] "77"  "Wickes"               "4.61"
#[3,] "265" "Wang Labs"            "8.75"
#[4,] "9"   "CrossLand Savings"    "6.32"
#[5,] "228" "JPS Textile Group"    "2.00" 

正则表达式说明

  • ^[0-9]+:在字符串的开头(即^)由0到9的数字组成的任何模式
  • .*:贪婪的匹配,在上述情况下基本上是两个空格所包围的任何事物
  • [0-9.]+$:再次在字符串的末尾(即$)加上数字+点

括号用于表示我想捕捉正则表达式适合的 string 部分。抓住它们后,这些子字符串将折叠起来并以逗号分隔。最后,我们可以使用strsplit函数拆分整个字符串,并使用do.call函数绑定行

答案 1 :(得分:1)

您可以使用分组正则表达式匹配信息,而不是拆分文本,并从所需的三个组中提取信息。尝试使用此正则表达式,

(.+?)\.\s+(.+)\s+(\d+\.\d+)

这将在group1,group2和group3中捕获您的信息。

Demo

在这里,组1捕获公司信息之前的第一个数字,组2捕获公司信息,组3捕获#.##格式的最后一个数字

检查此r代码,

companies = c("612. Grt. Am. Mgt. & Inv. 7.33")
result <- str_match(companies, pattern = "(.+?)\\.\\s+(.+)\\s+(\\d+\\.\\d+)")
result[,2]
result[,3]
result[,4]

打印

[1] "612"
[1] "Grt. Am. Mgt. & Inv."
[1] "7.33"

答案 2 :(得分:1)

使用以下正则表达式:

^(.*?)\.(.*?)(?=\d)(.*)$

Demo

三个捕获组包含所需的信息:第一个捕获组捕获所有内容,直到找到第一个'.';第二个捕获组捕获所有内容,直到找到一个数字(这是通过正向超前,这确保了数字不会被消耗,因为我们需要在下一组中捕获它),而第三组将捕获所有内容,直到最后。

答案 3 :(得分:1)

您可以使用3个捕获组:

([^.]+)\.\s+(\D+)\s+(\d\.\d{2})

例如

companies=c("612. Grt. Am. Mgt. & Inv. 7.33")
pattern="([^.]+)\\.\\s+(\\D+)\\s+(\\d\\.\\d{2})"
str_match(companies, pattern)

结果

     [,1]                             [,2]  [,3]                   [,4]  
[1,] "612. Grt. Am. Mgt. & Inv. 7.33" "612" "Grt. Am. Mgt. & Inv." "7.33"

查看regex101 demo | R demo

说明

  • ([^.]+)在第1组中捕获匹配1次以上而不是一个点(也要不匹配换行符,请使用[^.\r\n]
  • \.\s+匹配一个点和1+倍的空白字符
  • (\D+)在第2组中匹配超过1次而不是数字的捕获
  • \s+匹配1次以上的空白字符
  • (\d\.\d{2})在第3组中捕获一个数字,点和2个数字(格式为#。##)

答案 4 :(得分:1)

您应该能够调试所编写的正则表达式。

> as.regex(pattern2)
<regex> ([\d]+)\.\s((?:[\w]+|[\w]+\s[\w]+))\s(\d\.[\d]+)

Plug it in在regex101上,您会看到字符串并不总是匹配。右侧的说明告诉您,点和数字之间只能有1个或2个空格分隔的单词。另外,WRD[\w]+模式)不匹配点和非字母,数字或_的其他任何字符。现在,您知道您需要将字符串与

匹配
^(\d+)\.(.*?)\s*(\d\.\d{2})$

请参见this regex demo。转换为Rebus:

pattern2 <- START %R%            # ^ - start of string
 capture(one_or_more(DGT)) %R%   # (\d+) - Group 1: one or more digits
 DOT %R%                         # \. - a dot
 "(.*?)" %R%                     # (.*?) - Group 2: any 0+ chars as few as possible
 zero_or_more(SPC) %R%           # \s* - 0+ whitespaces 
 capture(DGT %R% DOT %R% repeated(DGT, 2)) %R% # (\d\.\d{2}) - Group 3: #.## number
END                              # $ - end of string

检查:

> pattern2
<regex> ^([\d]+)\.(.*?)[\s]*(\d\.[\d]{2})$

> companies <- c("612. Grt. Am. Mgt. & Inv. 7.33","77. Wickes 4.61","265. Wang Labs 8.75","9. CrossLand Savings 6.32","228. JPS Textile Group 2.00")
> str_match(companies, pattern = pattern2)
     [,1]                             [,2]  [,3]                    [,4]  
[1,] "612. Grt. Am. Mgt. & Inv. 7.33" "612" " Grt. Am. Mgt. & Inv." "7.33"
[2,] "77. Wickes 4.61"                "77"  " Wickes"               "4.61"
[3,] "265. Wang Labs 8.75"            "265" " Wang Labs"            "8.75"
[4,] "9. CrossLand Savings 6.32"      "9"   " CrossLand Savings"    "6.32"
[5,] "228. JPS Textile Group 2.00"    "228" " JPS Textile Group"    "2.00"

警告capture(lazy(zero_or_more(ANY_CHAR)))返回([.]*?)模式,该模式匹配尽可能少的0个或更多点,而不匹配任何0+个字符,因为rebus具有一个错误:它使用字符类repeatedone_or_more包装所有zero_or_more[])字符。这就是为什么“手动”添加(.*?)的原因。

可以使用[\w\W] / [\s\S][\d\D]之类的常见结构来解决或解决此问题:

pattern2 <- START %R%                          # ^ - start of string
 capture(one_or_more(DGT)) %R%                 # (\d+) - Group 1: one or more digits
 DOT %R%                                       # \. - a dot
 capture(                                      # Group 2 start:
  lazy(zero_or_more(char_class(WRD, NOT_WRD))) #  - [\w\W] - any 0+ chars as few as possible
 ) %R%                                         # End of Group 2
 zero_or_more(SPC) %R%                         # \s* - 0+ whitespaces 
 capture(DGT %R% DOT %R% repeated(DGT, 2)) %R% # (\d\.\d{2}) - Group 3: #.## number
END

检查:

> as.regex(pattern2)
<regex> ^([\d]+)\.([\w\W]*?)[\s]*(\d\.[\d]{2})$

请参见regex demo