正则表达式,用于在函数体中查找函数调用

时间:2014-06-30 01:25:47

标签: regex r grep

请将read.table的正文视为使用以下代码创建的文本文件:

sink("readTable.txt")
body(read.table)
sink()

使用正则表达式,我想在foo(a, b, c)中找到"readTable.txt"形式的所有函数调用(但有任意数量的参数)。也就是说,我希望结果包含read.table主体中所有被调用函数的名称。这包括形式
的嵌套函数  foo(a, bar(b, c))。可以包含保留字(returnfor等)和使用反向标记('=='()'+'()等)的函数,因为我可以在以后删除它们。

所以一般来说,我正在寻找模式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(

我该如何改进这个正则表达式?

2 个答案:

答案 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组