ステップ数についておしえてください

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
どんどんどん
記事: 7
登録日時: 5年前

ステップ数についておしえてください

#1

投稿記事 by どんどんどん » 5年前

始めまして。ソースコードの解説をお願いしたいです。
私自身、プログラミング経験1週間で入門書を一通り読んだ程度の知識です。

独学でC言語を勉強中ですが、どれだけ調べても理解できませんので解説をお願いしたいです。
以下のプログラムの内容ですが、ソースコードのコメント数と実行数を計算する。
実行行でのコメントは実行行として扱う。
スペース(全角 半角)、改行、タブのみはカウントしない。
となっています。
2回目のwhile文(このwhile文からコメントやら空白数をカウント)が理解できません。
何故、フラグを立てることでコメントかコメントじゃないかを判別できるのでしょうか?
またどの行で空白を求めているのでしょうか?
求めている事は多数ですがよろしくお願い致します。
*掲載のソースコードはテキストの模範解答になります。




コード:





#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define BUFF 1024

int main()
{
	FILE *fp;
	char fname[256];
	char str[BUFF];
	
	int len = 0;
	int i = 0;
	
	int all = 0;
	int exe = 0;
	int come = 0;
	
	int flaga = 0;	
	int flagb = 0;	
	int flagc = 0;	

	while (1) {
		printf("ステップ数をカウントします。\n");
		printf("ソ\ースファイル名:");
		
		fp = fopen(gets(fname), "r");
		if (fp == NULL) {
			perror("ファイルが存在しません\n");
			continue;
		}
		
		while ((fgets(fname, BUFF, fp)) != NULL) { //ここからどような処理をしているのかわかりません。
			len = strlen(fname);
			
			for (i = 0; i < len; i++) {		
				if (isgraph(fname[i])) {	 
					
					if (fname[i] == '/' && fname[i+1] == '/' && flagb == 0) {	
						flaga = 1;	
						
					}else if (fname[i] == '/' && fname[i+1] == '*' && flaga == 0) {	
						
					}else if (fname[i] == '/' && fname[i-1] == '*' && flagb == 1) {	
						flagb = 0;	
						
					}else{	
						
						if (flaga == 0 && flagb == 0) {	
							flagc = 1;	
						}
					}
				}
			}
			
			for (i = 0; i <len; i++) {
				if(isgraph(fname[i])){
					all++;				
					
					if (flagc == 0) {	
						come++;
					}
						
						break;
				}
			}
			
				
			
			flaga = 0;
			flagc = 0;

		}
		
		exe = all - come;
		

		printf("実行行:%d行\n", exe);
		printf("コメント行:%d行\n", come);
	}
	
	fclose(fp);
	return 0;
}




アバター
あたっしゅ
記事: 663
登録日時: 13年前
住所: 東京23区
連絡を取る:

Re: ステップ数についておしえてください

#2

投稿記事 by あたっしゅ » 5年前

まず fgets が何をしているか、わかっていますか ?


>何故、フラグを立てることでコメントかコメントじゃないかを判別できるのでしょうか?

if で、コメントかコメントじゃないか、判断しています。

> if (fname == '/' && fname[i+1] == '/' && flagb == 0) {

"//" かどうが判断。

> }else if (fname == '/' && fname[i+1] == '*' && flaga == 0) {

"/*" かどうが判断。


> }else if (fname == '/' && fname[i-1] == '*' && flagb == 1) {

"*/" かどうが判断。

> if(isgraph(fname)){

isgraph で、「スペース(全角 半角)、改行、タブ」で、ないかどうか、判断しています。
ただし、これで全角空白を判断できるかは、文字コードに依存するかな ?

この書き込みは、レスの素早さを優先したもので、細かいところで、間違っている可能性が、あるかもしれません。
VTuber:
東上☆海美☆(とうじょう・うみみ)
http://atassyu.php.xdomain.jp/vtuber/index.html
レスがついていないものを優先して、レスするみみ。時々、見当外れなレスしみみ。

中の人:
手提鞄あたッしュ、[MrAtassyu] 手提鞄屋魚有店
http://ameblo.jp/mratassyu/
Pixiv: 666303
Windows, Mac, Linux, Haiku, Raspbery Pi, Jetson Nano, 電子ブロック 持ち。

かずま

Re: ステップ数についておしえてください

#3

投稿記事 by かずま » 5年前

どんどんどん さんが書きました:
5年前
*掲載のソースコードはテキストの模範解答になります。
そのプログラムは模範解答ではありません。
・gets を使っている。
・fgets で 256バイトしかない fname を 1024バイトだとしている。
・fname[0] が '/' で fname[1] が '/' でも '*' でもない時、
 fname[-1] を見に行く。
・flaga, flagb, flagc の役割がはっきりしない。
・/* comment */ をコメント行として数えない。

そこで、こんなプログラムを書いてみました。

コード:

/*
 * コメント行数のカウント
 */
#include <stdio.h>   // printf, fgets, fopen, fclose
#include <string.h>  // strchr
#include <ctype.h>   // isgraph

#define BUFF 1024

int main(void)
{
	// ファイル名入力がないとループを終了
	while (1) {
		FILE *fp;
		char fname[256]; // ファイル名
		char str[BUFF];  // 行バッファ
		char *p;
		int i;
		int n_exe = 0;   // コメント以外の行の数
		int n_com = 0;   // コメントだけの行の数
		int in_com = 0;	 // コメント内フラグ

		printf("ステップ数をカウントします。\n"
		       "ソースファイル名:");
		if (fgets(fname, sizeof fname, stdin) == NULL) return 1;
		p = strchr(fname, '\n');
		if (p != NULL) *p = '\0';
		if (fname[0] == '\0') break;
		
		fp = fopen(fname, "r");
		if (fp == NULL) {
			perror("ファイルが存在しません\n");
			continue;
		}
		
		while ((fgets(str, BUFF, fp)) != NULL) {
			int is_exe = 0; // コメント以外の行確定フラグ
			int is_com = 0; // コメントだけの行確定フラグ
			int c_com = 0;  // 行内の /* と */ の数
			for (i = 0; str[i] != '\0'; i++) {		
				if (!in_com) {
					if (str[i] == '/') {
						if (str[i+1] == '/') {
							is_com = 1; break;
						}
						if (str[i+1] == '*') {
							c_com++; in_com = 1; i++;
						}
						else {
							is_exe = 1; break;
						}
					}
					else if (isgraph((unsigned char)str[i])) {
						is_exe = 1; break;
					}
				}
				else if (str[i] == '*' && str[i+1] == '/') {
					c_com++; in_com = 0; i++;
				}
			}
			if (is_exe)
				n_exe++;
			else if (is_com || in_com || c_com)
				n_com++;
		}
		printf("実行行:%d行\n", n_exe);
		printf("コメント行:%d行\n", n_com);
		fclose(fp);
	}
	return 0;
}
これでもまだ不完全です。
行末の ¥ による継続行の処理をしていません。

かずま

Re: ステップ数についておしえてください

#4

投稿記事 by かずま » 5年前

そのプログラムが模範解答ではない理由をさらに追加します。
・ファイルを複数指定した場合、最後のファイルしか fclose しない。
・fname は char で負の値の可能性があり、isgraph に渡すのは問題あり。
・while (1) の無限ループを終了する手段がない。

どんどんどんさん、回答を無視しないで、
何らかの応答をお願いします。

かずま

Re: ステップ数についておしえてください

#5

投稿記事 by かずま » 5年前

訂正
・fname[ i] は char で負の値の可能性があり、isgraph に渡すのは問題あり。
オフトピック

コード:

[i] をタグと解釈するのはやめて欲しい。
このせいで過去の記事を検索しても、ダメに
なっているものがたくさんあり、残念です。
BBCode で、[i]: OFF とかできないのかな?

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: ステップ数についておしえてください

#6

投稿記事 by みけCAT » 5年前

オフトピック
かずま さんが書きました:
5年前
[​i] をタグと解釈するのはやめて欲しい。
このせいで過去の記事を検索しても、ダメに
なっているものがたくさんあり、残念です。
記憶が正しければ以前は閉じタグの[/i]がなければタグとみなされなかった気がしますが、
閉じタグがなくてもタグとみなされるように改悪されてしまったようですね…
なんででしょう?
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

どんどんどん
記事: 7
登録日時: 5年前

Re: ステップ数についておしえてください

#7

投稿記事 by どんどんどん » 5年前

皆様、たくさんのご返答ありがとうございました。
特にかずま様、わざわざサンプルコードまで作成して頂きありがとうございます。度々のお願いになりますがお手すきであればそのコードを解説をしていただけないでしょうか?

私の掲載している回答が間違っているとの事ですが、私自身、IT企業へ営業職として転職し自身のスキルアップの為、エンジニアの皆さんが受ける研修を少しだけ受けており、掲載した模範解答は先輩エンジニアから頂いた回答になり多分先輩が作成されたもので、何かの問題集に掲載されていたとかではないと思います。
正直、私のレベルですと何が間違いで、何が正解かわかっていませんので、今後ともそういった指摘を頂けるとありがたいです。

かずま

Re: ステップ数についておしえてください

#8

投稿記事 by かずま » 5年前

どんどんどん さんが書きました:
5年前
私の掲載している回答が間違っているとの事ですが、
その「模範解答」のプログラムは、警告が出るかもしれませんが、
とりあえずコンパイルはできるはずです。
実行行とコメント行を数えるプログラムですが、本当に動くか
どうか、いくつかのサンプルデータでテストしてみましたか?

test.c

コード:

#include <stdio.h>

int main(void)
{
	// コメント
	printf("hello, world\n");
	return 0;
}
実行を開始して、このプログラムのファイル名を入力すると
次のように表示されます。

コード:

ステップ数をカウントします。
ソースファイル名:test.c
実行行:6行
コメント行:1行
ステップ数をカウントします。
ソースファイル名:^C
8行のプログラムで、空行が 1行あるので、
この結果は正しいことが分かりませす。
ところが、コメント行を「/* コメント */」に変えると

コード:

実行行:7行
コメント行:0行
と表示されます。
次のように変えても、全部実行行にカウントされます。

コード:

/*
 * コメント
 */
とにかくたくさんのテストデータで動作を確認するべきです。

コード:

	printf("hello, world\n"); // コメント
	printf("hello, world\n"); /* コメント */
	/* コメント */ printf("hello, world\n");
flagb なんか、宣言で 0 に初期化されたまま、
どこにも 1 にするところがありません。
char str[BUFF]; の str も使用されていません。

"ソースファイル名:" が "ソ\ースファイル名:" になっていますが、
環境(OSやコンパイラ) が何かを教えてもらえませんか?

どんどんどん さんが書きました:
5年前
お手すきであればそのコードを解説をしていただけないでしょうか?
最初の while (1) は複数のファイルを扱うためではなく
ファイルがオープンできない時にやり直すためのものだ
ということが分かったので、コードを書き直してみました。

コード:

/*
 * 実行行とコメント行の行数カウント
 */
#include <stdio.h>   // printf, fgets, fopen, fclose, perror
#include <string.h>  // strchr
#include <ctype.h>   // isgraph

#define BUFF 1024

int main(void)
{
	FILE *fp;
	char fname[256]; // ファイル名
	char str[BUFF];  // 行バッファ
	char *p;
	int i;
	int n_exe = 0;   // 実行行の行数
	int n_com = 0;   // コメント行の行数
	int n_blank = 0; // 空行の行数
	int in_com = 0;	 // /* ... */ のコメント内フラグ

	while (1) {
		printf("ステップ数をカウントします。\n"
			   "ソースファイル名:");
		if (fgets(fname, sizeof fname, stdin) == NULL) return 1;
		p = strchr(fname, '\n');  // 行末の '\n' を探す
		if (p != NULL) *p = '\0'; // '\n' が見つかったら削除
		if (fname[0] == '\0') return 0; // ファイル名がないと終了
		fp = fopen(fname, "r");
		if (fp != NULL) break;    // オープン成功でループを抜ける
		perror("ファイルが存在しません\n");
	}
	
	while ((fgets(str, BUFF, fp)) != NULL) {
		int is_exe = 0;   // 実行行確定フラグ
		int is_com = 0;   // コメント行確定フラグ
		int has_com = 0;  // 行内の「/*」または「*/」の存在フラグ
		for (i = 0; str[i] != '\0'; i++) {		
			if (!in_com) {     // 「/* で始まるコメント」の内部ではない
				if (str[i] == '/') {
					if (str[i+1] == '/') {
						is_com = 1; break;  //「//で始まるコメント」確定
					}
					if (str[i+1] == '*') {  //「/*」でコメント開始
						has_com = 1; in_com = 1; i++;
					}
					else {            // 「/」はコメントではなく、除算演算子
						is_exe = 1; break;  // 実行行確定
					}
				}
				else if (isgraph((unsigned char)str[i])) { //「/」でもなく空白でもない文字
					is_exe = 1; break;      // 実行行確定
				}
			}
			else if (str[i] == '*' && str[i+1] == '/') { //「*/」でコメント終了
				has_com = 1; in_com = 0; i++;
			}
		}
		if (is_exe)
			n_exe++;
		else if (is_com || in_com || has_com)
			n_com++;
		else
			n_blank++;
	}
	printf("実行行: %d行\n", n_exe);
	printf("コメント行: %d行\n", n_com);
	printf("空行: %d行\n", n_blank);
	fclose(fp);

	return 0;
}
行の先頭から順番に見ていって、
・「//」で始まれば、コメント行確定
・「/*」で始まれば、コメントに入るがその後出るかもしれない
・上記以外で空白でないなら、実行行確定
ただし、直前までの行で「/*」によりコメントに入っていれば
「*/」を探すことに専念しないといけません。

else if (is_com || in_com || has_com) の意味ですが
・「//」で始まっていれば in_com が 1
・直前までの行で「/*」によりコメントに入っていれば in_com が 1
・「/*」の「*/」の両方があると in_com は 0 だが、has_com が 1
ということで、コメント行であることをチェックしています。

どこが分からないのかを具体的に質問してください。

どんどんどん
記事: 7
登録日時: 5年前

Re: ステップ数についておしえてください

#9

投稿記事 by どんどんどん » 5年前

かずま様。度々のご質問にお答え頂き、また新しいコードまで頂きありがとうございます。
具体的な質問をさせて頂きたいのですが、そもそも何故

コード:

for (i = 0; str[i] != '\0'; i++) {		
			if (!in_com) {     // 「/* で始まるコメント」の内部ではない
				if (str[i] == '/') {
					if (str[i+1] == '/') {
						is_com = 1; break;

で//で始まればコメント行確定となるのでしょうか?
大前提の考え方から教えて頂けるとありがたいです。
また頂いているコードですと

コード:

/*

456

*/

のようにコメントの間に改行を入れるとそれもコメントとして判断されてしまうのですが、コメントとしてではなく、空白としてカウントすることはできませんか?

またご質問のあった2点ですが、1つWindows7でサクラエディタを使用しC言語の勉強を行っております。
2つ模範解答でのサンプルテストですが、私自身がソースコードをまったく理解できていなかったので、理解してから実行したく、そういった確認作業は本当に動くか動かないか程度の簡単なものしか行っておりません。

かずま

Re: ステップ数についておしえてください

#10

投稿記事 by かずま » 5年前

どんどんどん さんが書きました:
5年前
具体的な質問をさせて頂きたいのですが、そもそも何故

コード:

for (i = 0; str[i] != '\0'; i++) {		
			if (!in_com) {     // 「/* で始まるコメント」の内部ではない
				if (str[i] == '/') {
					if (str[i+1] == '/') {
						is_com = 1; break;

で//で始まればコメント行確定となるのでしょうか?
大前提の考え方から教えて頂けるとありがたいです。
「//」から行末までの全部がコメントです。これが大前提です。
「//」で始まって、コメントでない行の例がありますか?
「//」で始まったら、行の残りは見る必要がありません。
 だから、break; します。

「//」で始まらないのに、このコードでコメント行確定と
なることがあります。それは、

コード:

	/* comment1 */  // comment2
「/*」で in_com が 1 になり、「*/」で in_com が 0 になり、
「//」で is_com が 1 になり、コメント行確定です。

どんどんどん さんが書きました:
5年前

コード:

/*

456

*/

のようにコメントの間に改行を入れるとそれもコメントとして判断されてしまうのですが、コメントとしてではなく、空白としてカウントすることはできませんか?
できますよ。フラグを 1個追加してみました。

コード:

	while (fgets(str, BUFF, fp) != NULL) {
		int is_exe = 0;   // 実行行確定フラグ
		int is_com = 0;   // コメント行確定フラグ
		int has_com = 0;  // 行内の「/*」または「*/」の存在フラグ
		int not_blank = 0;  // コメント内の非空白文字存在フラグ   // ★
		for (i = 0; str[i] != '\0'; i++) {		
			if (!in_com) {     // 「/* で始まるコメント」の内部ではない
				if (str[i] == '/') {
					if (str[i+1] == '/') {
						is_com = 1; break;  //「//で始まるコメント」確定
					}
					if (str[i+1] == '*') {  //「/*」でコメント開始
						has_com = 1; in_com = 1; i++;
					}
					else {            // 「/」はコメントではなく、除算演算子
						is_exe = 1; break;  // 実行行確定
					}
				}
				else if (isgraph((unsigned char)str[i])) { //「/」でもなく空白でもない文字
					is_exe = 1; break;      // 実行行確定
				}
			}
			else if (str[i] == '*' && str[i+1] == '/') { //「*/」でコメント終了
				has_com = 1; in_com = 0; i++;
			}
			else if (isgraph((unsigned char)str[i]))          // ★
				not_blank = 1;                                // ★
		}
		if (is_exe)
			n_exe++;
		else if (is_com || (in_com && not_blank) || has_com)  // ★
			n_com++;
		else
			n_blank++;
	}

かずま

Re: ステップ数についておしえてください

#11

投稿記事 by かずま » 5年前

かずま さんが書きました:
5年前
できますよ。フラグを 1個追加してみました。
not_blank を追加する代わりに、has_com を利用することができますね。

コード:

			else if (isgraph((unsigned char)str[i]))  // ★
				has_com = 1;                          // ★
		}
		if (is_exe)
			n_exe++;
		else if (is_com || has_com)                   // ★
			n_com++;
		else
			n_blank++;

どんどんどん
記事: 7
登録日時: 5年前

Re: ステップ数についておしえてください

#12

投稿記事 by どんどんどん » 5年前

かずま様ご丁寧な回答の上、たくさんのサンプルコード真にありがとうございます。
大前提以前の考えかたの話なんですが、

コード:

for (i = 0; str[i] != '\0'; i++) {		
			if (!in_com) {     // 「/* で始まるコメント」の内部ではない
				if (str[i] == '/') {
					if (str[i+1] == '/') {
						is_com = 1; break;
これは'\0まで一文字ずつ読み込んでいき、もし"/"がきてかつその次の文字が"/"なら1つカウントしてその処理を抜け出すということでいいんでしょうか?
また、かずま様のコードで言いますとどの行で実行行に書かれているコメントをコメントではなく実行行として判断しているのでしょうか?
なにぶん素人なもんですから本当に1から教えて頂けるとありがたいです。

かずま

Re: ステップ数についておしえてください

#13

投稿記事 by かずま » 5年前

どんどんどん さんが書きました:
5年前

コード:

for (i = 0; str[i] != '\0'; i++) {		
			if (!in_com) {     // 「/* で始まるコメント」の内部ではない
				if (str[i] == '/') {
					if (str[i+1] == '/') {
						is_com = 1; break;
これは'\0まで一文字ずつ読み込んでいき、もし"/"がきてかつその次の文字が"/"なら1つカウントしてその処理を抜け出すということでいいんでしょうか?
is_com = 1; は、コメント行確定フラグを ON(TRUE) にしている
のです。これは、「ひとつカウントして」ではありません。

fgets で str に 1行読み込み、3つのフラグを全部 OFF(FALSE)
にしました。for (i = 0; str[ i] != '\0'; i++) { で
その 1行を先頭から末尾まで 1文字ずつ調べようとします。

「//」 があれば、コメント行確定ですから、フラグ is_com を
ON にして、break; で forループを抜け、フラグを見て、
そこでコメント行の行数をカウントアップします。

カウントして抜け出すのではありません。フラグをセットして、
ループを抜け出してから、行数をカウントアップするのです。
どんどんどん さんが書きました:
5年前
どの行で実行行に書かれているコメントをコメントではなく実行行として判断しているのでしょうか?
具体的な例で考えてみましょう。

str が " a = 1; // comment\n" だったとします。
先頭から 1文字ずつ見ていくと、str[1] が 'a' なので、
if (!in_com) { の中の else if (isgraph((unsigned char)str[ i]) {
で、実行行確定フラグ is_exe を ON にして、ループを抜け、
フラグを見て、そこで実行行の行数をカウントアップします。

str が " /* comment */ a = 1;\n" だったとします。
先頭から 1文字ずつ見ていくと、
str[1] が '/'、str[2] が '*' なので、
has_com = 1, in_com = 1 になり、
i++ で i を 2 にして forの i++ で i は 3 になり、
/* の次の文字を見に行きます。
str[12] が '*'、str[13] が '/' なので、
has_com = 1、in_com = 0 になり、
i++ で i を 13 にして forの i++ で i は 14 になり、
*/ の次の文字を見に行きます。
str[15] が 'a' なので、
if (!in_com) { の中の else if (isgraph((unsigned char)str[ i]) {
で、実行行確定フラグ is_exe を ON にして、ループを抜け、
フラグを見て、そこで実行行の行数をカウントアップします。

for, break, if-else で制御の流れがどうなるかが、わからないのですか?

かずま

Re: ステップ数についておしえてください

#14

投稿記事 by かずま » 5年前

最初のプログラムをそれなりに動くようにしてみました。

コード:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define BUFF 1024

int main(void)
{
	FILE *fp;
	char fname[256]; // ファイル名
	char str[BUFF];  // 行バッファ

	int len = 0;   // 1行の長さ
	int i = 0;

	int all = 0;   // 空行以外の行数
	int exe = 0;   // 実行行の行数
	int come = 0;  // コメント行の行数

	int flaga = 0;  // // から行末までのフラグ
	int flagb = 0;  // /* から */ までのフラグ
	int flagc = 0;  // 実行行フラグ

	while (1) {
		printf("ステップ数をカウントします。\n");
		printf("ソースファイル名:");
		gets(fname);
		fp = fopen(fname, "r");
		if (fp != NULL) break;
		perror("ファイルが存在しません\n");
	}
	while (fgets(str, BUFF, fp) != NULL) {
		flaga = 0;
		flagc = 0;
		len = strlen(str);
		for (i = 0; i < len; i++) {
			if (isgraph(str[i])) {
				if (str[i] == '/' && str[i+1] == '/' && flagb == 0) {
					flaga = 1; i++;
				}
				else if (str[i] == '/' && str[i+1] == '*' && flaga == 0) {
					flagb = 1; i++;
				}
				else if (str[i] == '*' && str[i+1] == '/' && flagb == 1) {
					flagb = 0; i++;
				}
				else if (flaga == 0 && flagb == 0) {
					flagc = 1;
				}
			}
		}
		for (i = 0; i < len; i++) {
			if (isgraph(str[i])){
				all++;
				if (flagc == 0) {
					come++;
				}
				break;
			}
		}
	}
	exe = all - come;
	printf("実行行:%d行\n", exe);
	printf("コメント行:%d行\n", come);

	fclose(fp);
	return 0;
}
どこをどう変えたかわかりますか?

どんどんどん
記事: 7
登録日時: 5年前

Re: ステップ数についておしえてください

#15

投稿記事 by どんどんどん » 5年前

かずま様、毎回わかりやすい解説とソースコードをありがとうございます。
上記のように説明していただけると自分の知っている知識でも理解可能だなと思えます。
for,break,if-else文については理解していますが、どうもフラグの使い方が曖昧のようでそこをもう一度勉強しなおしたいと思います。

また新しく頂いたコードですが、i++;が追加されカウントするようになったって事でいいんでしょうか?

かずま

Re: ステップ数についておしえてください

#16

投稿記事 by かずま » 5年前

どんどんどん さんが書きました:
5年前
また新しく頂いたコードですが、i++;が追加されカウントするようになったって事でいいんでしょうか?
ファイルのオープン部分も少し変えましたが、これはこの問題の
本質とは関係ないのでどうでもいいです。

1行ずつ比較すれば、何が変わったかわかりますよね。
・行バッファに、fname ではなく、str を使うようにした。
・"/*" 検出のところに、flagb = 1; を追加した。[重要]
・"*/" の検出を、fname[ i]=='/' && fname[i-1]=='*' から
 str[ i]=='*' && str[i+1]=='/' に変更した。
・"//", "/*, "*/" 検出のところに、i++; を追加した。
・flag = 0; flagc = 0; を行のスキャンの前に移動した。

i++; を追加する意味が分かりますか?
for (i = 0; i < len; i++) { で 1文字ずつ見ていきますね。
ところが "//", "/*, "*/" 検出のところでは、2文字見ています。
だから i++; が必要なのです。これがないと
/*/ をコメントが始まってすぐに終了したと間違って解釈してしまいます。
*/* をコメントが終了してすぐに開始したと間違って解釈してしまいます。

if (isgraph(str[ i])) { の意味が分かりますか?
空白以外の文字を見ますよ、ということです。
if (!isspace(str[ i])) { と書いても問題ないと思います。

各フラグの意味ですが、
・flaga は、「//コメント」の中ですよ
・flagb は、「/*コメント*/」の中ですよ
・flagc は、コメントでないところに文字がありましたよ
ということです。

どんどんどん
記事: 7
登録日時: 5年前

Re: ステップ数についておしえてください

#17

投稿記事 by どんどんどん » 5年前

かずま様。いつもわかりやすい解説ありがとうございます。
プログラミングは奥が深くフラグを使えるようになるだけでプログラミングの幅が広がりますね。
自分でも理解しているところと理解していないところが曖昧ですので再度一から考え直したいと思います。
いつもご丁寧な解説ありがとうございます。非常に助かっています。

かずま

Re: ステップ数についておしえてください

#18

投稿記事 by かずま » 5年前

かずま さんが書きました:
5年前
・flaga は、「//コメント」の中ですよ
・flagb は、「/*コメント*/」の中ですよ
・flagc は、コメントでないところに文字がありましたよ
意味のある名前を使うべきですね。

コード:

#include <stdio.h>   // printf, puts, perror, fopen, fclose, fgets
#include <string.h>  // strchr
#include <ctype.h>   // isspace

int main(void)
{
	FILE *fp;        // 入力ファイルストリーム
	char str[1024];  // 行バッファ
	int exec = 0;    // 実行行の行数
	int comm = 0;    // コメント行の行数
	int blank = 0;   // 空行の行数
	int sa_com = 0;  // /*コメント*/フラグ (slash-asterisk commnet) ★

	puts("ステップ数をカウントします。");
	while (1) {
		char fname[256], *p;

		printf("ソースファイル名:");
		if (!fgets(fname, sizeof fname, stdin)) return 1;
		if (p = strchr(fname, '\n')) *p = '\0';  // '\n' があれば削除
		if (fp = fopen(fname, "r")) break;  // fopen成功ならループ終了
		perror("ファイルが存在しません\n");
	}
	while (fgets(str, sizeof str, fp)) {
		int ss_com = 0;    // //コメントフラグ (slash-slash comment) ★
		int ch_found = 0;  // コメント外の文字検出フラグ               ★
		unsigned char c;

		for (int i = 0; c = str[i]; i++)
			if (!isspace(c))    // 空白以外の文字なら
				if (!sa_com && c == '/' && str[i+1] == '/')
					ss_com = 1, i++;
				else if (!ss_com && c == '/' && str[i+1] == '*')
					sa_com = 1, i++;
				else if (sa_com && c == '*' && str[i+1] == '/')
					sa_com = 0, i++;
				else if (!ss_com && !sa_com)  // コメント外なら
					ch_found = 1;

		for (int i = 0; (c = str[i]) && isspace(c); i++) ;

		if (c == '\0')     // 行末まで見たが文字はなかった
			blank++;
		else if (ch_found) // 見つかった文字はコメント外の文字だった
			exec++;
		else
			comm++;
	}
	fclose(fp);

	printf("実行行:%d行\n", exec);
	printf("コメント行:%d行\n", comm);
	printf("空行:%d行\n", blank);
	return 0;
}

どんどんどん
記事: 7
登録日時: 5年前

Re: ステップ数についておしえてください

#19

投稿記事 by どんどんどん » 5年前

かずま様度々すいません。ありがとうございます。
ほぼ同じコードなのに変数の宣言名を変えるだけで、こんなにも変わるモノなのですね。
また、ステップ数とは違う質問になってしますのですがお答えいただける幸いです。
私自身プログラミング経験が無く一から勉強していますが論理的な考えが苦手なのか、ソースコードを見るとなんとなく何をしたいプログラムかわかるのですが、日本語で書かれたモノをソースコードに直すのは苦手です。
漢字で言うところの読めるけど書けない状態です。
ですので、こういった問題を解くときどういった考えかたをしているのか、ソースコードの中身以前にソースコードへの考え方を、またC言語はどういった学習方法をとられたのでしょうか?
そう言った根元の部分を教えて頂けるとありがたいです。

返信

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