最近搞软件著作权,去除代码空行和注释比较麻烦,想写个程序自动去除,去网上搜了下,发现有类似的程序,不过只有去除注释。鉴于word中可以去除空行(用^p^p替换^p),先用网上的代码,以后有时间写个完整版的,遍历代码文件夹搜索指定的格式,自动生成最终结果。

注:这个博客的方法不是最好的,最好的方法是使用正则表达式(2013.12.03)

转载自http://blog.csdn.net/mznewfacer/article/details/6942857

程序员面试宝典上面的题目有很多是很经典的问题,可供我们思考,而且会对我们面试有很大好处。

下面是第45页的一个题目:编写一个函数,实现把C/C++程序代码中的注释去掉,我开始看的时候总是看不懂,后来在网上看到一个网友详细的分析了其代码,但是我看了之后觉得有好多地方他分析的都不对,于是,我自己又花了半天的时间分析了一遍,觉得自己理解了,下面给出我的分析语句。如果还有不严谨的部分,请指教!

/********************************************************
 功能:去除C/C++中的注释
 输入:指向C/C++程序代码的指针及长度
 来源:程序员面试宝典第45页

分析:一次读取一行,分两种情况,因为有两种注释:

(1)在读取到的一行中查找“//”,如果找到,则把“//”及其后的部分扔掉。

(2)在读取到的一行中查找“/*”,记录位置pos1,然后再在这行中查找“*/”,如果找到,也记录位置pos2,扔掉它们与其中的内容,以pos2开始,继续查找“/*”;如果在当前行中没有找到,则去掉当前行中“/*”及其后的内容,读取新的一行,查找“*/”,如没有。则去掉读取到的这一行,再读一行,查找“*/”,如找到,记录位置pos2,去掉这一行的0到pos2之间的字符。

(3)进行步骤1、步骤2,直到程序结束。

编程时要考虑的特殊情况i:

“”中的“//”“/*”

''中的“//”“/*”

“//”与“/*”的嵌套关系,比如///* 、/*  //*/
 *********************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>

void remove_comment(char *buf, size_t size)
{
 char *p, *end, c;          //p-动态移动的字符指针,end-指向文件末尾的字符指针,c-存储没一个p指向的字符
 char *sq_start, *dq_start; //sq_start-单引号开始位置(single),dq_start-双引号开始(double)
 char *lc_start, *bc_start; //lc_start-//的开始位置,bc_start-/*的开始位置
 size_t len;                //记录某符号结束和开始的位置之差(长度,偏移量)
 
 p = buf;
 end = p + size;
 sq_start = dq_start = NULL;
 lc_start = bc_start = NULL;
 
 while (p < end) /*当指针没有到达文件末尾 */
 {
  c = *p;     //用字符变量c存储指针指向的字符
  
  switch (c) //根据c的值做相应处理
  {
   case '\'': /*处理单引号*/
   {
    if (dq_start || lc_start || bc_start) //当遇到过双引号、//或/*的时候,则不需要再判断'//'的情况了。
    {
     p++;
     continue; //继续下一个,对while而言的
    }
    /*******************************以下是没有遇到过双引号或//或/*的时候*******************************/
    if (sq_start == NULL) /****如果未遇到单引号****/
    {
     sq_start = p++; //start指向单引号的开始位置,p指向下一个

}
    else /*如果遇到过单引号,sq_start指向单引号开始位置*/
    {
     len = (p++) -sq_start;

if (len == 2 && *(sq_start+1) == '\\')

{

/*若遇到 “  '\''   ”这种情况则两个单引号并未匹配,遇到的“'”是“\' ”中的,而不是与sq_start所指向单引号匹配*/
      continue;
     }
     
     sq_start = NULL; //否则将sq_start置位为NULL

}
    /*******************************以上是没有遇到过双引号或//或/*的时候*******************************/
    break;
   }
   
   case '\"': /*处理双引号*/
   {
    if (sq_start || lc_start || bc_start) //当遇到过单引号、//或/*的时候,则不需要处理
    {
     p++;
     continue;
    }
    /*****************以下是没有遇到过单引号或//或/*的时候*****************/
    if (dq_start == NULL) /*如果没有遇到过双引号*/
    {
     dq_start = p++; //标记遇到了双引号
    }
    else if (*((p++) -1) =='\\')

{

/*若遇到 “" ab\''cd"   ”这种情况则两个双引号并未匹配,遇到的“"”是“\"”中的,而不是与dq_start所指向双引号匹配*/
     continue;
    }
    dq_start = NULL; //如果双引号中不是//,标记为NULL
    /*****************以上是没有遇到过单引号或//或/*的时候*****************/
   }
   
   case '/': //斜杠,注意这个斜杠也可以是'//',"//",//,/*/中的第二个斜杠,但会在下面第二行代码中被忽略掉
   {
    if (sq_start || dq_start || lc_start || bc_start) //如果是单引号、双引号、斜杠、/*的后面
    {
     p++;
     continue;
    }
    /***********************下面是遇到注释//或/*的时候****************************/
    c = *(p + 1); //否则c取p指向字符的下一个字符
    if (c == '/') //遇到了双斜杠
    {
     lc_start = p; //标记双斜杠的开始
     p += 2; //p指向双斜杠后面的字符
    }
    else if (c == '*') //遇到了/*
    {
     bc_start = p; //标记/*的开始
     p += 2; //p指向/*后面的字符
    }
    /*************************上面是遇到注释//或/*的时候**************************/
    else
    {

p++; 
    }
   }
   
   case '*': //星号,同斜杠,但少了如果遇到/*的情况,因为遇到这种情况后,要判断是不是遇到结束的地方*/了
   {
    if (sq_start || dq_start || lc_start) //如果是单引号、双引号、斜杠、/*的后面
    {
     p++;
     continue; 
    }
    
    if (*(p + 1) != '/') //如果星号后面紧跟的不是斜杠,那么忽略过。
    {
     p++;
     continue;
    }
    
    p += 2; //否则p指向斜杠后面那个字符。注意下面的清空语句,p指向的那个字符并不会被清除。
    memset(bc_start, ' ', p-bc_start); //清空/* …… */中间的内容包括注释符号本身。
    bc_start = NULL;
    break;
   }
   
   case '\n': /*换行符,主要处理遇到双斜杠时,需要清除双斜杠到\n的前面的字符*/
   {
    if (lc_start == NULL) //如果还没有遇到双斜杠,那么忽略
    {
     p++;
     continue;

}
    
    c = *(p - 1);
    /*如果遇到过双斜杠,清空双斜杠本身和到\n前面的那个字符,p指向下一个字符,/r是回车符(光标退回到最前面),这里要判断

c == '\r'是因为在UNIX系统下文件结尾的换行只有\n,而windows系统下文件结尾的换行为\r\n   */

memset(lc_start, ' ', (c == '\r'? ((p++) -1) : p++) - lc_start); 
    lc_start = NULL;
    break;
   }
   
   default:
    p++;
    break;
  }
  /****************************************************
  如果遇到双斜杠,这个if语句存在的意义在于万一最后
  一行代码是带有双斜杠但没有给换行符\n的,也要清除掉。
    *****************************************************/
  if (lc_start) 
  {
   memset(lc_start, ' ', p - lc_start);
  }
 }
}

/**********************************************
   main函数的开始
***********************************************/
int main (int argc, char *argv[])
{
 int fd, n;
 char buf[102400];
 
 if (argc != 2)
 {
  printf("command error: Input as ./command <file>\n");
 }
 
 fd = open(argv[1], O_RDONLY); /*只读打开*/
 if (fd == -1)
 {
  return -1;
 }
 
 n = read(fd, buf, sizeof(buf));
 if (n == -1 || n == 0)
 {
  close(fd);
  return -1;
 }
 printf("test\n");
 remove_comment(buf, n);
 *(buf + n) = '\0';
 printf("%s", buf);
 close(fd);
 
 return 0;
}

去除C/C++程序代码中的注释的更多相关文章

  1. 程序代码中退出函数exit()与返回函数return ()的区别

    程序代码中退出函数exit()与返回函数return ()的区别   exit(0):正常运行程序并退出程序:   exit(1):非正常运行导致退出程序:   return():返回函数,若在主函数 ...

  2. 在ASP.NET项目中的web.config文件里配置数据库连接并在程序代码中获取连接字符串

      1.在<connectionStrings> 标签里添加连接 <connectionStrings> <add name="ConnectionName&q ...

  3. Java代码中特殊注释

    Java代码中特殊注释 TODO: + 说明:标识处有功能代码待编写,待实现的功能在说明中会简略说明. FIXME: + 说明:标识处代码需要修正,甚至代码是错误的,不能工作,需要修复,如何修正会在说 ...

  4. IDEA插件:快速删除Java代码中的注释

    背景   有时,我们需要删除Java源代码中的注释.目前有不少方法,比如: 实现状态机.该方式较为通用,适用于多种语言(取决于状态机支持的注释符号). 正则匹配.该方式容易误判,尤其是容易误删字符串. ...

  5. 【我的Android进阶之旅】Android 源代码中的Java代码中//$NON-NLS-1$ 注释是什么意思?

    1.背景 最近在负责公司基础业务和移动基础设施的开发工作,正在负责Lint代码静态检查工作.因此编写了自定义的Lint规则,在编写自定义的Lint规则前,当然是需要去把Google的关于Lint检测的 ...

  6. javadoc 抽出代码中的注释

    背景: 在已经有的项目里面利用javadoc来抽出代码中每个类,每个方法的注释部分,形成一个文档. 方法1 利用eslipse来实现 之后会在指定的目录下生成html文档 方法2 利用命令来执行 先把 ...

  7. PHP 之去除代码中的注释

    测试文件代码如下: <?php /** * Created by PhpStorm. * User: Yang * Date: 2019/10/16 * Time: 10:25 */ // 计算 ...

  8. C#程序代码中常用的快捷键

    C#中的快捷键,可以更方便的编写代码 //CTRL + SHIFT + B 生成解决方案 //CTRL + F7 生成编译 //CTRL + O 打开文件 //CTRL + SHIFT + O 打开项 ...

  9. SAP程序代码中RANGE表的用法禁忌

    最近经常有出现以上的SQL代码导致程序DUMP,SAP错误日志如下:       经过检查RANGE表GR_MATNR,当用于WHERE条件是,只限较小的数据量的情况(约100条左右): 若为大数据量 ...

随机推荐

  1. hihocoder 1237 Farthest Point

    #1237 : Farthest Point 时间限制:5000ms 单点时限:1000ms 内存限制:256MB 描述 Given a circle on a two-dimentional pla ...

  2. wap测试学习

    注意要点 UI元素 修改源码 物理键操作(回车.确认) 焦点 习惯性操作(前进.后退.屏幕翻转和停止) 刷新 重启服务器 重启浏览器 异常关闭 书签 cookies/session 缓存 接口 URL ...

  3. Ehcache(2.9.x) - API Developer Guide, Transaction Support

    About Transaction Support Transactions are supported in versions of Ehcache 2.0 and higher. The 2.3. ...

  4. UITableView详解(转)

    首先.对UITableView进行讲解,下面有对它进行实际的应用 UITableView 显示大型内容的列表 单行,多列 垂直滚动,没有水平滚动 大量的数据集 性能强大,而且普遍存在于iPhone的应 ...

  5. 仿QQ注册验证码的实现。

    最近发现一些网站的验证码全部换成了“极验”和“点触”的,发现QQ的注册也是与“点触”的相似.就想尝试实现一个. 先上效果图: 下面贴上主要思路及代码: 第一步:得到常用汉字列表 public stat ...

  6. NAT地址转换原理全攻略

    NAT转换方式及原理 在NAT的应用中,可以仅需要转换内部地址(就是“内部本地址”转换成“内部全局地址”),这是最典型的应用,如内部网络用户通过NAT转换共享上网:也可以是仅需要转换外部地址(就是“外 ...

  7. 随笔之Android平台上的进程调度探讨

    http://blog.csdn.net/innost/article/details/6940136 随笔之Android平台上的进程调度探讨 一由来 最近在翻阅MediaProvider的时候,突 ...

  8. JAVA UDP网络编程学习笔记

    一.UDP网络编程概述 采用TCP协议通信时,客户端的Socket必须先与服务器建立连接,连接建立成功后,服务器端也会持有客户端连接的Socket,客户端的Socket与服务器端的Socket是对应的 ...

  9. apktool反编译工具

    几个报错的解决办法 apktool反编译时经常会出现下面的信息 Input file was not found or was not readable. Destination directory ...

  10. ### 学习《C++ Primer》- 9

    Part 9: 模板与泛型编程(第16章) // @author: gr // @date: 2016-03-18 // @email: forgerui@gmail.com 1. 模板参数 类型模板 ...