去除c中的注释

最近老师给了一道题啊...叫我们写一个扫描器,让它可以去除空白,然后可以去除注释,把结果导入另外的文件里啊....


答:空白就检查吧。注释用stack匹配/**/检查//去行

 

去掉C/C++程序代码中的注释

C知识点 2009-12-27 17:36:04 阅读106 评论0 字号:

程序员面试宝典的题目解析方法基本上没什么问题,但是答案中有很多纰漏,甚至有些事错误的,这是很多有一定编程经验的程序员看过那本书得出的共同结论。

有错误不怕,我们学习的人一定要懂,毕竟那不是教材嘛,自己要把c基础打好,上面有很多经典问题供我们思考,而且会对我们面试有很大好处。不过读者一定要相信,这本书不可能提高你的C基础水平。

下面是第45页的一个题目,其中有一处错误,我将那句话注释掉了,写了一句正确的代码,且给了原因分析,编译通过,可以去掉程序代码中的注释语句。如果还有不严谨的部分,盼指为幸!

===============================================================================

        下面的代码比较复杂一些,请分段思考。

        另外要联系思考:在""和''中间的双斜杠中第一个斜杠在"和'后面判断就跳过了,第二次是让case语句来处理,然而case的第一个if就是依靠前面是否已经探测到过"或',探测到了的话,要跳过。

        还要考虑到非注释中的单引号''中间只能有一个/,否则C语句非法。

===============================================================================

/********************************************************
 功能:去除C/C++中的注释
 输入:指向C/C++程序代码的指针
 来源:程序员面试宝典第45页
 注意:①要考虑到""或' '中的//和/*,//和/*的嵌套关系。
    ②单引号、双引号中的//是两个字符,第一个字符在单引号的case语句中跳过了,
    第二个字符则在case '/'中处理。
 *********************************************************/

#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) /*当指针没有到达文件末尾   /r///***///*/,故意带这些不规范的符号的,因为调试就用这个代码,哈哈。*/
 {
  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; //len = p-sq_start; p++;
     if (len == 2 && *(sq_start+1) == '//') //这个是将遇到'//'的情况排除
     {
      continue; //忽略单引号中单独存在//的时候,即不再往下处理。
     }
     
     sq_start = NULL; //否则将sq_start置位为NULL,C语言中单引号内最多只可能有一个字符而已,不要思考复杂了哦len == 0 或 1 或 2
    }
    /*******************************以上是没有遇到过双引号或//或/*的时候*******************************/
    break;
   }
   
   case '/"': /*处理双引号,其实只也是为了排除"//"的情况,否则不需要有这个情况判断,注意第二个斜杠不在这里判断*/
   {
    if (sq_start || lc_start || bc_start) //当遇到过单引号、//或/*的时候,则不需要处理
    {
     p++;
     continue;
    }
    /*****************以下是没有遇到过单引号或//或/*的时候*****************/
    if (dq_start == NULL) /*如果没有遇到过双引号*/
    {
     dq_start = p++; //标记遇到了双引号
    }
    else if (*((p++) -1) =='//') //双引号中的/也不需要处理。
    {
     continue;
    }
    printf("hello // world?? /**/"); //这种情况呢?怎么办?——这个情况会在遇到/是的第一个if语句被跳过
    
    
    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
    { //其它情况,再去判断下一个是什么符号——注意:C程序可以有其他情况吗?这句话我认为永远不可能执行到。
     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; /*这两行本程序每次case后面都紧跟,就是忽略过的意思*/
    }
    
    c = *(p - 1);
    //如果遇到过双斜杠,清空双斜杠本身和到n前面的那个字符,p指向下一个字符,/r是回车符(光标退回到最前面),要忽略。有这个情况吗???
    memset(lc_start, ' ', (c == '/r'? ((p++) -1) : p++) - lc_start);
    lc_start = NULL;
    break;
   }
   
   default:
    p++;
    break;
  }
  /****************************************************
  如果遇到双斜杠,这个if语句存在的意义在于万一最后
  一行代码是带有双斜杠但没有给换行符/n的,也要清除掉。
  不带文件末尾的双斜杠的行尾一定有/n,这不是代码中写的
  /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;
}

 

二叉树——根据遍历结果,画出对应的二叉树

数据结构 2009-12-26 12:24:49 阅读79 评论0 字号:

这道题目很经典,具体如下:

已知遍历结果如下,试画出对应的二叉树:

前序:A B C E H F I J D G K

中序:A H E C I F J B D K G

解题要点:

1、前序、中序、后序——都针对中间那个节点而言(根节点也是中间的节点)。

前序,指先遍历中间节点,然后左,然后右。

中序,指左——中——右。

后序,指右——中——左。

2、根据两种不同序列的遍历方法,便可画出二叉树。

解题答案如下:(对照着看会好理解这道题目一些的)

二叉树——根据遍历结果,画出对应的二叉树 - canlynet - DC的嵌入式笔记

解题思路:

1、前序中序都首先找出A,推断出:A没有左孩子(否则中序要先遍历其左孩子)。A为根节点(前序先遍历中节点)。

2、A没有左孩子,一定就只有右孩子了(否则哪来那么多后面的字符)。右孩子一定是B,根据前序结果(前序先遍历中节点)。

3、中序的第二个不是B,说明B一定有左孩子(中序先遍历左孩子)。既然有左孩子,那么在前序的B后面那位一定是啦——C,C有左孩子吗?一定有,否则中序的A后面就跟C啦。那么前序的C后面的E一定是C的左孩子(还是深入讲一下:前序是先遍历左,但左还有左的时候,得先遍历左的左,一直深入下去,找到最终的那个左节点,这个一定要明白,否则此题无法解开)。

4、同理:E也有左孩子,就是前序的E后面那位:H!好,既然中序的A后面是H,H一定没有左孩子啦。

5、下面怎么办呢?前序:H后面是F,中序:H后面的E、C已经确定了位置了,再后面是I,怎么办?

6、注意:中序H后面是E,说明H没有右孩子啊!(中序遍历方法!!)同理,E后面是C说明E也没有右孩子,C后面呢,是B吗?如果是,那么C也没有,惊喜了吧,中序当中C后面是I,说明C有右孩子,是谁呢?

7、既然C后面有右孩子,那么在前序中H后面的F一定就是我们要找的右孩子啦(因为前序是找中节点,然后找左孩子,左孩子中又先找中间节点,再找左孩子的左孩子……中——左——右,嵌套思想)。

8、F确定好位置后,那么考虑F有左孩子吗?如果没有,中序的C后面应该跟F,但是中序的C后面跟I,说明F有左节点,那么前序找完F一定要找到这个左节点了,就是说前序的F后面的I一定是F的左节点。

9、I有左孩子吗?中序的C后面跟的是I,说明I就是最终的左孩子了。

10、F有右孩子吗?如果没有,中序I的下一位应该是B,可是并非,所以它有右孩子,那么前序的I后面的J一定是这个右孩子。

11、J有左孩子吗?如果有,中序的F后面不能是J,可是并非如此,所以没有了。J有右孩子吗?如果有,中序的已经找过J啦,下一位就是找右孩子,所以看中序的J后面那一位,发现是B,B不是已经确定好位置了吗,所以啊,没有啦。回到B节点了。

12、前序的J后面的D一定就是B的右孩子(因为回到B了嘛,前序要先把B的右孩子当中节点找到,再去找右孩子的左孩子,再把左孩子当中节点找到……嵌套啊)。

13、D有左孩子吗?如果有,中序的B后面就不可能是D(因为中序要先找到最最最最最左边的那个啊)。所以没有。既然没有,那么前序就要找D的右孩子啦,那就是前序的D后面那个G。

14、G有左孩子吗?如果没有的话,中序的D后面一定是G啦,可是不是,所以它有左孩子,这个左孩子就是前序在找到G后面该找到的那位了,就是K。

OK,二叉树的图画出来了。

总结二叉树查找关键点:

1、每次都要假设,假设了立即推翻,如果假设有,发现不能成立的话,就推翻了这个假设,即确定了一个节点的存在与否。

2、一定要明白前序、中序、后序的遍历方法,要透彻的领悟到嵌套的思想,比如找左孩子,结果找到的左孩子还有左孩子,那么要先找左孩子的左孩子,如果左孩子的左孩子还有左孩子,那么……以此类推。

3、假设一定就一次,便可深入,否则就带有一定的猜测,然后证实的成分了,容易晕掉。

4、每次确定好一个位置都是100%确定的,所以画出来,不要犹豫,然后在前序、中序上做相应标记,知道自己下一个该确定那个字符的位置了。

5、解这个题目,一定不能着急,心要稳,别深入,没有更好的深入的方法(我没找到而已,哈哈)。每次找到一个节点就直接问,他有左孩子吗?如果有,那么在中序的查找中一定就该找他了,结果发现不是,所以推翻。如果没有,那么在前序一定就该找到这个节点了。方法就这一个而已,可以解开题目。

6、这个方法对后序查找也生效。

数据结构经典问题——出栈顺序

数据结构 2009-12-26 11:28:59 阅读109 评论2 字号:

对于数据结构的问题,如果思路稍有不对,就容易陷入逻辑混乱。我希望自己对数据结构的理解,能够给大家一点帮助。我会将所有我有过心得的问题在我的博客上写出来,欢迎大家浏览,如果有什么不对的地方,还请大家指正,有问题可以给我留言,我会尽量解决,谢谢。

声明一下我写博客的初衷:不是炫耀,而是回报。因为我在计算机方面的知识好多都从网上找到答案,因此我也

将自己搜寻整理的材料,自己写的材料,展示到网上,算是尽一份力吧。

一个经典问题如下(不愿意看思路的可以直接看红色字体部分):

一个栈的入栈序列是a,b,c,d,e则栈的不可能的输出序列是:()

A edcbd         B decba           C dceab            D abcde

栈之根本——先进后出(first in,lastout)初次接触到这个问题的人,或许会认为入栈abcde,所以出栈只能是edcba所以BCD都不对。

其实是这个问题描述有歧义,应该是分段入栈的顺序,也就是说,可能先入栈a,取出a,入栈b,取出b……,所以D也是可能的。

知道这个意思了以后,就要明确这个问题的矛盾根本所在:第一次出栈d,说明什么?说明a,b,c一定早已入栈(入栈顺序决定的)。那么在出栈d以后,a,b,c的出栈顺序一定是c,b,a,而不用理会中间穿插着出栈了d后面的字符(因为可以再入栈,再出栈嘛)。所以立即选中C,不用犹豫,理由简单:d出栈了,abc一定已经入栈,那么abc只能以cba的顺序出栈,C不符合,OK!This problem is so esay, Thanks for my teacher Wang Shanshan.

数据结构中的思路一定要单一,要简明,抓住问题根本,万不可考虑过多,试探法只有在不知道解题思路的情况下去尝试,尝试多了,你会发现,逻辑容易混乱,俗称“我晕”!

 

去除C/C++中的注释

文章分类:C++编程

C代码 复制代码
  1. /********************************************************  
  2.     功能:去除C/C++中的注释  
  3.     输入:指向C/C++程序代码的指针  
  4.     来源:程序员面试宝典第45页  
  5.     注意:①要考虑到""或' '中的//和/*,//和/*的嵌套关系。  
  6.           ②单引号、双引号中的//是两个字符,第一个字符在单引号的case语句中跳过了,  
  7.           第二个字符则在case '/'中处理。  
  8.  *********************************************************/  
  9.   
  10. #include <stdio.h>   
  11. #include <stdlib.h>   
  12. #include <fcntl.h>   
  13. #include <string.h>   
  14. #include <unistd.h>   
  15. #include <sys/types.h>   
  16.   
  17. void remove_comment(char *buf, size_t size)   
  18. {   
  19.     char *p, *end, c;          //p-动态移动的字符指针,end-指向文件末尾的字符指针,c-存储没一个p指向的字符   
  20.     char *sq_start, *dq_start; //sq_start-单引号开始位置(single),dq_start-双引号开始(double)   
  21.     char *lc_start, *bc_start; //lc_start-//的开始位置,bc_start-/*的开始位置   
  22.     size_t len;                //记录某符号结束和开始的位置之差(长度,偏移量)   
  23.        
  24.     p = buf;   
  25.     end = p + size;   
  26.     sq_start = dq_start = NULL;   
  27.     lc_start = bc_start = NULL;   
  28.        
  29.     while (p < end) /*当指针没有到达文件末尾   /r///***///*/,故意带这些不规范的符号的,因为调试就用这个代码,哈哈。*/   
  30.     {   
  31.         c = *p;     //用字符变量c存储指针指向的字符   
  32.            
  33.         switch (c) //根据c的值做相应处理   
  34.         {   
  35.             case '/''/*处理单引号,其实只是为了排除'//'的情况,否则不需要有这个情况判断*/  
  36.             {   
  37.                 if (dq_start || lc_start || bc_start) //当遇到过双引号、//或/*的时候,则不需要再判断'//'的情况了。   
  38.                 {   
  39.                     p++;   
  40.                     continue//继续下一个,对while而言的   
  41.                 }   
  42.                 /*******************************以下是没有遇到过双引号或//或/*的时候*******************************/   
  43.                 if (sq_start == NULL) /****否则:如果未遇到单引号****/  
  44.                 {   
  45.                     sq_start = p++; //start指向单引号的开始位置,p指向下一个(分两句理解)   
  46.                 }   
  47.                 else /*如果遇到过单引号,sq_start指向单引号开始位置*/  
  48.                 {   
  49.                     len = (p++) -sq_start; //len = p-sq_start; p++;   
  50.                     if (len == 2 && *(sq_start+1) == '//') //这个是将遇到'//'的情况排除   
  51.                     {   
  52.                         continue//忽略单引号中单独存在//的时候,即不再往下处理。   
  53.                     }   
  54.                        
  55.                     sq_start = NULL; //否则将sq_start置位为NULL,C语言中单引号内最多只可能有一个字符而已,不要思考复杂了哦len == 0 或 1 或 2   
  56.                 }   
  57.                 /*******************************以上是没有遇到过双引号或//或/*的时候*******************************/  
  58.                 break;   
  59.             }   
  60.                
  61.             case '/"'/*处理双引号,其实只也是为了排除"//"的情况,否则不需要有这个情况判断,注意第二个斜杠不在这里判断*/  
  62.             {   
  63.                 if (sq_start || lc_start || bc_start) //当遇到过单引号、//或/*的时候,则不需要处理   
  64.                 {   
  65.                     p++;   
  66.                     continue;   
  67.                 }   
  68.                 /*****************以下是没有遇到过单引号或//或/*的时候*****************/   
  69.                 if (dq_start == NULL) /*如果没有遇到过双引号*/  
  70.                 {   
  71.                     dq_start = p++; //标记遇到了双引号   
  72.                 }   
  73.                 else if (*((p++) -1) =='//'//双引号中的/也不需要处理。   
  74.                 {   
  75.                     continue;   
  76.                 }   
  77.                 printf("hello // world?? /**/"); //这种情况呢?怎么办?——这个情况会在遇到/是的第一个if语句被跳过   
  78.                    
  79.                    
  80.                 dq_start = NULL; //如果双引号中不是//,标记为NULL   
  81.                 /*****************以上是没有遇到过单引号或//或/*的时候*****************/  
  82.             }   
  83.                
  84.             case '/'//斜杠,注意这个斜杠也可以是'//',"//",//,/*/中的第二个斜杠,但会在下面第二行代码中被忽略掉   
  85.             {   
  86.                 if (sq_start || dq_start || lc_start || bc_start) //如果是单引号、双引号、斜杠、/*的后面   
  87.                 {   
  88.                     p++;   
  89.                     continue;   
  90.                 }   
  91.                 /***********************下面是遇到注释//或/*的时候****************************/   
  92.                 c = *(p + 1); //否则c取p指向字符的下一个字符   
  93.                 if (c == '/'//遇到了双斜杠   
  94.                 {   
  95.                     lc_start = p; //标记双斜杠的开始   
  96.                     p += 2; //p指向双斜杠后面的字符   
  97.                 }   
  98.                 else if (c == '*'//遇到了/*   
  99.                 {   
  100.                     bc_start = p; //标记/*的开始   
  101.                     p += 2; //p指向/*后面的字符   
  102.                 }   
  103.                 /*************************上面是遇到注释//或/*的时候**************************/   
  104.                 else  
  105.                 { //其它情况,再去判断下一个是什么符号——注意:C程序可以有其他情况吗?这句话我认为永远不可能执行到。   
  106.                     p++;    
  107.                 }   
  108.             }   
  109.                
  110.             case '*'//星号,同斜杠,但少了如果遇到/*的情况,因为遇到这种情况后,要判断是不是遇到结束的地方*/了   
  111.             {   
  112.                 if (sq_start || dq_start || lc_start) //如果是单引号、双引号、斜杠、/*的后面   
  113.                 {   
  114.                     p++;   
  115.                     continue;    
  116.                 }   
  117.                    
  118.                 if (*(p + 1) != '/'//如果星号后面紧跟的不是斜杠,那么忽略过。   
  119.                 {   
  120.                     p++;   
  121.                     continue;   
  122.                 }   
  123.                    
  124.                 p += 2; //否则p指向斜杠后面那个字符。注意下面的清空语句,p指向的那个字符并不会被清除。   
  125.                 memset(bc_start, ' ', p-bc_start); //清空/* …… */中间的内容包括注释符号本身。   
  126.                 bc_start = NULL;   
  127.                 break;   
  128.             }   
  129.                
  130.             case '/n'/*换行符,主要处理遇到双斜杠时,需要清除双斜杠到/n的前面的字符*/  
  131.             {   
  132.                 if (lc_start == NULL) //如果还没有遇到双斜杠,那么忽略   
  133.                 {   
  134.                     p++;   
  135.                     continue/*这两行本程序每次case后面都紧跟,就是忽略过的意思*/  
  136.                 }   
  137.                    
  138.                 c = *(p - 1);   
  139.                 //如果遇到过双斜杠,清空双斜杠本身和到n前面的那个字符,p指向下一个字符,/r是回车符(光标退回到最前面),要忽略。有这个情况吗???   
  140.                 memset(lc_start, ' ', (c == '/r'? ((p++) -1) : p++) - lc_start);    
  141.                 lc_start = NULL;   
  142.                 break;   
  143.             }   
  144.                
  145.             default:   
  146.                 p++;   
  147.                 break;   
  148.         }   
  149.         /****************************************************  
  150.         如果遇到双斜杠,这个if语句存在的意义在于万一最后  
  151.         一行代码是带有双斜杠但没有给换行符/n的,也要清除掉。  
  152.         不带文件末尾的双斜杠的行尾一定有/n,这不是代码中写的  
  153.         /n而是我们的回车键换行操作写入文件的。  
  154.         *****************************************************/  
  155.         if (lc_start)    
  156.         {   
  157.             memset(lc_start, ' ', p - lc_start);   
  158.         }   
  159.     }   
  160. }   
  161.   
  162. /**********************************************  
  163.             main函数的开始  
  164. ***********************************************/  
  165. int main (int argc, char *argv[])   
  166. {   
  167.     int fd, n;   
  168.     char buf[102400];   
  169.        
  170.     if (argc != 2)   
  171.     {   
  172.         printf("command error: Input as ./command <file>/n");   
  173.     }   
  174.        
  175.     fd = open(argv[1], O_RDONLY); /*只读打开*/  
  176.     if (fd == -1)   
  177.     {   
  178.         return -1;   
  179.     }   
  180.        
  181.     n = read(fd, buf, sizeof(buf));   
  182.     if (n == -1 || n == 0)   
  183.     {   
  184.         close(fd);   
  185.         return -1;   
  186.     }   
  187.     printf("test/n");   
  188.     remove_comment(buf, n);   
  189.     *(buf + n) = '/0';   
  190.     printf("%s", buf);   
  191.     close(fd);   
  192.        
  193.     return 0;   
  194. }  
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值