質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

ファイル

ファイルとは、文字列に基づいた名前又はパスからアクセスすることができる、任意の情報のブロック又は情報を格納するためのリソースです。

Q&A

解決済

2回答

963閲覧

ファイル読み込みのワードカウンタの作成について

alice331

総合スコア11

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

ファイル

ファイルとは、文字列に基づいた名前又はパスからアクセスすることができる、任意の情報のブロック又は情報を格納するためのリソースです。

0グッド

0クリップ

投稿2020/01/07 13:37

編集2020/01/07 15:31

ファイルの読み込みで文字数、単語数、行数をカウントするのですが//
のコメント部分はカウントしないソース作成しているのですが、どのようにすればよろしいでしょうか?
実際にコメント部分も含めたコードは書けています。
途中までは出来ているのですが、出力結果はコメントもカウントしてしまいます。最終的にコメント含むとコメント含まずの両方を出力したいです。
よろしくお願い致します。コードも書いていただけると助かります

質問の詳細追加
test.textを読み込みます
this is a pen
/コメント1/
this is an apple
/コメント2/

・コメント含む
単語数(/コメント1/で1単語扱い) 10個 行数 4行 文字数 41文字

・コメント含まず
単語数 8個 行数 2行 文字数 23文字

単語は一つのカタマリで単語扱いにしています。

#include <stdio.h> #include <stdlib.h> #define TRUE 1 #define FALSE 0 int main(int argc, char *argv[]) { FILE *fp; /* 入力ファイル用ファイルポインタ */ int ch; /* 入力文字 */ int line=0; /*行数*/ int word=0; /*単語数*/ int character=0; /*文字数*/ int whitespace=TRUE;/*空白文字を読んでいる最中は真(単語を読んでいる間,偽)*/ char back; if(argc == 1) { fp=stdin; /*標準入力を入力ファイルとする*/ } else if(argc == 2) { /*指定したファイルを開く*/ if((fp=fopen(argv[1],"r"))== NULL) { fprintf(stderr,"%s:ファイルが開けませんでした%s\n", argv[0],argv[1]); exit(1); } } else { } /*EOF が現れるまで 1 文字ずつ読みとる*/ while((ch = getc(fp)) != EOF) { // printf("standerd back=%c ch=%c\n",back,ch); /* '/''*'の文字かどうか判定する*/ if(back=='/' && ch=='*') { /* '/''*'の場合はEOFまで1文字ずつ読み取る*/ while((ch = getc(fp)) != EOF) { //printf("innet back=%c ch=%c\n",back,ch); /*一文字ずつ読み取り'*''/'を読み取ったらループを抜ける*/ if(back=='*' && ch=='/') { break; } /*一文字前に読み取った文字を代入する*/ back=ch; } }else { back=ch; /*文字数を増やす*/ character++; /*改行なら行数を増やす*/ if(ch=='\n') { line++; } /*空白、改行、タブの場合は空白とする。*/ if(ch==' '||ch=='\n'||ch=='\t') { whitespace = TRUE; } /*空白の場合は単語を増やす*/ else if(whitespace) { whitespace = FALSE; /*単語数を増やす*/ word++; } } } printf("行数=%d 単語数=%d 文字数=%d\n", line, word, character); fclose(fp); exit(0); }

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

y_waiwai

2020/01/07 13:47

このままではコードが読みづらいので、質問を編集し、<code>ボタンを押し、出てくる’’’の枠の中にコードを貼り付けてください
jimbe

2020/01/07 13:49

コードには専用の枠があります. 入力枠の上辺にある <code> を押して出てくる ``` の間にある "コード" を置き換える形で記入(コピペ)してください. また, "ここに言語を入力" を "c" に置き換えますと, c の書式を認識してフォントが変わったりします. ご質問は編集できますので, 入力枠の下にあるプレビューで確認しながら編集して頂けると助かります.
guest

回答2

0

ベストアンサー

出力結果はコメントもカウントしてしまいます

"/" の初めの "/" がカウントされてしまっているのではないでしょうか.
"/" を読んだ時点でカウントを遅延させる(次が "
" で無かったら "/" の分もカウントする)等, の細工が必要なのではないでしょうか.


追加

文字コード SJIS(MS932) として修正してみました.
結果表示の()内がコメントを含む場合のカウントです.

c

1#include <stdio.h> 2#include <stdlib.h> 3#define TRUE 1 4#define FALSE 0 5 6int isSpace(int c) { 7 return c==' '||c=='\n'||c=='\t'; 8} 9int isSJIS1(int c) { 10 return ((c>=0x81)&&(c<=0x9f))||((c>=0xe0)&&(c<=0xfc)); 11} 12/* 13* getc に 14* ・SJIS全角も1文字として返す 15* ・改行コード(\n,\r,\r\n)を'\n'に統一する 16* 機能を追加 17*/ 18int getSJIS(FILE *fp) { 19 int ch, c; 20 if((ch = getc(fp)) != EOF) { 21 if(isSJIS1(ch)) { 22 if((c = getc(fp)) != EOF) { 23 ch=(ch<<8)|(c&0xff); 24 } 25 } else if(ch=='\r') { 26 ch='\n'; 27 if((c = getc(fp)) != EOF) { 28 if(c!='\n') { 29 ungetc(c,fp); 30 } 31 } 32 } 33 } 34 return ch; 35} 36int main(int argc, char *argv[]) { 37 FILE *fp; /* 入力ファイル用ファイルポインタ */ 38 int ch; /* 入力文字 */ 39 int line=0; /*行数*/ 40 int word=0; /*単語数*/ 41 int character=0; /*文字数*/ 42 int comment=0; /*コメント数*/ 43 int comment_chars=0; /*コメント文字数*/ 44 int comment_lines=0; /*コメント改行数*/ 45 int whitespace=TRUE;/*空白文字を読んでいる最中は真(単語を読んでいる間,偽)*/ 46 int back; 47 48 if(argc == 1) { 49 fp=stdin; /*標準入力を入力ファイルとする*/ 50 } else if(argc == 2) { 51 /*指定したファイルを開く*/ 52 if((fp=fopen(argv[1],"r"))== NULL) { 53 fprintf(stderr,"%s:ファイルが開けませんでした%s\n", argv[0],argv[1]); 54 exit(1); 55 } 56 } 57 58 /*EOF が現れるまで 1 文字ずつ読みとる*/ 59 while((ch = getSJIS(fp)) != EOF) { 60 // printf("standerd back=%c ch=%c\n",back,ch); 61 /* '/''*'の文字かどうか判定する*/ 62 if(ch=='/') { 63 if((ch = getSJIS(fp)) != EOF) { 64 if(ch=='*') { 65 comment_chars+=2; 66 /* '/''*'の場合はEOFまで1文字ずつ読み取る*/ 67 back = 0; 68 while((ch = getSJIS(fp)) != EOF) { 69 if(!isSpace(ch)) { 70 comment_chars++; 71 } 72 //printf("innet back=%c ch=%c\n",back,ch); 73 /*一文字ずつ読み取り'*''/'を読み取ったらループを抜ける*/ 74 if(back=='*' && ch=='/') { 75 break; 76 } 77 /*一文字前に読み取った文字を代入する*/ 78 back=ch; 79 } 80 comment++; 81 while((ch = getSJIS(fp)) != EOF) { 82 if(ch=='\n') { 83 comment_lines++; 84 break; 85 } 86 if(ch!=' ' && ch!='\t') { 87 ungetc(ch,fp); 88 break; 89 } 90 } 91 continue; 92 } 93 ungetc(ch,fp); 94 } 95 ch = '/'; 96 } 97 98 /*改行なら行数を増やす*/ 99 if(ch=='\n') { 100 line++; 101 } 102 /*空白、改行、タブの場合は空白とする。*/ 103 if(isSpace(ch)) { 104 whitespace = TRUE; 105 } 106 /*空白の場合は単語を増やす*/ 107 else if(whitespace) { 108 whitespace = FALSE; 109 /*単語数を増やす*/ 110 word++; 111 } 112 if(!whitespace) { 113 /*文字数を増やす*/ 114 character++; 115 } 116 } 117 printf("行数=%d(%d) 単語数=%d(%d) 文字数=%d(%d)\n", 118 line, line+comment_lines, word, word+comment, character, character+comment_chars); 119 fclose(fp); 120 exit(0); 121}

投稿2020/01/07 14:06

編集2020/01/07 17:46
jimbe

総合スコア12648

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

alice331

2020/01/07 14:19

jimbe様 回答ありがとうございます。 具体的にどのような細工が必要でしょうか? 初心者な為詳しく教えて頂ければ幸いです。 よろしくお願いいたします。
jimbe

2020/01/07 14:48

この回答はコードを見ただけですので, 具体的な所は仕様の詳細が分からないと書けません. 文字数・単語数・行数をカウントということですが, コメントがある場合どのようにカウントするのでしょう. 例えば, 1行で /* ~ */ が完結する場合, /* の行と */ の行が違う場合, 「FILE *fp; /* 入力ファイル用ファイルポインタ */」のように後ろの付く場合等で, 文字数・単語数・行数はどのように変化するのか, ご提示のコードを読ませた場合, コメントをカウントに含める場合と含めない場合でそれぞれの値は幾つと表示されるようにしたいのかをご説明願えますか. こちらの欄は狭いですので, ご質問に追加してください.
alice331

2020/01/07 15:32

質問に追加させていただきました。 どうぞ、よろしくお願いいたします。
jimbe

2020/01/07 16:24

全角文字も1文字と数えるようですが, 文字コードによって全角文字の判定が難しいことはご理解されていますか. 文字コードは何と前提すればよろしいでしょうか. 改行コードも同様に '\n'/'\r\n'/'\r' と3つの種類があります. ご提示のコードでは '\n' の場合のみ対応しているようですが, こちらについてもどうお考えでしょうか.
alice331

2020/01/07 22:49

ソースコードありがとうございました。 全角文字の判定は難しいのは知りませんでした。改行コードは '\n'のみで構いません。 文字コードやまだ習っていないものもたくさんあり、もう少し簡単なコードになりますでしょうか?申し訳ありません。
jimbe

2020/01/08 03:11

文字コードに関しましては, 簡単にといってもこれ以上はならないかと思います. もし半角のみとする(もしくは全角は考えない)ならば勿論簡単になりますが. 具体的に何が習っていなくて難しいのかを仰っていただけますか.
alice331

2020/01/08 05:09

半角のみだとどうなりますでしょうか? あと、コメント部分を単純に読み飛ばすコードはどうなりますでしょうか?
jimbe

2020/01/08 05:20

半角のみとすれば, isSJIS1関数と, それを使っている getSJIS 関数内の if(isSJIS1(ch)) { if((c = getc(fp)) != EOF) { ch=(ch<<8)|(c&0xff); } } else が不要になります. コメントの読み飛ばしは, 単にカウントしないだけということになりますので, こちらで新たに追加した comment_chars, comment, comment_lines を加算しない(もしくは単に使わない) というだけです.
alice331

2020/01/08 07:48

ありがとうございます。 なんとなく形になりました! コードの理解をしたいので、コメント部分の解説をいただけますでしょうか? if((ch = getSJIS(fp)) != EOF)の部分はどのような意味になりますか? よろしくお願いします。
jimbe

2020/01/08 08:36

if((ch = getSJIS(fp)) != EOF) は, ご提示のコードにあった while((ch = getc(fp)) != EOF) と似たものです. getc は半角のみを扱いますので, 全角も1文字として扱えるよう getSJIS 関数を作りました. "ch = getSJIS(fp)" によって ch に1文字が代入され, (c では代入式がそのまま"代入した値"を返しますので) if 文によって「"代入した値"が EOF で無ければ 次の {} 内を実行する」ということになります.
jimbe

2020/01/08 08:51

getSJIS 関数は改行コードの調整も行っていますが, 全角も無く改行コード調整も不要でしたら存在意義がありませんので, getSJIS 関数も削除して main 関数内の getSJIC を全て getc にすることが出来ます.
alice331

2020/01/08 13:22

本当にありがとうございます。 最後に下記のコードの理由を教えてください。 /*空白とタブが読み込まれたらループを抜ける*/ if(ch!=' ' && ch!='\t') { break; } } continue; } } ch='/'; } ----------------------------------------------------- continue;をする理由をお願いします。 ch='/';はコメントの最後の'/'をchに代入するという意味でよろしいでしょうか? お手数ですがよろしくお願い致します。
jimbe

2020/01/08 14:57

その continue が戻る先は > /*EOF が現れるまで 1 文字ずつ読みとる*/ > while((ch = getSJIS(fp)) != EOF) { です. continue に辿り着いた時点で, コメントの終わりの "*/" (の後の空白と改行)までの読み込みと処理が終了しています. コメントに関する処理が全て終わっていますので, 次の文字の処理に移る為に戻しています. ch='/' は, その直前の } に対応する if 文 (ch=getSJIS(fp)) != EOF が偽, もしくはさらに内側の if(ch=='*') が偽の場合に通ります. (両方真の場合は continue で上に戻りますので.) これが意味するのは, '/' がファイルの最後の文字か, '/' の次の文字が '*' では無い(=コメントの開始を示す "/*" ではない) 場合ですので, 単なる文字として後続のコードを実行する為に(ch=getSJISで値が変わってしまっていますので)戻しています.
guest

0

こういった処理には状態遷移の考え方を用いるのが常道です。

とりあえずアスキーコードの範囲内だけを考えるとして、単語を構成するのはアルファベットのみ、それ以外は区切り文字として考えることにします。

持ちうる状態は

  • 単語処理中
  • 区切り文字処理中
  • コメント処理中

の三種類です。

更に言えば、 / 一文字を読んだ状態とコメント処理中に * のみを読んだ状態もあるので合計で五種類の「状態」が考えられます。

これらの状態と、状態を移動する条件を合わせたものが状態遷移図です。

イメージ説明

現在の状態と新たに入力した文字を元に場合分けをして処理すればよいので、この場合は 13 個の条件を並べた if 文があれば良いことになります。 プログラムに複雑な構造を持ち込まずに単調に条件を並べるだけで良いので楽です。 他の状態から単語処理中に遷移したときにカウンタを増加させれば単語の数を数えることが出来ます。 コメントを一単語扱いにするときは他の条件からコメント処理中に遷移したときにもカウンタを増加させればよいのです。

ただし、状態遷移図を残して置かないと後から見てわけわからないプログラムになりがちかもしれません。

追記

以上の考え方に基づいてプログラムにするとこうなります。

c

1#include <stdio.h> 2#include <stdlib.h> 3#include <ctype.h> 4#include <iso646.h> 5 6enum state_t { 7 word, 8 punctuation, 9 comment, 10 comment_pending, 11 comment_finish_pending 12}; 13 14int main(int argc, char *argv[]) 15{ 16 FILE *fp; 17 18 switch(argc) { 19 case 1: 20 fp = stdin; 21 break; 22 case 2: 23 fp = fopen(argv[1],"r"); 24 if(fp == NULL) { 25 fprintf(stderr, "%s:ファイルが開けませんでした%s\n", argv[0], argv[1]); 26 return EXIT_FAILURE; 27 } 28 break; 29 default: 30 fprintf(stderr, "引数の個数が間違っています"); 31 return EXIT_FAILURE; 32 } 33 34 int line_count = 0; 35 int word_count = 0; 36 int character_count = 0; 37 enum state_t state = punctuation; 38 int ch; 39 40 while((ch = getc(fp)) != EOF) { 41 character_count++; 42 if(ch=='\n') line_count++; 43 44 if(state==punctuation && not isalpha(ch) && ch!='/') { 45 } 46 else if(state==punctuation && isalpha(ch)) { 47 state = word; 48 word_count++; 49 } else if(state==punctuation && ch=='/') { 50 state = comment_pending; 51 } else if(state==word && ch=='/') { 52 state = comment_pending; 53 } else if(state==word && not isalpha(ch) && ch!='/') { 54 state = punctuation; 55 } else if(state==word && isalpha(ch)) { 56 } else if(state==comment_pending && isalpha(ch)) { 57 state = word; 58 word_count++; 59 } else if(state==comment_pending && not isalpha(ch) && ch!='*') { 60 state = punctuation; 61 } else if(state==comment_pending && ch=='*') { 62 state = comment; 63 word_count++; // コメントを一語扱いする場合 64 } else if(state==comment && ch!='*') { 65 } else if(state==comment && ch=='*') { 66 state = comment_finish_pending; 67 } else if(state==comment_finish_pending && ch=='/') { 68 state = punctuation; 69 } else if(state==comment_finish_pending && ch!='/') { 70 state = comment; 71 } 72 } 73 74 printf("行数=%d 単語数=%d 文字数=%d\n", line_count, word_count, character_count); 75 fclose(fp); 76 77 return 0; 78}

アスキーコードの範囲内しか考えていないので、文字数はバイト単位で数えてしまうことに気を付けてください。

投稿2020/01/08 03:42

編集2020/01/08 06:21
SaitoAtsushi

総合スコア5446

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問