ステップカウンターの作成について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
ruki

ステップカウンターの作成について

#1

投稿記事 by ruki » 16年前

はじめまして。
現在ステップカウンターを作成しています。
環境はWindowsXP/コンパイラはBorlandC++ Compiler 5.5/Turbo Debuggerです。
言語はCです。

今の状態では、自分がカウントしたいものを手動入力し、その結果もコマンドプロンプト上でしか
見れません。

そこで、このプログラムを以下のように改良したいのです。
・特定のフォルダから.cの拡張子のものだけを抜き出しステップをカウントする。
・その結果をtxt形式で出力する。

ステップ数をカウントするところまでは、自力でできたのですが
上のようにするとなるとどうすればいいのか全く分かりません。

現在Cを勉強し始めたばかりで、下のコードも検索しながら手探りで書いたものですので
おかしいところ等あるかもしれません。

リンク先は、Yahooの知恵袋で私が質問したものです。
マルチポストになってしまうかと思い、リンクしました。


書いたコードは以下のようになっています。
#include <stdio.h> 
#include <stdlib.h> 
#include<ctype.h> 
int main() 
{ 
  FILE *fp; 
  int brank; 
  int coment; 
  int c; 
  int iscoment;
  char *p; 
  char filename[100]; 
  char buffer[500]; 

  brank = coment = 0; /* 初期化 */ 

  printf("ファイル名を入力してください。"); 
  scanf("%s",filename); 

  if ((fp = fopen(filename, "r")) == NULL) { /*ファイルが開けなかったら終了*/ 
  fprintf(stderr, "ファイルを開くことができません!\n"); 
  exit(2); 
  } 

  while (fgets(buffer,sizeof(buffer),fp)!=NULL){ 
    iscoment=0; 
    p=buffer; 
    while (*p!='\0') { /* ファイルの終わりまで1字ずつ読む */ 
      if (!isspace(*p)){ 
         iscoment=1; 
         break; 
      } 
      p++; 
    } 
    if (iscoment){ 
      coment++; 
    } 
    else{ 
      brank++; 
    } 
  } 

  printf("ファイル名:%s\n",filename); 
  printf("実行行%u,空白行%u",coment,brank); /* 結果を表示 */ 

  fclose(fp); /* ファイルを閉じる */ 

  return 0; 
}

toyo

Re:ステップカウンターの作成について

#2

投稿記事 by toyo » 16年前

フォルダのファイル名を取得するなら BCCでは以下のような方法があります。
#include <stdio.h>
#include <dirent.h>

int main(void)
{
        struct dirent* f;
        DIR* fd;
        fd = opendir(".");
        if (fd == NULL) {
            printf("open dir error\n");
            return 0;
        }
        while (f = readdir(fd)) {
            printf("%s\n", f->d_name);
        }
        closedir(fd);
        return 0;
}

non

Re:ステップカウンターの作成について

#3

投稿記事 by non » 16年前

_findfirstと_findnextを使う方法もあります。
10日ほど前に同じような質問があったときの解答例を再度UPします。
これは、ファイルリストを作るために、ファイル名を配列に入れるようになってます。
#include <stdio.h>
#include <string.h>
#include <io.h>

char f_name[100][20];//ファイルの最大を100個、文字数を20文字までにしました。
int f_no=0;

void find_file( char *filename ){
    struct _finddata_t c_file;
    long   hFile;

    if( (hFile = _findfirst( filename, &c_file )) !=-1 ){
        strcpy(f_name[f_no++],c_file.name);
        while( _findnext( hFile, &c_file ) == 0 )
            strcpy(f_name[f_no++],c_file.name);
        _findclose( hFile );
    }
}
int main(void)
{
    int i;
    find_file("*.c");
    for(i=0;i<f_no;++i)
        puts(f_name);
    return 0;
}

ruki

Re:ステップカウンターの作成について

#4

投稿記事 by ruki » 16年前

_findfirstを使った方法で無事作成することができました。
txt形式での出力も自力で何とかできました。

お二方とも分かりやすいコードを教えてくださりありがとうございました。

また何か分からないことがあったら聞きにきたいと思います。

ruki

Re:ステップカウンターの作成について

#5

投稿記事 by ruki » 16年前

先日は、お世話になりました。
先輩に先日のステップカウンターのプログラムを見せたところ、いくつか改良するようにといわれました。

一つ目にフリーのソフト等にあるようなフォルダを参照するような窓を出し
そこで参照したフォルダの中身をすべて見るようにすること。
二つ目にコメント文のカウントは別でするようにし、実行行のカウントからはぶくこと。

以下は実際に自分なりに組んでみたコメントカウントのプログラムです。
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

void com( char *name ) {
    FILE *fp;
    int c;
    char filename[100];
    char buffer[500];
    char *buf;
    int come;

    fp = fopen( name, "r" );

    while ( fgets( buffer,sizeof( buffer ),fp )!=NULL ) {
        while (  c= getc(buffer)!=EOF) {     // ファイルの終わりまで1字ずつ読む
        
            if (c=='/') {    
                c= getc(buffer);
                if (c=='/') {
                    come++;
                    break;    
                }
            }
            
            else if ( *p=='/' && *p+1=='*' ) {
                while ( *p=='*' && *p+1=='/' ){
                    come++;
                    break;    
                }
            }
    }
    printf("コメント行%d",come);        
}

int main(){

    com("test.c");        

    return 0;
}
//や/*~*/の含まれる文章がきたらカウントすればいいと思い上のようにしたのですが

エラー E2288 coment_test3.c 17: -> か ->* の左には構造体のポインタが必要(関数 com )
エラー E2288 coment_test3.c 17: -> か ->* の左には構造体のポインタが必要(関数 com )
警告 W8075 coment_test3.c 17: 問題のあるポインタの変換(関数 com )
エラー E2288 coment_test3.c 20: -> か ->* の左には構造体のポインタが必要(関数 com )
エラー E2288 coment_test3.c 20: -> か ->* の左には構造体のポインタが必要(関数 com )
警告 W8075 coment_test3.c 20: 問題のあるポインタの変換(関数 com )
エラー E2451 coment_test3.c 27: 未定義のシンボル p(関数 com )
エラー E2140 coment_test3.c 37: ここでは宣言はできない(関数 com )
エラー E2141 coment_test3.c 37: 宣言の構文エラー(関数 com )
エラー E2139 coment_test3.c 42: 宣言に ; がない(関数 com )
エラー E2134 coment_test3.c 42: 複合文に } がない(関数 com )

このようにエラーがたくさん出てしまいました。
正直今回はお手上げになってしまいました。

今回質問したいことは、以下のようなものです。

・一つ目に改良しろといわれたフォルダを参照するような動作はCのみで実現することはできるのか。
・どのようにすればコメント文のみをカウントできるようになるのか。

環境は前回と一緒でWindowsXP/コンパイラはBorlandC++ Compiler 5.5/Turbo Debuggerです。
言語はCです。
Cの勉強を始めたばかりですので、分からないことだらけですが
ぜひご教示願えるとうれしいです。

御津凪

Re:ステップカウンターの作成について

#6

投稿記事 by 御津凪 » 16年前

まずはエラーの原因から。
・getc 関数の引数に buffer が指定されているが、渡さなければならない引数は fp です。
  (17~20行目のエラーはこれが原因のはず)
・27行目のエラーは文字通り、 p が定義されていません。(c ではないですか?)
・二つ目の while 文に対応する '}' が無い
  (37行目以降のエラーの原因)
次に、処理についての問題点です。
(単にエラーを対処しただけでは正常に動作しない、というのは置いといて)
・fgets と getc が混在していて、ファイルから読み込む場所が散在している。
  (混乱の元&複雑化するのでどちらかに統一したほうがいいと思います)
・*p+1 は期待通りの結果にならない。
  (*p が優先されるため、 *p+1 は 次のポインタ(文字)ではなく、ポインタの指す文字の値+1となる)
・"////////" など、 '/' が二個より多く並んでいる時の対処を行っていない
  ("////"の場合、二個のコメント行があると解釈してしまう)
・"*/" が見つかるまでの while 処理が無限ループとなる。
  (p がずっと変わらないため)
というところでしょうか。

フォルダを参照するような動作は、 C 言語のみでは出来ません。
Win32 API という Windows の機能を扱う関数群を利用する必要があります。
(こちらはある程度の知識が必要なので、ひとまず後回しにしたほうがいいと思います)

コメント文の解釈・処理は、以下のようにすれば良いと思います。
・文中に "//" が存在したら、それ以降の改行までの文字をコメントとする
  (コメント内に "/*" があっても処理しないこと)
・文中に "/*" が存在したら、 "*/" が見つかるまでの文字をコメントとする
  (コメント内に "//" があっても処理しないこと)
フォルダ選択の窓を出し、使用する方法は
ここではおなじみ(?)の猫でもわかるプログラミングのサイト内で、
http://www.kumei.ne.jp/c_lang/sdk3/sdk_276.htm
に、載っています。

toyo

Re:ステップカウンターの作成について

#7

投稿記事 by toyo » 16年前

ファイルを開くダイアログを出すにはWinAPIを使わないとだめです
#include <windows.h>
#include <stdio.h>

int main(void)
{
    OPENFILENAME ofn;
    char path[MAX_PATH] = "";

    memset(&ofn,0,sizeof(OPENFILENAME));
    ofn.lStructSize = sizeof(OPENFILENAME);
    ofn.lpstrFile = path;
    ofn.nMaxFile = MAX_PATH;
    if (!GetOpenFileName(&ofn))
    {
        printf("Error: 0x%x\n", CommDlgExtendedError());
    }
    else
    {
        printf("ファイル名: %s\n", path);
    }
    return 0;
}

御津凪

Re:ステップカウンターの作成について

#8

投稿記事 by 御津凪 » 16年前

コードを書いてみました。
toyo さんのコードと共に参考までにどうぞ。
#include <stdio.h>

// 解析モード
enum {
    PARSEMODE_NONE       = -1,  // 通常
    PARSEMODE_COM_SINGLE = 0,   // 単行コメント
    PARSEMODE_COM_MULTI  = 1,   // 複行コメント
};

void com_counter( const char *fname ) {
    FILE *fp;
    int ch,prev_ch;
    int comments[2];
    int parsemode = PARSEMODE_NONE;

    // ファイルオープン
    fp = fopen(fname, "r");
    if( !fp ){
        printf("ファイルを開けませんよ。\"%s\"\n",fname);
        return;
    }

    // 初期化
    comments[PARSEMODE_COM_SINGLE] = 0;
    comments[PARSEMODE_COM_MULTI]  = 0;
    prev_ch = '\0';

    // 解析処理
    while( (ch = getc(fp)) != EOF ){   // ファイルの終わりまで1字ずつ読む
        switch( parsemode ){           // 解析モードごとに処理を変える
            case PARSEMODE_COM_SINGLE: // 解析モード:単行コメント
                if( ch == '\n' ){
                    parsemode = PARSEMODE_NONE;
                }
                break;
            case PARSEMODE_COM_MULTI:  // 解析モード:複行コメント
                if( prev_ch == '*' && ch == '/' ){
                    parsemode = PARSEMODE_NONE;
                }
                break;
            default:                   // 解析モード:ノーマル
                if( ch == '/' ){
                    if( prev_ch == '*' ){
                        ++comments[PARSEMODE_COM_MULTI];
                        ch = '\0'; // そのままだと "/*/" を誤認してしまうので関係ない文字にする
                    }
                    else if( prev_ch == '/' ){
                        ++comments[PARSEMODE_COM_SINGLE];
                    }
                }
        }

        prev_ch = ch; // 前の文字を記憶する
    }

    // ファイルを閉じる
    fclose(fp);

    // それぞれのカウント数を出力
    printf("解析結果:"
           "  単行コメント数 :%d\n"
           "  複行コメント数 :%d\n",
           comments[PARSEMODE_COM_SINGLE],
           comments[PARSEMODE_COM_MULTI]);
}

int main( void ){

    com_counter("test.c");

    return 0;
}
# コンパイルチェックはしていませんのであしからず。

ruki

Re:ステップカウンターの作成について

#9

投稿記事 by ruki » 16年前

御津凪さんtoyoさんありがとうございました。

一度目の御津凪さんのアドバイスを見てエラーをつぶしてもカウントができなかったので
頭を抱えていたところでした。

お二方のコードを参考に自分なりに書いてみようと思います。
その際また相談に来ることがあると思いますので、よろしくお願いいたします。

本当にありがとうございます。

ruki

Re:ステップカウンターの作成について

#10

投稿記事 by ruki » 16年前

お久しぶりです。
御津凪さんのアドバイスを元に自分でプログラムを組んでみました。
#include <stdio.h>

int com_counter( const char *fname );

int main(){

  char str[500];
    
    printf("コメント数:%d",com_counter("test2.cpp"));

    return 0;
}

//コメントのみをカウントする
int com_counter( const char *fname ) {
    FILE *fp;
    unsigned single;    //単行カウント
    unsigned multi;    //複行カウント
    int count,flag;    // /であるかどうかのフラグ、複行フラグ
    int ch,prev_ch;    //文字格納、一文字前記憶
    unsigned sum;

    //初期化
    single=multi=count=flag=0;
    prev_ch = '\0';
    
    //ファイルオープン
    fp = fopen(fname, "r");
    
    // 解析処理
    while( (ch = getc(fp)) != EOF ){   // ファイルの終わりまで1字ずつ読む
        if(ch !='/'&&ch!='*'&&ch!='\n'){    //コメント以外の場合再度読み込み
            continue;
        }
        if(flag>=1){    //複行フラグが立っていたら
            goto mu;    //ラベル文の位置まで飛ぶ
        }
        if(count==1){
            goto par;
        }
        else if(ch == '/'){    ///かどうかの判定
            prev_ch='/';
            count=1;
            continue;
        }
        par:
        if(prev_ch == '/'&& ch == '/' ){    
            single++;    //次の文字が/ならば単行を増やして
        }
        else if(ch == '*'){    //次の文字が*なら
            mu:    
            flag++;    //フラグを立てる
            
            if(flag==1){
            continue;    //フラグが立ったら強制的にループ文先頭まで戻す
            }
        
            if(ch=='\n'){ //複行文の中で改行されていたら複行カウント増加
                multi++;
                continue;
            }
            else if(ch=='*'){    //*がきたら記憶しておく
                prev_ch='*';            
                continue;
            }
            else if(prev_ch == '*' && ch == '/'){ //複行カウント増加
                multi++;
                flag=0;
            }
        }
        count=0;
    }                

    fclose(fp); // ファイルを閉じる

    sum=single+multi;    //単行文と複行文の合計をまとめる
    
    return sum;                    
}
このようにしてみました。
列挙体とスイッチを組み合わせたやり方がよく分からなかったので、goto文とcontinue文を使って
やってみました。
これによって
/*
abc
*/
などの文を3行と数えることができるようになりました。

現在悩んでいるのは以下のようなソースのコメントを読む際のことです。
/*
multi
multi
*/
#include<stdio.h>

int main(){

    int a=6,b; //single

    printf("%d\n",a);

    b=a/2;
    a=3;

    a+=b;    
    printf("%d\n",a);    

}
/*multi*/

//single trap
9行目のように実行文の後にコメント文が来ているとその行までコメントとみなしてしまうのです。
その対策としてif(ch !='/'&&ch!='*'&&ch!='\n')この文を入れたのですが、効果がありませんでした。

どのようにすれば、実行文の後の一行コメントを実行文として読み込めるようになるんでしょうか。
何度も質問してしまい申し訳ないですが、ご教示願えるとうれしいです。

御津凪

Re:ステップカウンターの作成について

#11

投稿記事 by 御津凪 » 16年前

お久しぶりです。

ruki さんからのコードから変更を加える場合、
・コメント中でない場合で、空白文字(半角文字とタブ文字)以外の文字が出たら
 その行は実行文であると解釈する(フラグを立てる)
 ただし、フラグを立てるときの文字で '/' がきた場合は注意すること
・単行コメントが現れた場合、その行が実行文であると解釈していた場合、カウントしない
・改行が来たら実行文フラグを降ろす
を実装すればOKだと思われます。

ruki

Re:ステップカウンターの作成について

#12

投稿記事 by ruki » 16年前

返事に間があいてしまい、申し訳ありません。
御津凪さんのアドバイスを見てプログラムを組んだところ大分精度の高いものができました。
後もう少し自分で改良を加えて正しいカウントができるものを作りたいと思います。
アドバイスを下さった方々本当にありがとうございました。

閉鎖

“C言語何でも質問掲示板” へ戻る