请将read.table
的正文视为使用以下代码创建的文本文件:
sink("readTable.txt")
body(read.table)
sink()
使用正则表达式,我想在foo(a, b, c)
中找到"readTable.txt"
形式的所有函数调用(但有任意数量的参数)。也就是说,我希望结果包含read.table
主体中所有被调用函数的名称。这包括形式
的嵌套函数
foo(a, bar(b, c))
。可以包含保留字(return
,for
等)和使用反向标记('=='()
,'+'()
等)的函数,因为我可以在以后删除它们。
所以一般来说,我正在寻找模式text(
或text (
然后可能的嵌套函数,如text1(text2(
,但跳过文本如果它是一个参数,而不是一个函数。这是我到目前为止的地方。它很接近,但并不完全存在。
x <- readLines("readTable.txt")
regx <- "^(([[:print:]]*)\\(+.*\\))"
mat <- regexpr(regx, x)
lines <- regmatches(x, mat)
fns <- gsub(".*( |(=|(<-)))", "", lines)
head(fns, 10)
# [1] "default.stringsAsFactors()" "!missing(text))"
# [3] "\"UTF-8\")" "on.exit(close(file))" "(is.character(file))"
# [6] "(nzchar(fileEncoding))" "fileEncoding)" "\"rt\")"
# [9] "on.exit(close(file))" "\"connection\"))"
例如,在上面的[9]
中,调用就在那里,但我不希望结果中有file
。理想情况下,它是on.exit(close(
。
我该如何改进这个正则表达式?
答案 0 :(得分:6)
如果您曾尝试使用正则表达式解析HTML,那么您就知道它可能是一场噩梦。使用某些HTML解析器并以这种方式提取信息总是更好。我对R代码有同感。 R的优点在于它的功能性,您可以通过代码检查任何功能。
像
这样的东西call.ignore <-c("[[", "[", "&","&&","|","||","==","!=",
"-","+", "*","/", "!", ">","<", ":")
find.funcs <- function(f, descend=FALSE) {
if( is.function(f)) {
return(find.funcs(body(f), descend=descend))
} else if (is(f, "name") | is.atomic(f)) {
return(character(0))
}
v <- list()
if (is(f, "call") && !(deparse(f[[1]]) %in% call.ignore)) {
v[[1]] <- deparse(f)
if(!descend) return(v[[1]])
}
v <- append(v, lapply(as.list(f), find.funcs, descend=descend))
unname(do.call(c, v))
}
可行。在这里,我们迭代函数中的每个对象,寻找call
s,忽略那些你不关心的对象。你可以在像
find.funcs(read.table)
# [1] "default.stringsAsFactors()"
# [2] "missing(file)"
# [3] "missing(text)"
# [4] "textConnection(text, encoding = \"UTF-8\")"
# [5] "on.exit(close(file))"
# [6] "is.character(file)"
# ...
如果您想查看其他功能的函数调用,可以将descend=
参数设置为TRUE
。
我确信有很多软件包可以让这更容易,但我只想表明它是多么简单。
答案 1 :(得分:3)
Perl模式下的递归正则表达式
在一般情况下,我相信您已经意识到尝试匹配此类结构的危险:如果您的文件包含您不想匹配的if()
之类的内容,该怎么办?
话虽如此,我相信这种递归正则表达式符合我理解它们的要求
[a-z]+(\((?:`[()]|[^()]|(?1))*\))
请参阅demo。
我没有完全按照R
语法进行划分,但是这样的事情应该可行,并且您可以调整函数名称和参数以满足您的需求:
grepl("[a-z]+(\\((?:`[()]|[^()]|(?1))*\\))", subject, perl=TRUE);
<强>解释强>
[a-z]+
与左括号前的字母匹配(
启动第1组\(
匹配左括号(?:
启动将重复的非捕获组。捕获组匹配多种可能性:BACKTICK[()]
匹配反引号+ (
或)
(抱歉,不知道如何在此编辑器中显示反引号|[^()]
或匹配一个不是括号的字符|(?1)
或者匹配第1组括号(recurse))*
关闭非捕获组,重复零次或多次\)
匹配右括号)
结束第1组