这是提取源文件中所有字符串的简单方法。我们可以做出一个重要的决定:我们是否预处理代码?否则,如果它们是通过宏生成的,我们可能会错过一些字符串。我们还必须将#
视为注释字符。
由于这是一个快速而肮脏的解决方案,因此 C 代码的语法正确性不是问题。但是,我们将尊重评论。
现在,如果源代码经过预处理(使用gcc -E source.c
),那么多行字符串已经折叠成一行!此外,评论已被删除。甜的。剩下的唯一注释是用于调试目的的提及行号和源文件。基本上我们要做的就是
$ gcc -E source.c | perl -nE'
next if /^#/; # skip line directives etc.
say $1 while /(" (?:[^"\\]+ | \\.)* ")/xg;
'
输出(以我的其他答案中的测试文件作为输入):
""
"__isoc99_fscanf"
""
"__isoc99_scanf"
""
"__isoc99_sscanf"
""
"__isoc99_vfscanf"
""
"__isoc99_vscanf"
""
"__isoc99_vsscanf"
"Here, on the other hand, I've gone crazyand really let the literal span several lineswithout bothering with quoting each line'scontent. This works, but you can't indent"
"Hello %s:\n%s\n"
"World"
所以是的,这里有很多垃圾(它们似乎来自__asm__
块),但这非常有效。
注意我使用的正则表达式:/(" (?:[^"\\]+ | \\.)* ")/x
. 捕获中的模式可以解释为
" # a literal '"'
(?: # the begin of a non-capturing group
[^"\\]+ # a character class that matches anything but '"' or '\', repeated once or more
|
\\. # an escape sequence like '\n', '\"', '\\' ...
)* # zero or more times
" # closing '"'
该解决方案的局限性是什么?
- 我们需要一个预处理器
- 此代码经过测试
gcc
clang
也支持该-E
选项,但我不知道输出是如何格式化的。
- 字符文字是一种失败模式,例如
myfunc('"', a_variable, '"')
将被提取为"', a_variable, '"
.
- 我们还从其他源文件中提取字符串。(误报)
哦等等,我们可以通过解析预处理器插入的源文件注释来修复最后一点。他们看起来像
# 29 "/usr/include/stdio.h" 2 3 4
因此,如果我们记住当前文件名,并将其与我们想要的文件名进行比较,我们可以跳过不需要的字符串。这一次,我将把它写成一个完整的脚本而不是一个单行。
use strict; use warnings;
use autodie; # automatic error handling
use feature 'say';
my $source = shift @ARGV;
my $string_re = qr/" (?:[^"\\]+ | \\.)* "/x;
# open a pipe from the preprocessor
open my $preprocessed, "-|", "gcc", "-E", $source;
my $file;
while (<$preprocessed>) {
$file = $1 if /^\# \s+ \d+ \s+ ($string_re)/x;
next if /^#/;
next if $file ne qq("$source");
say $1 while /($string_re)/xg;
}
用法:$perl extract-strings.pl source.c
这现在产生输出:
"Here, on the other hand, I've gone crazyand really let the literal span several lineswithout bothering with quoting each line'scontent. This works, but you can't indent"
"Hello %s:\n%s\n"
"World"
如果你不能使用方便的预处理器来折叠多行字符串并删除注释,这会变得更丑陋,因为我们必须自己考虑所有这些。基本上,您想一次吞下整个文件,而不是逐行迭代。然后,您跳过任何评论。不要忘记忽略预处理器指令。之后,我们可以像往常一样提取字符串。基本上,你必须重写语法
Start → Comment Start
Start → String Start
Start → Whatever Start
Start → End
到正则表达式。由于上面是常规语言,这并不难。