程序員面試寶典,如何刪除代碼的註釋

程序員面試寶典上面的題目有很多是很經典的問題,可供我們思考,而且會對我們面試有很大好處。

下面是第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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章