当谈到正则表达式时,我在学习曲线上的某个地方,我需要使用它们来自动修改一堆C头中的函数原型。有没有人知道一个不错的正则表达式来查找C头中的任何和所有函数原型,同时排除其他所有内容?
编辑:最初不清楚的三件事:
答案 0 :(得分:17)
您可以使用ANSI C yacc/lex grammar实现解析器。
答案 1 :(得分:10)
要正确执行此操作,您需要根据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*
;
这绝不是正确的,需要工作。但如果你愿意付出一些努力并改进它,它可能代表一个起点。它可以通过跨越行,函数指针参数,MACROS以及可能的许多其他东西的函数定义来打破。
请注意,BNF 可以转换为正则表达式。它将是一个庞大而复杂的正则表达式,但它是可行的。
答案 2 :(得分:7)
对于一次性练习,您可能最好从简单开始并查看您必须扫描的代码。选择三个最差的标题,生成一个正则表达式或一系列正则表达式来完成这项工作。您必须决定是否以及如何处理包含函数声明的注释(实际上,包含注释的函数声明)。处理:
extern void (*function(int, void (*)(int)))(int);
由于嵌套括号,(可能是标准C函数signal()
)在正则表达式中很难。如果你没有任何这样的函数原型,那么花时间研究如何处理它们就浪费了时间。类似的注释适用于指向多维数组的指针。您可能会有简约的风格习惯来简化您的生活。您不能使用C99(C ++)注释;你不需要围绕它们编码。你可能不会在一行中放置多个声明,无论是否有共同类型 - 所以你不必处理它。
extern int func1(int), func2(double); double func3(int); // Nasty!
答案 3 :(得分:5)
假设您的代码格式类似于
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 “!@#$+%^”
答案 4 :(得分:2)
这是一个正则表达式,是查找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."
答案 5 :(得分:1)
单线版正则表达式听起来很难。我个人使用perl脚本。这很容易。 基本方法是> 1.调用您最喜欢的c预处理器以消除注释并扩展宏。 (所以它更容易) 2.计算'{''}'符号。对于普通C中的函数,它们具有可预测的行为,允许您检测函数名称。 3.将函数名称查找到原始源(在预处理之前获取具有typedef的签名) 这是一种效率低下的方法,但对我来说效果很好。 第1步并不是必需的,但它会让你的生活更轻松
答案 6 :(得分:0)
假设您将整个c文件读入$ buffer。 *首先创建regexp,用相同数量的空格和换行符替换所有注释,这样行和列位置不会改变 *创建可以处理带括号的字符串的regexp *然后像这样的regexp找到函数: (静态|)\ S +(\ W +)\ S * $ parenthezized_regexp + * {
此reg exp不处理函数定义使用预处理程序指令的函数。
如果你选择lex / yacc,你必须结合ansi c和预处理器语法来处理函数定义中的那些预处理器指令
答案 7 :(得分:0)