5

使用 grep、vim 的 grep 或其他 unix shell 命令,我想在一个大型 cpp 文件中找到它们的主体中包含特定单词的函数。

在我正在使用的文件中,我正在查找的单词位于缩进行上,相应的函数标题是缩进行上方的第一行,从位置 0 开始,而不是“{”。

例如在以下代码片段中搜索 JOHN_DOE

int foo ( int arg1 ) 
{
    /// code 
}
void bar ( std::string arg2  )
{
    /// code
    aFunctionCall( JOHN_DOE );
    /// more code
}

应该给我

void bar ( std::string arg2  )

我希望在 grep/vim/unix shell 脚本中捕获的算法可能最好使用缩进和格式假设,而不是尝试解析 C/C++。

感谢您的建议。

4

8 回答 8

3

我可能会因此而被否决!

我是一个狂热的 (G)VIM 用户,但是当我想查看或理解一些代码时,我使用Source Insight。不过,我几乎从不将它用作实际的编辑器。

在这种情况下,它完全符合您的要求,例如在关系窗口中显示使用某些突出显示的数据类型/定义/常量/等的所有函数/方法...

来源洞察网站的关系窗口的 gif
(来源:sourceinsight.com

哎哟! 我的代表去了。

于 2009-05-07T10:13:17.670 回答
2

据我所知,这是做不到的。原因如下:

首先,您必须跨行搜索。没问题,在 vim 中向字符类添加 _ 会告诉它包含新行。所以 {_.*} 将匹配多行括号之间的所有内容。

所以现在你需要匹配函数头的任何模式(即使你让它工作也很脆弱),然后,这就是问题所在,它和你的搜索字符串之间的任何行,最后匹配你的搜索字符串。所以你可能有一个像

/^\(void \+\a\+ *(.*)\)\_.*JOHN_DOE

但发生的情况是 vim 第一次找到函数头时,它开始匹配。然后它匹配每个字符,直到找到 JOHN_DOE。其中包括文件中的所有函数头。

所以问题是,据我所知,除了这个正则表达式模式之外,没有办法告诉 vim 匹配每个字符。即使有,正则表达式也不是这项工作的工具。这就像用锤子打开啤酒一样。我们应该做的是编写一个简单的脚本,为您提供这些信息,我有。

fun! FindMyFunction(searchPattern, funcPattern)
  call search(a:searchPattern)
  let lineNumber = line(".")
  let lineNumber = lineNumber - 1
  "call setpos(".", [0,  lineNumber, 0, 0])

  let lineString = getline(lineNumber)
  while lineString !~ a:funcPattern
    let lineNumber = lineNumber - 1
    if lineNumber < 0
      echo "Function not found :/"
    endif
    let lineString = getline(lineNumber)
  endwhile

  echo lineString

endfunction

这应该会给你你想要的结果,而且它比 Cthulhu 自己口中吐出的正则表达式更容易共享、调试和重新调整用途。

于 2009-05-06T18:09:25.910 回答
1

您不能使用正则表达式可靠地做到这一点,因为代码不是正则语言。您需要一个真正的解析器来处理相关语言。

于 2009-05-06T13:32:51.957 回答
1

艰难的呼吁,虽然作为一个起点,我会建议这个精彩的VIM 正则表达式教程

于 2009-05-06T13:11:37.860 回答
1

对于那种东西,虽然又是原始搜索,但我会推荐compview插件。它将打开一个搜索窗口,因此您可以看到搜索发生的整行并自动跳转到它。提供了一个很好的概述。

替代文字
(来源:axisym3.net

于 2009-05-06T13:55:43.180 回答
1

啊!我承认这有点过头了:

一个过滤标准输入、去除注释和将函数体放在同一行的小程序。它会被类声明中的命名空间和函数定义所迷惑,除了其他的东西。但这可能是一个好的开始:

#include <stdio.h>
#include <assert.h>

int main() {
    enum {
        NORMAL,
        LINE_COMMENT,
        MULTI_COMMENT,
        IN_STRING,
    } state = NORMAL;
    unsigned depth = 0;
    for(char c=getchar(),prev=0; !feof(stdin); prev=c,c=getchar()) {
        switch(state) {
        case NORMAL:
            if('/'==c && '/'==prev)
                state = LINE_COMMENT;
            else if('*'==c && '/'==prev)
                state = MULTI_COMMENT;
            else if('#'==c)
                state = LINE_COMMENT;
            else if('\"'==c) {
                state = IN_STRING;
                putchar(c);
            } else {
                if(('}'==c && !--depth) || (';'==c && !depth)) {
                    putchar(c);
                    putchar('\n');
                } else {
                    if('{'==c)
                        depth++;
                    else if('/'==prev && NORMAL==state)
                        putchar(prev);
                    else if('\t'==c)
                        c = ' ';
                    if(' '==c && ' '!=prev)
                        putchar(c);
                    else if(' '<c && '/'!=c)
                        putchar(c);
                }
            }
            break;
        case LINE_COMMENT:
            if(' '>c)
                state = NORMAL;
            break;
        case MULTI_COMMENT:
            if('/'==c && '*'==prev) {
                c = '\0';
                state = NORMAL;
            }
            break;
        case IN_STRING:
            if('\"'==c && '\\'!=prev)
                state = NORMAL;
            putchar(c);
            break;
        default:
            assert(!"bug");
        }
    }
    putchar('\n');
    return 0;
}

它的 c++,所以只是它在一个文件中,将它编译成一个名为“剥离器”的文件,然后:

cat my_source.cpp | ./stripper | grep JOHN_DOE

所以考虑输入:

int foo ( int arg1 ) 
{
    /// code 
}
void bar ( std::string arg2  )
{
    /// code
    aFunctionCall( JOHN_DOE );
    /// more code
}

“”的输出cat example.cpp | ./stripper是:

int foo ( int arg1 ) { }
void bar ( std::string arg2 ){  aFunctionCall( JOHN_DOE ); }

“”的输出cat example.cpp | ./stripper | grep JOHN_DOE是:

void bar ( std::string arg2 ){  aFunctionCall( JOHN_DOE ); }

查找函数名称(猜测它的最后一个标识符在“ (”之前)的工作留给读者作为练习。

于 2009-05-06T19:18:21.033 回答
0

您可以使用grep -r -n -H JOHN_DOE *它将从当前目录开始递归地在文件中查找“JOHN_DOE”

您可以使用以下代码实际找到包含文本表达式的函数:

    public void findFunction(File file, String expression) {
    Reader r = null;
    try {
        r = new FileReader(file);
    } catch (FileNotFoundException ex) {
        ex.printStackTrace();
    }
    BufferedReader br = new BufferedReader(r);

    String match = "";
    String lineWithNameOfFunction = "";

    Boolean matchFound = false;

    try {
        while(br.read() > 0) {
            match = br.readLine();
            if((match.endsWith(") {")) ||
                    (match.endsWith("){")) ||
                    (match.endsWith("()")) ||
                    (match.endsWith(")")) ||
                    (match.endsWith("( )"))) {
                // this here is because i guessed that method will start
                // at the 0
                if((match.charAt(0)!=' ') && !(match.startsWith("\t"))) {
                    lineWithNameOfFunction = match;                        
                }
            }

            if(match.contains(expression)) {
                matchFound = true;
                break;
            }
        }
        if(matchFound)
            System.out.println(lineWithNameOfFunction);
        else 
            System.out.println("No matching function found");
    } catch (IOException ex) {
        ex.printStackTrace();
    }
}

我用 JAVA 写了这个,测试了它,就像一个魅力。虽然有一些缺点,但对于初学者来说很好。没有添加对包含相同表达式的多个函数的支持,也许还有其他一些东西。试试看。

于 2009-05-06T13:48:49.843 回答
0

就像罗伯特说的,正则表达式会有所帮助。在命令模式下,通过键入“/”字符后跟您的正则表达式来启动正则表达式搜索。

Ctags 1也可能对您有用。它可以为项目生成标签文件。这个标签文件允许用户直接从函数调用跳转到它的定义,即使它在另一个文件中使用“CTRL+]”。

于 2009-05-06T13:51:26.797 回答