用awk去除C语言注释

今天闲逛Linux宝库,看到论坛里有人在讨论如何用 shell 脚本来处理 C 语言注释,发帖时间是 08-10-23(以前怎么都没注意到,失败...),但问题好像并没被解决。正好这两天玩 sed & awk,来小试一下身手。

C语句注释

本文讨论的是 C99 标准,它支持单行注释(“// ...”)和块注释(“/*...*/”),并且当单行注释以“/”结尾时也可以跨多行。测试代码如下:

#include <stdlib.h>
#include <stdio.h>
int main (int argc, char *argv[])
{
// not show/
not show/
not show
// not show
/* not show */
int is; // not show
int/* not show */ ms; /* not show */
double ds; // not show/
not show/
not show
double dm; /* ...
not show
not show */ float fs; /**
* now show
*/
float/**/ fm;
char cs[] = "aaa // /***/";
char cm1[] = /* not show */"hello*/";
char cm2[] = "/*redraiment"/* not show */;
/* printf("/////"); */
return EXIT_SUCCESS;
}

其中绿色部分就是注释,经过处理后需要将它们全部移除或用替换成空字符。论坛原帖中没有处理以“/”结尾的单行注释,也没处理注释关键字出现在字符串中的情况。

工具的选择

sed 是一个流编辑器,它能对文件进行“插入”、“删除”、“替换”、“追加”等编辑操作;而 awk 是一门模式匹配的程序设计语言,它除了能编辑文本还可以统计信息,你可以把它看成基于文本文件的数据库系统。原帖中作者使用 sed 来解决,因为问题涉及的操作仅仅是删除 C 代码中的注释。但由于以下原因导致 sed 心有余而力不足:

一、不支持最小匹配

正则表达式默认采用贪心匹配策略,在正则的标准中通过在量词后面加“?”来使用最小匹配策略,详细规则介绍请参见这里。问题中多行注释必须使用最小匹配原则,如果关键词只有一个字符,就可以通过排除字符集来模拟,比如我们经常用“"[^"]*"”来匹配一个字符串。可惜 C 语言的注释关键词都是多字符。

二、排序字符(Collating Symbols)只是一个美丽的梦想

排序字符用于字符列表(character list)中。按照文档的描述,排序字符可将多个字符当一个字符来匹配。比如模式“[[.ch.]i]”可以匹配行“char”和“int”,但不能匹配“coho”。如果它能被支持,就可以把“/*”、“*/”、“//”都看做一个字符,通过(一)中的排除字符集来实现最小匹配。可惜到目前为止,我接触的工具中没有一款支持这个特性,更不用说对正则支持平平的 sed 了。

三、字符串来捣乱

在《sed单行脚本学习笔记》中我给出了一段 sed 单行脚本,用于替换不在字符串中的模式。看起来正适合解决这个问题,但它的前提是模式要和待修改的文本完全匹配。由(一)、(二)两个条件决定了 sed 实现的正则表达式无法匹配 C 语言所有类型的注释。另外,sed 提供的控制语句是“b、t”,它们的功能是类似于 C 语言的 goto,因此它不能像“if ... else ...”一样方便地判断某个注释的起始位置是否在字符串中。

由于上述原因,我们需要一个变量来记录当前状态——是否在字符串中。因此我使用 awk 来解决。

我的解决方案

# filename: strip_c_comment.awk
# issue: awk -f scrip_c_comment.awk test.c
BEGIN { FS="" }
!(ignore_line && $NF == "//") && !ignore_line-- {
ignore_line = 0;
for(i = 1; i <= NF; i++) {
if (ignore_block) {
if ($i $(i+1) == "*/") {
ignore_block = 0
i++ # remove '*'
}
continue
}
if (!instr && $i $(i+1) == "/*") {
ignore_block = 1
i++ # remove '/'
continue
}
if (!instr && $i $(i+1) == "//") {
ignore_line = ($NF == "//")? 1: 0
break
}
if ($i == "/"") {
instr = 1 - instr
}
printf($i)
}
printf("/n")
}

在开始时将 FS 设为空字符串,使得输入记录的每个字符都成为一个独立的字段。代码中的三个布尔变量分别代表:

  1. ignore_line:如果上一行是以“/”结尾的单行注释则为“True”;
  2. ignore_block:如果当前字符在块注释中则为“True”;
  3. instr:如果当前字符在非注释的字符串内则为“True”。

脚本的工作就是保留“ignore_line”和“ignore_block”都为“False”时的字符。

执行结果

#include <stdlib.h>
#include <stdio.h>
int main (int argc, char *argv[])
{
int is;
int ms;
double ds;
double dm;
float fs;
float fm;
char cs[] = "aaa // /***/";
char cm1[] = "hello*/";
char cm2[] = "/*redraiment";

return EXIT_SUCCESS;
}


版权声明

请尊重原创作品。转载请保持文章完整性,并以超链接形式注明原始作者“redraiment”和主站点地址,方便其他朋友提问和指正。

联系方式

我的邮箱,欢迎来信(redraiment@gmail.com)
我的Blogger(子清行):http://redraiment.blogspot.com/
我的Google Sites(子清行):https://sites.google.com/site/redraiment
我的CSDN博客(梦婷轩):http://blog.csdn.net/redraiment
我的百度空间(梦婷轩):http://hi.baidu.com/redraiment

时间: 2025-01-06 17:45:59

用awk去除C语言注释的相关文章

实现去除c语言注释的小工具_C 语言

去除C代码中的注释,1. 单行注释//:2. 多行注释/**/:3. 单行注释以"\"结尾则下一行也为注释:4. 字符串中的注释不处理.说是C语言,但其实所有C语系的都可以,比如Java. 小工具:去除C语言注释  复制代码 代码如下: #include <stdio.h> int main(int argc, char* argv[]) {  enum {    literal,    single,    multiple,    string  } mode = li

awk系列:如何使用awk语言编写脚本

从 awk 系列开始直到第 12 部分,我们都是在命令行或者脚本文件里写一些简短的 awk 命令和程序. 然而 awk 和 shell 一样也是一个解释型语言.通过从开始到现在的一系列的学习,你现在能写可以执行的 awk 脚本了. 和写 shell 脚本差不多,awk 脚本以下面这一行开头: #! /path/to/awk/utility -f  例如在我的系统上,awk 工具安装在 /user/bin/awk 目录,所以我的 awk 脚本以如下内容作为开头: #! /usr/bin/awk -

awk 系列:如何使用 awk 语言编写脚本

从 awk 系列开始直到第 12 部分,我们都是在命令行或者脚本文件里写一些简短的 awk 命令和程序. 然而 awk 和 shell 一样也是一个解释型语言.通过从开始到现在的一系列的学习,你现在能写可以执行的 awk 脚本了. 和写 shell 脚本差不多,awk 脚本以下面这一行开头: #! /path/to/awk/utility -f 例如在我的系统上,awk 工具安装在 /user/bin/awk 目录,所以我的 awk 脚本以如下内容作为开头: #! /usr/bin/awk -f

Swift语言指南(二) Swift语言基础:注释和分号

注释 通过注释向自己的代码中注入不可执行的文本,作为你自己的笔记或提示.Swift编译器运行时会忽略注释. Swift的注释与C语言极其相似,单行注释以两个反斜线开头: //这是一行注释 多行注释以/*开始,以*/结束: <span style="color: #008000;">/* 这也是一条注释, 但跨越多行 */ </span> 查看本栏目更多精彩内容:http://www.bianceng.cnhttp://www.bianceng.cn/Progra

去除PHP代码中的空白和注释

  PHP内置了一个php_strip_whitespace方法用于读取php文件并去除代码中的空白和注释,但不支持直接读取内容去除空白和注释,下面的方法则可以支持读取字符串内容,并且ThinkPHP框架内置了该方法. /** * 去除代码中的空白和注释 * @param string $content 代码内容 * @return string */ function strip_whitespace($content) { $stripStr = ''; //分析php源码 $tokens

使用正则去除php代码中的注释方法_php实例

测试代码 文件:a.PHP <?php /** * 加法计算 * 测试 */ // 设定$a的值 $a = 10; // 设定$b的值 $b = 5; // 加法 $c = $a + $b; # 输出结果 echo $c; 文件:test.php echo "源码:<br />"; show_source('./a.php'); echo "<hr />去除注释后:<br />"; highlight_string(remo

Shell脚本实现C语言代码行数统计_linux shell

写了一个比较粗糙的C语言代码行数统计脚本,目前还有些bug,而且效率也不高.脚本主要就是去除大部分的注释后统计行数,相当于做了一部分预处理的工作.下面是代码: #!/bin/bash filename=$1 echo "`whoami`" if [ $# -lt 1 ];then echo "usage : ./scripts filename" exit -1 fi if [ ! -f $filename ];then echo "$filename i

Unix下如何使用awk

1.什么是awk 你可能对UNIX比较熟悉,但你可能对awk很陌生,这一点也不奇怪,的确,与其优秀的功能相比,awk还远没达到它应有的知名度.awk是什么?与其它大多数UNIX 命令不同的是,从名字上看,我们不可能知道awk的功能:它既不是具有独立意义的英文单词,也不是几个相关单词的缩写.事实上,awk是三个人名的缩写,他们 是:Aho.(Peter)Weinberg和(Brain)Kernighan.正是这三个人创造了awk---一个优秀的样式扫描与处理工具. AWK的功能是什么?与sed和g

linux命令终极系列(awk)

来源:http://www.cppblog.com/API/archive/2011/04/24/144895.html 参考:http://man.linuxde.net/awk 一.awk功能特点     awk是一个非常重要的命令或者认为是一种语言.因为他可以做数学运算,流程控制语句,流控制,还有样式装入的功能.反正是非常强大.awk是三位创建者编写的.把awk定义为:样式扫描处理语言.   awk吸收了C语言很多的特点,所以与C语言有点类拟. 二.awk的调用方式    awk提供了适应