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

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

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

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

意見交換

クローズ

6回答

1646閲覧

C言語コードレビュー:バッファに残った文字を読み飛ばす。

albertadam

総合スコア11

C

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

0グッド

0クリップ

投稿2023/07/18 07:19

編集2023/07/18 08:20

0

0

調査したこと・試したこと

fgets, scanf, getcharの三つの入力として使われる関数をもとに、決められた文字数だけをデータとして持ち、それ以上の入力は読み飛ばして、バッファの中身を空にするということを行ってきました。

じゃあなぜgetcharを投稿したのかというと、今後入力した文字列に条件(半角のみとか)をつける際に、一文字ずつ受け取って、判定して、次の文字を受け取って、判定して、、、、を繰り返した方が、正確に情報を読み取り、バグが少ないと考えたからです。

ただ自分で解答した通り、scanfで文字数を指定して、入力できることを知り、受け取った後にバッファを読み飛ばせばいいことに気づきました。

scanfの方法ではfgetsができないことがわかっています。fgetsは\nも読み取ってしまうので、while(getchar()!=''\n);の記述をしてしまうと、while文での入力待ちが発生してしまいます。scanfは\nを読み取らないので、質問解答に記載した方法が最良なのではないかと考えます。

皆様の意見をお聞かせ下さい。

テーマ、知りたいこと

記事に載せましたコードのレビューをお願いしたいです。
また改善策などありましたら、コメントのほどお願いします。

ソースコード説明

ソースコードの内容としまして、
入力文字列を上限10文字で受け取るというものです。
入力はgetcharで行います。

10文字以下の入力の場合は、そのままプリントされます。
10文字以上の入力の場合は、10番目以降の文字を読み捨てられる。

なぜソースコードにscanfがあるのか

ご指摘いただいた内容です。
バッファに残っていないことを確認するために、scanfを置いておき、
・何もせずに入力を受け取っていたら、バッファに文字が残っており、
・入力待ち状態になったら、バッファに文字が残っていないことがわかるようになっています。

ソースコード

C

1#include <stdio.h> 2 3int main(){ 4 char name[10]; 5 int c; 6 int i; 7 int flag = 0; 8 9 for(i = 0; i < 10; i++){ 10 c = getchar(); 11 12 if(c == '\n'){ 13 name[i] = '\0'; 14 break; 15 } 16 17 name[i] = c; 18 19 if(i == 9 && c != '\n'){ 20 name[i + 1] = '\0'; 21 flag = 1; 22 break; 23 } 24 } 25 26 if(flag == 1){ 27 name[i] == '\0'; 28 while(getchar() != '\n'); 29 } 30 31 int num; 32 scanf("%d", &num); 33 34 printf("%s", name); 35 36 return 0; 37}

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

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

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

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

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

回答6

#1

albertadam

総合スコア11

投稿2023/07/18 07:46

自分で解答
scanf()で文字数指定して、while(getchar() != '\n');でやった方がシンプル。
ただgetcharは一文字ずつ読むので、早めに判定をすることができるが、scanfは一気読みなので、その分の速度が違いそうな気もしている。
・1文字ずつ判定して、OKNGを出すか、
・一気に読んでから、一つずつ見てOKNGを出すか、
の違いだと思っている。

C

1#include <stdio.h> 2 3int main(){ 4 5 int input, input2, input3; 6 7 scanf("%10d", &input); 8 9 while(getchar() != '\n'); 10 11 scanf("%2d", &input2); 12 13 while(getchar() != '\n'); 14 15 scanf("%3d", &input3); 16 17 while(getchar() != '\n'); 18 19 printf("%d-%d-%d\n", input, input2, input3); 20 21 return 0; 22}

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

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

#2

fana

総合スコア12151

投稿2023/07/18 07:53

10文字以下の入力の場合は、そのままプリントされます。
10文字以上の入力の場合は、10番目以降の文字を読み捨てられる。

「N以上」「N以下」「N以降」というのは全てNを含む言葉ですから,
この説明だと 10文字 という境界付近でどう振る舞うのが正解なのかが読み取れません.


20行目の name[i + 1] = '\0'; は,i==9 のときにのみ実施され得るので,name[10] にアクセスしています.(char name[10]; の領域外です.)
(また,ここが走ったとき,今度は27行目にて name[9]\0 にすることになりそうなのも怪しく見えます.果たしてどちらが所望の動作なのか?)

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

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

#3

albertadam

総合スコア11

投稿2023/07/18 08:09

fana 様

貴重なご意見ありがとうございます。
まさしくその通りで、領域を破壊していました。

c

1char name[11];

であれば、領域の破壊は防がれると思いました。

私の所望といたしましては、以下のコードが正しいのではないかと考えます。
10文字でピッタリ区切り、それ以上の入力があるのであれば、getcharで読み飛ばす。

c

1#include <stdio.h> 2 3int main(){ 4 char name[11]; 5 int c; 6 int i; 7 int flag = 0; 8 9 for(i = 0; i < 10; i++){ 10 c = getchar(); 11 12 if(c == '\n'){ 13 name[i] = '\0'; 14 break; 15 } 16 17 name[i] = c; 18 19 if(i == 9 && c != '\n'){ 20 name[i + 1] = '\0'; 21 flag = 1; 22 break; 23 } 24 } 25 26 if(flag == 1){ 27 while(getchar() != '\n'); 28 } 29 30 int num; 31 scanf("%d", &num); 32 33 printf("%s", name); 34 35 return 0; 36}

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

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

#4

ardin

総合スコア555

投稿2023/07/18 08:09

編集2023/07/18 08:11

getcharはエンターキーも文字として取るため、

if(c == '\n'){ }

の判定で10文字達していなくてもエンターキーが押されればこのループを脱出します。

20行目の

name[i + 1] = '\0';

iが9の時に入ってきますが、name[10]は範囲外となります。

int num; scanf("%d", &num);

これの必要性はよく分からない。

28行目のwhile(getchar() != '\n');
これ、この時点でも抜けられるんですね・・・

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

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

#5

SaitoAtsushi

総合スコア5714

投稿2023/07/18 08:27

細かい書き方はどれを選んでもどうでもいいと思います。 (意図した通りに動作するというのは大前提ですが。)

ただ、手頃な単位で関数に分割して名前を付けておくべきでしょう。 分割が適切であれば雑に書いてもそんなに見通しは悪くなりませんし、問題があったときに調査しやすいです。

私が書くとしたらこんな感じでしょう。 (関数ごとの挙動をコメントで書いておくとなお良いかもしれません。)

c

1#include <stdio.h> 2 3void skip_until_enter(void) { 4 int ch; 5 while ((ch = getchar()) != '\n'); 6} 7 8size_t limited_read(char* name, size_t buffer_size) { 9 size_t i; 10 int ch; 11 for (i = 0; i < buffer_size - 1 && (ch = getchar()) != '\n'; ++i) { 12 name[i] = ch; 13 } 14 name[i] = '\0'; 15 if (i == buffer_size - 1) skip_until_enter(); 16 return i; 17} 18 19int main(void) { 20 char name[10]; 21 22 limited_read(name, sizeof(name)); 23 24 int num; 25 scanf("%d", &num); 26 27 printf("%s", name); 28}

ちなみにこれは改行なしで EOF が入力されたときのことを想定していないので、そのへんのチェックも追加したほうが良いかもしれないですね。

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

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

#6

fana

総合スコア12151

投稿2023/07/18 08:44

編集2023/07/18 08:59

#3

for で回数指定しているのに別途 i==9 みたいな判定があったりとか,\0 に関する処理が複数ある点は纏められるのではないでしょうか.

C

1int main() 2{ 3 char name[11]; 4 int c; 5 int i; 6 7 for( i=0; i<10; ++i ) 8 { 9 c = getchar(); 10 11 if( c=='\n' || c==EOF ) 12 { break; } 13 14 name[i] = c; 15 } 16 name[i] = '\0'; 17 18 if( i == 10 ) //余計に入力されたことはこれで判断できるかと 19 { 20 printf( "(Clear Input Buffer)\n" ); 21 while( getchar() != '\n' ); 22 } 23 24 printf( "name = [%s]\n", name ); 25 return 0; 26}

[追記]
↑ではループ後に i==10 で判断しているけど,入力文字をチェックして途中でループを抜ける場合がある想定ならばそうもいかないか.
…というわけで素直にフラグで判定するならこんな感じかな.

C

1int main() 2{ 3 char name[11]; 4 int c; 5 int i; 6 7 char ClearInputBufferFlag = 1; //フラグを立てておいて… 8 for( i=0; i<10; ++i ) 9 { 10 c = getchar(); 11 12 if( c=='\n' || c==EOF ) 13 { 14 ClearInputBufferFlag = 0; //フラグを降ろしてループを抜ける. 15 break; 16 } 17 18 if( c<'0' || '9'<c ) //何らかのチェックに引っかかった場合 19 { break; } //単にループを抜ける 20 21 name[i] = c; 22 } 23 name[i] = '\0'; 24 25 //フラグが降ろされていないなら入力バッファのクリア処理を実施する 26 if( ClearInputBufferFlag ) 27 { 28 printf( "(Clear Input Buffer)\n" ); 29 while( getchar() != '\n' ); 30 } 31 32 printf( "input = [%s]\n", name ); 33 return 0; 34}

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

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

最新の回答から1ヶ月経過したため この意見交換はクローズされました

意見をやりとりしたい話題がある場合は質問してみましょう!

質問する

関連した質問