【问题标题】:Regex to pull out C function prototype declarations?正则表达式提取 C 函数原型声明?
【发布时间】:2009-01-24 15:44:23
【问题描述】:

当谈到正则表达式时,我处于学习曲线的某个阶段,我需要使用它们来自动修改一堆 C 标头中的函数原型。有谁知道一个体面的正则表达式可以在 C 标头中查找任何和所有函数原型,同时排除其他所有内容?

编辑:最初不清楚的三件事:

  1. 关心 C++,只关心 C++。这意味着无需担心模板等问题。
  2. 解决方案必须使用 typedef 和结构,不限于基本的 C 类型。
  3. 这是一种一次性的事情。它不需要很漂亮。我不在乎它有多少杂乱无章,只要它有效,但我不想要一个复杂、难以实施的解决方案。

【问题讨论】:

    标签: c regex function


    【解决方案1】:

    您可以使用ANSI C yacc/lex grammar 实现解析器。

    【讨论】:

    • 我不能重复它。正则表达式不能替代解析器。 +1。
    • Answer 乍一看似乎很好,但尝试遵循它会发现,简单的语法解析器是一个糟糕的选择。它显然需要在将代码传递给解析器之前进行预处理,这本身需要付出很大的努力。但即便如此,解析器也会阻塞由 typedefs 指定的自定义类型。正如this FAQ 中所述,需要创建语义分析器,这是一项艰巨的任务。可悲的是,解析器非常适合教育,但在实际解决手头的任务时效率非常低,这确实可以用正则表达式来完成,而且痛苦要少得多。
    【解决方案2】:

    要正确执行此操作,您需要根据 C 语言语法进行解析。但是,如果这仅适用于 C 语言并且仅适用于头文件,也许您可​​以采取一些捷径并在没有完全成熟的 BNF 的情况下度过难关。

    ^
    \s*
    (unsigned|signed)?
    \s+
    (void|int|char|short|long|float|double)  # return type
    \s+
    (\w+)                                    # function name
    \s*
    \(
    [^)]*                                    # args - total cop out
    \)
    \s*
    ;
    

    这绝不是正确的,需要努力。但它可以代表一个起点,如果你愿意付出一些努力并改进它。它可以被跨越行的函数定义、函数指针参数、宏以及可能许多其他东西破坏。

    请注意,BNF 可以转换为正则表达式。这将是一个大而复杂的正则表达式,但它是可行的。

    【讨论】:

    • 我怀疑经典的正则表达式不适合解析嵌套的 C++ 模板定义。
    • 没错 - 幸运的是,他们处理的是 C 而不是 C++。
    • 经典正则表达式是非递归的,因此它们不能真正表达 BNF 中的所有内容。
    • 您的符号至少不考虑限定符(const、volatile、restrict)或 unsigned long int 或 long double 或指针或数组或 typedef(文件 *?)。你得到+1;你说它不完整,但它是一个开始。但这些只是需要担心的一些因素。
    • 它只适用于返回标准 C 类型的函数,而不是自定义类型
    【解决方案3】:

    对于一次性练习,您最好从简单的开始并查看您必须扫描的代码。选择三个最差的标题,生成一个正则表达式或一系列正则表达式来完成这项工作。您必须决定是否以及如何处理包含函数声明的 cmets(实际上,处理包含 cmets 的函数声明)。处理:

    extern void (*function(int, void (*)(int)))(int);
    

    (可能是标准 C 函数 signal())在正则表达式中很困难,因为嵌套的括号。如果您没有任何这样的函数原型,那么花时间研究如何处理它们就是浪费时间。类似的 cmets 适用于指向多维数组的指针。很有可能你有风格惯例来简化你的生活。您不得使用 C99 (C++) cmets;你不需要围绕它们编写代码。您可能不会将多个声明放在一行中,无论有没有共同类型 - 所以您不必处理它。

    extern int func1(int), func2(double); double func3(int);  // Nasty!
    

    【讨论】:

      【解决方案4】:

      假设您的代码格式类似于

      type name function_name(variables **here, variables &here)
      {
          code
      }
      

      这是 Powershell 的单行代码:

      ls *.c, *.h | sls "^(\w+( )?){2,}\([^!@#$+%^]+?\)"
      

      返回结果如下:

      ...
      common.h:37:float max(float a, float b)
      common.h:42:float fclamp(float val, float fmin, float fmax)
      common.h:51:float lerp(float a, float b, float b_interp)
      common.h:60:float scale(float val, float valmin, float valmax, float min,
      float max)
      complex.h:3:typedef struct complex {
      complex.h:8:double complexabs(complex in)
      complex.h:13:void complexmult(complex *out, complex a, complex b)
      complex.h:20:void complexadd(complex *out, complex a, complex b)
      complex.h:27:int mandlebrot(complex c, int i)
      ...
      

      要查看没有文件细节的行,请添加format-table -property line(或缩写为ft -p line):

      ls *.c, *.h | sls "^(\w+( )?){2,}\([^!@#$+%^]+?\)" | format-table -p line
      

      返回:

      Line
      ----
      void render(SDL_Surface *screen)
      void saveframe(SDL_Surface *screen)
      int handleevents(SDL_Surface *screen)
      int WinMain(/*int argc, char* args[]*/)
      void printscreen(SDL_Surface *screen, unsigned int exclude)
      void testsection(char name[])
      void sdltests(SDL_Surface *screen, SDL_Window *window, int width, int height)
      int WinMain(/*int argc, char *argv[]*/)
      int random(int min, int max) {
      int main(int argc, char *argv[])
      

      奖励:正则表达式的解释:

      ^(\w+(\s+)?){2,}\([^!@#$+%^]+?\)
      ^                                Start of a line
       (         ){2,}                 Create atom to appear to or more times
                                       (as many as possible)
        \w+(\s+)?                      A group of word characters followed by
                                       an optional space
                      \(            \) Literal parenthesis containing
                        [^!@#$+%^]+?   A group of 0 or more characters
                                       that AREN'T in “!@#$+%^”
      

      【讨论】:

        【解决方案5】:

        这是一个正则表达式,它是查找 C 函数名称的良好起点:

        ^\s*(?:(?:inline|static)\s+){0,2}(?!else|typedef|return)\w+\s+\*?\s*(\w+)\s*\([^0]+\)\s*;?
        

        这些是验证表达式的一些测试用例:

        // good cases
        static BCB_T   *UsbpBufCtrlRemoveBack   (BCB_Q_T *pBufCtrl);
        inline static AT91_REG *UDP_EpIER               (UDP_ENDPOINT_T *pEndpnt);
        int UsbpEnablePort (USBP_CTRL_T *pCtrl)
        bool_t IsHostConnected(void)
        inline AT91_REG *UDP_EpCSR (UDP_ENDPOINT_T *pEndpnt)
        
        // shouldn't match
        typedef void (*pfXferCB)(void *pEndpnt, uint16_t Status);
            else if (bIsNulCnt && bIsBusyCnt)
                    return UsbpDump(Buffer, BufSize, Option);
        

        最后,这是一个简单的 TCL 脚本,用于读取文件并提取所有函数原型和函数名称。

        set fh [open "usbp.c" r]
        set contents [read $fh]
        close $fh
        set fileLines [split $contents \n]
        set lineNum 0
        set funcCount 0
        set funcRegexp {^\s*(?:(?:inline|static)\s+){0,2}(?!else|typedef|return)\w+\s+\*?\s*(\w+)\s*\([^0]+\)\s*;?}
        foreach line $fileLines {
            incr lineNum
            if {[regexp $funcRegexp $line -> funcName]} {
                puts "line:$lineNum, $funcName"
                incr funcCount
            }; #end if
        
        }; #end foreach
        puts "$funcCount functions found."
        

        【讨论】:

        • 如何使用 GNU grep 来实现?
        【解决方案6】:

        单行正则表达式听起来很难。我个人为此使用 perl 脚本。这很容易。 基本方法是> 1. 调用你最喜欢的 c 预处理器来消除 cmets 并扩展宏。 (所以更容易) 2.计数'{''}'符号。对于纯 C 中的函数,它们具有可预测的行为,可让您检测函数名称。 3.在原始源中查找函数名称(在预处理以获得具有typedefs的签名之前) 这是一种效率低下的方法,但对我来说效果很好。 第 1 步并不是真正必要的,但它会让您的生活更轻松

        【讨论】:

        • 通过代码中的示例,此答案的质量将从无到好。
        • 是的,举个例子会更有帮助。
        【解决方案7】:

        假设您已将整个 c 文件读入 $buffer。 * 首先创建正则表达式,用相同数量的空格和换行符替换所有 cmets,这样行和列的位置就不会改变 * 创建可以处理带括号的字符串的正则表达式 * 然后像这样的正则表达式找到函数: (静态|)\s+(\w+)\s*$parenthezized_regexp+*{

        此 reg exp 不处理函数定义使用预处理器指令的函数。

        如果你选择 lex/yacc,你必须结合 ansic 和预处理器语法来处理函数定义中的那些预处理器指令

        【讨论】:

          【解决方案8】:

          继续伟大的Dean TH answer

          这会找到

          • 只有函数而不是声明
          • 返回指针的函数

          ^([\w\*]+( )*?){2,}\(([^!@#$+%^;]+?)\)(?!\s*;)

          【讨论】:

            猜你喜欢
            • 2013-03-25
            • 2012-07-15
            • 1970-01-01
            • 1970-01-01
            • 2019-11-30
            • 1970-01-01
            • 2021-03-29
            • 2015-07-08
            • 1970-01-01
            相关资源
            最近更新 更多