从字符串中提取元素的更好策略

时间:2014-08-06 17:41:33

标签: regex r gsub strsplit

我有一个看起来像这样的字符串:

x <- "\r\n      Ticker Symbol: RBO\r\n  \t   Exchange: TSX \r\n\t   Assets ($mm) 36.26 \r\n\t   Units Outstanding: 1,800,000 \r\n\t   Mgmt. Fee** 0.25 \r\n      2013 MER* n/a \r\n\t   CUSIP: 74932K103"

我需要的是:

list(Ticker = "RBO", Assets = 36.26, Shares = 1,800,000)

我尝试过分裂,正则表达式等等。但我觉得我的字符串操作技能不符合要求。

到目前为止,这是我的“最佳”尝试。

x <- unlist(strsplit(unlist(strsplit(x, "\r\n\t") ),"\r\n"))
trim <- function (x) gsub("^\\s+|\\s+$", "", x)
x <- trim(x)
gsub("[A-Z]+$","\\2",x[2]) # bad attempt to get RBO

4 个答案:

答案 0 :(得分:1)

也许:

 sub( "\\\r\\\n.+$", "", sub( "^.+Ticker Symbol: ", "", x) )
[1] "RBO"

我想你可以用括号在一个模式中完成所有操作。和反向引用。

> sub( "^.+Ticker Symbol: ([[:alpha:]]{1,})\\\r\\\n.+$", "\\1", x)
[1] "RBO"

答案 1 :(得分:1)

更新/更好的回答:

看看cat(x)readLines(x)在这里有很多帮助

> cat(x)
#
#      Ticker Symbol: RBO
#      Exchange: TSX 
#      Assets ($mm) 36.26 #
#      Units Outstanding: 1,800,000 
#      Mgmt. Fee** 0.25 
#      2013 MER* n/a 
#      CUSIP: 74932K103
> readLines(textConnection(x))
# [1] ""                                   "      Ticker Symbol: RBO"          
# [3] "  \t   Exchange: TSX "              "\t   Assets ($mm) 36.26 "          
# [5] "\t   Units Outstanding: 1,800,000 " "\t   Mgmt. Fee** 0.25 "            
# [7] "      2013 MER* n/a "               "\t   CUSIP: 74932K103"

现在我们知道一些事情。一,我们不需要第一行,我们想要第二行。这使事情变得更容易,因为现在第一行匹配我们想要的第一行。接下来,列表名称与字符串中的名称匹配会更容易。我选择了这些。

> nm <- c("Symbol", "Assets", "Units")

现在我们必须使用grepsapply一起使用,我们将返回一个命名的匹配向量。在value = TRUE中设置grep会将字符串返回给我们。

> (y <- sapply(nm, grep, x = readLines(textConnection(x))[-1], value = TRUE))
# b                              Symbol                               Assets 
#           "      Ticker Symbol: RBO"           "\t   Assets ($mm) 36.26 " 
#                                Units 
# "\t   Units Outstanding: 1,800,000 " 

然后我们strsplit"[: ]"上,取每个分组中的最后一个元素,然后我们就完成了。

> lapply(strsplit(y, "[: ]"), tail, 1)
$Symbol
[1] "RBO"

$Assets
[1] "36.26"

$Units
[1] "1,800,000

您可以使用

获得相同的结果
> g <- gsub("[[:cntrl:]]", "", capture.output(cat(x))[-1])
> m <- mapply(grep, nm, MoreArgs = list(x = g, value = TRUE))
> lapply(strsplit(m, "[: ]"), tail, 1)

希望有所帮助。


原始答案:

看起来如果你从大桌子中拉出这些,他们都会在同一个元素中插入#34; slot&#34;每一次,也许这可能会更容易一些。

> s <- strsplit(x, "[: ]|[[:cntrl:]]")[[1]]

说明:
  - [: ]匹配":"字符,后跟空格字符
  - |
  - [[:cntrl:]]任何控制字符,在本例中为\r\t\n中的任何一个。这可能更好地解释了here

然后,nzchar在上面的结果中查找非零长度字符串,如果匹配则返回TRUE,否则返回FALSE。因此,我们可以查看第一行的结果,确定匹配的位置,以及基于此的子集。

> as.list(s[nzchar(s)][c(3, 8, 11)])
[[1]]
[1] "RBO"

[[2]]
[1] "36.26"

[[3]]
[1] "1,800,000"

您可以通过将s指定为内部调用来将is放入一行。由于函数和调用是由内到外计算的,因此在R到达外部s子集之前会分配s。但这可读性稍差。

s[nzchar(s <- strsplit(x, "[: ]|[[:cntrl:]]")[[1]])][c(3,8,11)]

所以这将是s <- strsplit(...) - &gt; [[ - &gt; nzchar - &gt; s[..&gt; - [c(3,8,11)]

答案 2 :(得分:1)

如果您只想提取字符串的不同部分,可以使用regexpr查找短语并在短语后提取内容。例如

extr<-list(
    "Ticker" = "Ticker Symbol: ",
    "Assets" = "Assets ($mm) ",
    "Shares" = "Units Outstanding: "
)

lines<-strsplit(x,"\r\n")[[1]]

Map(function(p) {
    m <- regexpr(p, lines, fixed=TRUE)
    if(length( w<- which(m!=-1))==1) {
        gsub("^\\sw+|\\s$", "",
            substr(lines[w], m[w] + attr(m,"match.length")[w], nchar(lines[w])))
    } else {
        NA
    }
}, extr)

根据需要返回命名列表

$Ticker
[1] "RBO"

$Assets
[1] "36.26"

$Shares
[1] "1,800,000"

此处extr是一个列表,其中元素的名称是将在最终列表中使用的名称,元素值是将在文本中匹配的确切字符串。我还在gsub添加了任何空格。

答案 3 :(得分:1)

stringr包适用于从字符串中抓取数据。以下是我每次使用的步骤。您可以随时根据需要制定具体或强大的规则。

require(stringr)

## take out annoying characters
x <- gsub("\r\n", "", x)
x <- gsub("\t", "", x)
x <- gsub("\\(\\$mm\\) ", "", x)

## define character index positions of interest
tickerEnd <- str_locate(x, "Ticker Symbol: ")[[1, "end"]]
assetsEnd <- str_locate(x, "Assets ")[[1, "end"]]
unitsStart <- str_locate(x, "Units Outstanding: ")[[1, "start"]]
unitsEnd <- str_locate(x, "Units Outstanding: ")[[1, "end"]]
mgmtStart <- str_locate(x, "Mgmt")[[1, "start"]]

## get substrings based on indices
tickerTxt <- substr(x, tickerEnd + 1, tickerEnd + 4) # allows 4-character symbols
assetsTxt <- substr(x, assetsEnd + 1, unitsStart - 1)
sharesTxt <- substr(x, unitsEnd + 1, mgmtStart - 1)

## cut out extraneous characters
ticker <- gsub(" ", "", tickerTxt)
assets <- gsub(" ", "", assetsTxt)
shares <- gsub(" |,", "", sharesTxt)

## add data to data frame
df <- data.frame(ticker, as.numeric(assets), as.numeric(shares), stringsAsFactors = FALSE)