基于stringr的函数的奇怪行为

时间:2014-05-12 12:20:17

标签: regex r parsing debugging stringr

为了更改结果数据框中的值,我使用基于stringr的函数,在Hadley Wickham(https://stackoverflow.com/a/12829731/2872891)的答案中推荐。除了最后将df更改为return (df)之外,我保留了函数的完整性,我更喜欢这个。但是,我看到一些奇怪的行为,我不确定是什么原因。 replace_all的后续调用,特别是调用#3和#4,无法恢复原始数据:http:mailto:。随后是可重现的示例

数据(只有一条数据记录):

请在GitHub上看到这个要点:https://gist.github.com/abnova/1709b1e0cf8a57570bd1#file-gistfile1-r

代码(为简洁起见,我通过详细解释删除了我的评论):

DATA_SEP <- ":"

rx <- "([[:alpha:]][^.:]|[[:blank:]])::([[:alpha:]][^:]|[[:blank:]])"
results <- gsub(rx, "\\1@@\\2", results)
results <- gsub(": ", "!@#", results) # should be after the ::-gsub
results <- gsub("http://", "http//", results)
results <- gsub("mailto:", "mailto@", results)

results <- gsub("-\\r\\n", "-", results) # order is important here
results <- gsub("\\r\\n", " ", results)

results <- gsub("\\n:gpl:962356288", ":gpl:962356288", results)

results <- readLines(textConnection(unlist(results)))
numLines <- length(results)
results <- lapply(results, function(x) gsub(".$", "", x))

data <- read.table(textConnection(unlist(results)),
                   header = FALSE, fill = TRUE,
                   sep = DATA_SEP, quote = "",
                   colClasses = "character", row.names = NULL,
                   nrows = numLines, comment.char = "",
                   strip.white = TRUE)

replace_all(data, fixed("!@#"), ": ")
replace_all(data, fixed("@@"), "::")
replace_all(data, fixed("http//"), "http://")
replace_all(data, fixed("mailto@"), "mailto:")

结果 - 实际:

> data$V3
[1] "http//www.accessgrid.org/"
> data$V17
[1] "http//mailto@accessgrid-tech@lists.sourceforge.net"

结果 - 预期:

> data$V3
[1] "http://www.accessgrid.org/"
> data$V17
[1] "http://mailto:accessgrid-tech@lists.sourceforge.net"

我感谢任何帮助和/或建议。

2 个答案:

答案 0 :(得分:2)

我对此进行了测试,发现使用多次调用replace_all进行替换时出现问题。

replace_all(data, fixed("!@#"), ": ")
replace_all(data, fixed("@@"), "::")
replace_all(data, fixed("http//"), "http://")
replace_all(data, fixed("mailto@"), "mailto:")

您没有看到预期输出的原因是因为您之后没有将replace_all调用的结果分配给任何内容。它应该是......

data <- replace_all(data, fixed("!@#"), ": ")
data <- replace_all(data, fixed("@@"), "::")
data <- replace_all(data, fixed("http//"), "http://")
data <- replace_all(data, fixed("mailto@"), "mailto:")
data

在不使用stringr的情况下执行此操作的另一种方法是创建包含模式和替换的向量,并通过一次调用来遍历它们。

re  <- c('!@#', '@@', 'http//', 'mailto@')
val <- c(': ',  '::', 'http://', 'mailto:')

replace_all <- function(pattern, repl, x) {
    for (i in 1:length(pattern))
       x <- gsub(pattern[i], repl[i], x, fixed=T)
       x
}
replace_all(re, val, data)

输出

[3] "http://www.accessgrid.org/"
[17] "http://mailto:accessgrid-tech@lists.sourceforge.net"   

答案 1 :(得分:0)

在几乎完成@hwnd建议的替代gsub - )实现后,我意识到我的原始代码存在什么问题。我快速测试了固定代码并确认了我的想法。我只需要每次后续 replace_str来电,重新保存结果,由之前的每次调用返回。因此,固定代码如下所示:

# Now we can safely do post-processing, recovering original data
data <- replace_all(data, fixed("!@#"), ": ")
data <- replace_all(data, fixed("@@"), "::")
data <- replace_all(data, fixed("http//"), "http://")
data <- replace_all(data, fixed("mailto@"), "mailto:")

再次感谢@hwnd提出的宝贵建议,这有助于我弄清楚这个问题。