Cソースファイルからコメント行だけを削除する

難しそうですがとりあえずまずは仕様を満たすように我武者羅に実装してみます。

#include <stdio.h>

/*
コメントの除去プログラム
// 
*/

// /*

int main (void) {
    int c;                // 読み込む文字を格納
    int linebegin    = 0; // 始めの/フラグ用
    int linecomment  = 0; // 一行コメント用のフラグ
    int linescomment = 0; // 複数行コメント用のフラグ
    int linesend     = 0; // 複数行コメント用の終了タグフラグ
    
    while((c=getchar()) != EOF){
        // 一行コメントの場合、改行コードまでを削除
        if ( linecomment ) {
            if ( c == '\n') {
                putchar(c);
                linecomment = 0;
            }
            continue;
        }
        
        // 複数行コメントの場合、*/が見つかるまで削除
        if ( linescomment ) {
            if ( linesend ) {
                if ( c == '/' ) {
                    linescomment = 0;
                }
                else {
                    putchar('*');
                }
                linesend = 0;
            }
            else {
                if ( c == '*' ) {
                    linesend = 1;
                }
            }
            continue;
        }
        
        // 一行、複数行なのかをチェックする
        if ( linebegin ) {
            if ( c == '/' ) {
                linecomment = 1;
            }
            else if ( c == '*' ) {
                linescomment = 1;
            }
            else {
                putchar('/');
                putchar(c);
            }
            linebegin = 0;
            continue;
        }
        
        // コメント開始/を発見したら一行コメントなのか複数コメントなのかを
        // チェックするためのフラグをONにする
        if ( !linebegin ) {
            if ( c == '/' ) {
                linebegin = 1;
                continue;
            }
        }
        
        /* ここで出力 */putchar(c);/* うまく表示されるかな?*/
    }
    
    
    return 0;
}

//
/**/
$ cat main.c | main
#include <stdio.h>





int main (void) {
    int c;                
    int linebegin    = 0; 
    int linecomment  = 0; 
    int linescomment = 0; 
    int linesend     = 0; 
    
    while((c=getchar()) != EOF){
        
        if ( linecomment ) {
            if ( c == '\n') {
                putchar(c);
                linecomment = 0;
            }
            continue;
        }
        
        
        if ( linescomment ) {
            if ( linesend ) {
                if ( c == '/' ) {
                    linescomment = 0;
                }
                else {
                    putchar('*');
                }
                linesend = 0;
            }
            else {
                if ( c == '*' ) {
                    linesend = 1;
                }
            }
            continue;
        }
        
        
        if ( linebegin ) {
            if ( c == '/' ) {
                linecomment = 1;
            }
            else if ( c == '*' ) {
                linescomment = 1;
            }
            else {
                putchar('/');
                putchar(c);
            }
            linebegin = 0;
            continue;
        }
        
        
        
        if ( !linebegin ) {
            if ( c == '/' ) {
                linebegin = 1;
                continue;
            }
        }
        
        putchar(c);
    }
    
    
    return 0;
}


うまくできました。

なるべく各チェックがわかりやすくなるように処理を書いてみました。

多分熟練したプログラマが実装すればもっとシンプルに書けるんだろうとは思います。

ということでちょっとWeb漁ってみたところこんな削除プログラムを発見しました。

#include<stdio.h> 
#include<stdlib.h> 
int main(int argc,char*argv[])
{
    FILE* fp;
    char* buf;
    int reteral=0;
    size_t i,len; 

    if (argc>1&&(fp=fopen(argv[1],"r"))){
        fseek(fp,0,SEEK_END);
        len=ftell(fp);
        buf=(char*)malloc(len);rewind(fp);
        fread(buf,len,1,fp);
        fclose(fp); 

        for(i=0;i<len;i++) {
            if(!reteral&&buf[i]=='/'&&(i+1)<len&&buf[i+1]=='/') {
                i+=2; 
                while(i<len&&buf[i]!='\n')i++;
            } else if(!reteral&&buf[i]=='/'&&(i+1)<len&&buf[i+1]=='*') {
                i+=2; 
                while(i<len&&!(buf[i]=='*'&&(i+1)<len&&buf[i+1]=='/'))i++;
                i+=2;
            } else if((buf[i]=='\"'||buf[i]=='\'')&&(buf[i-1]!='\\')) {
                reteral=!reteral;
            }
            putchar(buf[i]);
        }
    }
    return 0;
} 

ここでは対象がファイルなので、バッファリングしてからスキャンするという方式で実装しています。処理対象の長さが不明(ストリーミングなど)の場合は、1文字ずつ読み込んで状態遷移する方式にするのが一般的。

http://c-kadai.sakura.ne.jp/index.php?itemid=13

なるほど、ですね。

一旦中身を全て配列に読み込んでから処理していますね。

全部読み込んでいるので現在の配列の添え字+1を見れば//なのか/*なのかを判別できるので割りとシンプルに書けるといったところでしょうか。

ただこの記事の人も指摘してるように長さが不明なときは一文字ずつ処理するのが正しいようです。