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

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

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

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

プログラミング言語

プログラミング言語はパソコン上で実行することができるソースコードを記述する為に扱う言語の総称です。

Q&A

解決済

4回答

3369閲覧

C言語で単純なアスキーアート的なプログラムの動作が想定通りに動かない

D-4C

総合スコア1

C

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

プログラミング言語

プログラミング言語はパソコン上で実行することができるソースコードを記述する為に扱う言語の総称です。

0グッド

0クリップ

投稿2021/10/20 07:27

前提・実現したいこと

C言語で単純なアスキーアート的なプログラムを作りたい。例えば、下のように入力したとする。

0 2 3 6 7 10 11 12 13 14 17 18 19 20 21 24 25 27 0 2 3 6 7 12 19 24 25 27 0 2 3 6 7 12 19 24 25 27 0 2 3 6 7 12 19 24 25 27 0 2 3 4 5 6 7 12 19 24 25 27 0 2 3 4 5 6 7 12 19 24 25 27 0 2 3 6 7 12 19 24 25 27 0 2 3 6 7 12 19 27 0 2 3 6 7 12 19 24 25 27 0 2 3 6 7 10 11 12 13 14 19 24 25 27

すると、列の先頭を0番目としてそれぞれの一列の数字番目に#が入り、次のように出力される。

# ## ## ##### ##### ## # # ## ## # # ## # # ## ## # # ## # # ## ## # # ## # # ###### # # ## # # ###### # # ## # # ## ## # # ## # # ## ## # # # # ## ## # # ## # # ## ## ##### # ## #

発生している問題・エラーメッセージ

数字列を入力する時にどうしても改行に反応してしまい、まとめて複数行に渡って入力することをできない。

該当のソースコード

C

1#include <stdio.h> 2#include <string.h> 3#define MAXLEN 128 4char buf[MAXLEN+2]; 5 6int main() { 7 char *p; 8 char str2[128]; 9 10 int s; 11 for(s=0;s < 128;s++){ 12 str2[s]=' '; 13 } 14 15 while(fgets(buf, sizeof(buf), stdin) != NULL) { 16 17 18 ////////////////////////////////////////// 19 char *p = buf; 20 int i; 21 while (sscanf(p,"%d",&i) != EOF) { 22 23 str2[i] = '#'; 24 25 p = strchr(p,' '); // ここからは空白の判定 26 if (p == NULL) break; 27 else while (*p == ' ') ++p; 28 29 } 30 /////////////////////////////////////// 31 printf("%s\n", str2); // 入力された文字列をそのまま出力 32 int s; 33 for(s=0;s < 128;s++){ 34 str2[s]=' '; 35 } 36 37 } 38 return 0; 39} 40 41

これに対し、前提・実現したいことで上げた入力例をまるまるコピーして入れると

0 2 3 6 7 10 11 12 13 14 17 18 19 20 21 24 25 27 # ## ## ##### ##### ## # 0 2 3 6 7 12 19 24 25 27 # ## ## # # ## # 0 2 3 6 7 12 19 24 25 27 # ## ## # # ## # 0 2 3 6 7 12 19 24 25 27 # ## ## # # ## # 0 2 3 4 5 6 7 12 19 24 25 27 # ###### # # ## # 0 2 3 4 5 6 7 12 19 24 25 27 # ###### # # ## # 0 2 3 6 7 12 19 24 25 27 # ## ## # # ## # 0 2 3 6 7 12 19 27 # ## ## # # # 0 2 3 6 7 12 19 24 25 27 # ## ## # # ## # 0 2 3 6 7 10 11 12 13 14 19 24 25 27 # ## ## ##### # ## #

というふうに割り込んで置き換え作業が実行されてしまう。

試したこと

fgets関数を色々いじってみましたが問題解決には至らなく、ここで質問をさせていただきました。

補足情報(FW/ツールのバージョンなど)

Win10,VScodeのターミナルで実行しました。

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

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

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

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

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

episteme

2021/10/20 07:38

想定通りのコードを書いていない。 コードを追いかければ入力と表示を交互に行っているのは明らか。
D-4C

2021/10/20 08:41

コメントありがとうございます。ちょっとfgets関数について根本的な間違いを犯しているかもしれないのでまた考えてみます!
dodox86

2021/10/20 11:13

たぶん、入力に応じたASCIIアートが書ければよいということが最終目的だと思うのですが、直近で解決したい、質問内容が「複数行にわたって入力できない、どうすればできるのか。」になっています。 1行ごとの入力で対応する行のASCIIアートの出力ができればよいのですよね? そうであれば最初に複数行入力することにこだわらなくてよいはずなのですが。
D-4C

2021/10/20 11:22

コメントありがとうございます。確かにその通りです。最初に複数行入力できれば出力も一気にされて作るのが楽になりそうだなーという考えでこのような質問となってしまいました。流石に安直な考えだったのかもしれません。 どうしても今の自分には難しいようでしたらやり方を変えますが、せっかく皆様が時間を割いて解決案を考えてくださっているので、この線でもう少し考えてみたいと思います。
dodox86

2021/10/20 11:28

> 最初に複数行入力できれば出力も一気にされて作るのが楽になりそうだなーという考えでこのような質問となってしまいました。流石に安直な考えだったのかもしれません。 いえ、まずは意図したとおり動くようプログラミングすることも大切だと思うので、複数行での入力パターンを否定するものでもありません。混乱しそうなので回答は控えておきます。
guest

回答4

0

入力する容量がわかっているのであれば、
バッファリング容量を入力する容量より大きくして
バッファリングを行バッファリングからフルバッファリングに変えれば、
fgets でも大丈夫だと思いますよ。

https://www.k-cube.co.jp/wakaba/server/func/setvbuf.html

投稿2021/10/20 10:07

PingHermit

総合スコア478

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

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

D-4C

2021/10/20 10:41

コメントありがとうございます。入力する数字は作りたい模様によって変わるので容量については特に決めてないませんが、是非参考にさせていただきたいと思います。fgets関数についての情報ありがとうございました!
guest

0

割り込んで置き換え作業が実行されてしまう。

「十分な大きさのバッファに処理結果をため込んでおいて,入力が終わった後にその内容を表示」という方針の簡素な例を示します.

※注:このコード例ではバッファが溢れた場合のことを考慮していません.

C

1int main() 2{ 3 // 4 //何かてきとーにでかいバッファを用意しておいて… 5 // 6 const int OUT_BUFF_SIZE = 256; //※(2次元の)結果全てを入れるのに十分な大きさにする 7 char OutputBuffer[OUT_BUFF_SIZE]; 8 for( int i=0; i<OUT_BUFF_SIZE; ++i ){ OutputBuffer[i]=' '; } 9 10 // 11 //入力中には,バッファ内に結果を作り… 12 // 13 int line_start_index=0; //OutputBuffer[line_start_index] が行頭の場所 14 int LastIndex = -1; //OutputBuffer[]に何かが突っ込まれた最も大きいindex 15 int val=-1; //入力された数値の決定作業用 16 int c; //fgetc()の戻り値用 17 while( (c=fgetc(stdin)) != EOF ) 18 { 19 if( c=='\n' || c==' ' ) 20 { 21 if( val>=0 ) 22 { 23 int idx = line_start_index+val; 24 OutputBuffer[idx] = '#'; 25 val = -1; 26 if( idx>LastIndex ){ LastIndex = idx; } 27 } 28 if( c=='\n' ) 29 { 30 OutputBuffer[LastIndex+1] = '\n'; 31 ++LastIndex; 32 line_start_index = LastIndex + 1; 33 } 34 } 35 else if( '0'<=c && c<='9' ) 36 { 37 int d = c - '0'; 38 val = ( val<0 ? d : val*10 + d ); 39 } 40 else 41 { 42 printf( "Invalid Input" ); 43 return 0; 44 } 45 } 46 OutputBuffer[LastIndex+1] = '\0'; 47 48 // 49 //最後にバッファ内容を表示する 50 // 51 printf( "%s", OutputBuffer ); 52 return 0; 53}

投稿2021/10/20 09:43

fana

総合スコア11996

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

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

D-4C

2021/10/20 10:46

コード例ありがとうございます。 バッファについては理解が浅い部分がかなり多いので、こちらの具体例をもとに色々いじってプログラムを組みたいと思います!
guest

0

ベストアンサー

fgets は改行までを取得する関数です。ですので改行を含める入力は不可能でしょう。
入力と表示を繰り返すコードになっているのですから、入力すれば出力されるのは当然です。
入力後に表示をしたいのであれば、入力を全て貯めておき、入力終了後にその貯めたデータを元に表示すべきと思います。

なお、 char str2[128] に対し for(s=0; s<128; s++) { str2[s]=' '; } としているのは問題があります。
c の文字列は最後に '\0' が入る文字配列です。128 の領域全てを空白で埋めると '\0' が入る余地が無く、また、入れる処理もありません。

投稿2021/10/20 07:45

jimbe

総合スコア13209

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

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

D-4C

2021/10/20 08:38 編集

fgets関数なら一気に入力できるなどとネットでみた気がしたのですが勘違いだったようですね。ありがとうございます。ならばfgets関数はこのプログラムを組むにあたっては余り役に立たないと考えるべきでしょうか。 あと\0の指摘、ありがとうございます。完全に忘れていました…
jimbe

2021/10/20 09:32

> fgets関数なら一気に入力できるなどとネットでみた気がした ネットの情報がどのような意図でその言葉になったのかに因ると思います。 fscanf のように値毎に取り出すのと比べれば、行として「一気に入力できる」と言えるかもしれません。 もし改行も含めて入力できるとしたら、では入力の終わりはどう指定するのかと疑問に思います。 必要なのはそのような「また聞き」の情報では無く、fgets のドキュメントの情報ではないでしょうか。 > ならばfgets関数はこのプログラムを組むにあたっては余り役に立たないと~ どうするにしろ、より問題なのはデータの保存領域の見積もりです。 ご提示のコードでは 128+2 (この+2が何なのか分かりませんが) の buf に入力を入れていますが、改行も含めて入力されたとして、「例えば、下のように~」とされた入力を受け止め切れる大きさでしょうか。 入力の行当たり文字数・行数等に一定の限界を決めておられるのであれば、必要最大量は計算できます。その分の領域を確保し、あとは fgets で行の入力を繰り返し行ってその領域に追加していけば良いだけと思います。
guest

0

fgets で 1行入力したら、あとは strtod で '#' の表示位置を読み取って、
前の位置との差を表示幅として printf すればよいでしょう。

C

1#include <stdio.h> // fgets, printf, putchar 2#include <stdlib.h> // strtol 3 4int main(void) 5{ 6 for (char s[256], *p, *q; p = fgets(s, sizeof s, stdin); putchar('\n')) 7 for (int a = -1, b; b = strtol(p, &q, 10), q != p; a = b, p = q) 8 printf("%*c", b - a, '#'); 9}

追記
質問を誤解していたようです。
標準入力をファイルに切り替えずに、キーボードからデータを入力すると
1行入力するごとに出力され、表示が混ざるということを解決したかったのですね。

とりあえず、入力を全部読んでしまうことにします。

C

1#include <stdio.h> // fgets, printf, putchar 2#include <stdlib.h> // strtol, free 3#include <string.h> // strdup 4 5int main(void) 6{ 7 char s[256], *t[1000]; 8 int n; 9 for (n = 0; n < 1000 && fgets(s, sizeof s, stdin); n++) 10 t[n] = strdup(s); 11 for (int i = 0; i < n; i++) { 12 char *p = t[i], *q; 13 for (int a = -1, b; b = strtol(p, &q, 10), q != p; a = b, p = q) 14 printf("%*c", b - a, '#'); 15 putchar('\n'); 16 } 17 for (int i = 0; i < n; i++) free(t[i]); 18}

投稿2021/10/20 10:42

編集2021/10/20 10:59
kazuma-s

総合スコア8224

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

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

fana

2021/10/20 11:03

これは, 【1行入力するごとに1行分の出力が出てきてしまう】 という,解決すべき課題を解消するものになっているのでしょうか?
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問