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

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

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

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

Q&A

解決済

4回答

27689閲覧

[C言語] 一行に並ぶスペースで区切られた個数が不定の数値の読み取り

Sophian

総合スコア36

C

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

0グッド

2クリップ

投稿2016/03/15 01:43

編集2016/03/16 06:50

###前提・実現したいこと
C言語によって一行に並ぶスペースで区切られた個数が不定の数値の読み取りを実現したいです。例えば入力が、

4 4 6 2 8 6 4 10 51 4 3 1 2 13 4
  • 最初に以降いくつの数値が並ぶかの入力がある
  • 以降その個数だけ数値が並ぶ
  • 各数値の間はスペースで区切られている
  • 改行は最初に指定した個数を全て入力し終わった際にのみ

M 数値1 数値2 数値3 … 数値M

となっていた場合、上記3行の入力中に出力が行われないようにして上記の数値を全て変数、もしくは配列に数値として個別に格納することは可能でしょうか。
よろしくお願いします。

###発生している問題
例えば最初の1文字だけ変数に入力し、以後その分だけFor文で繰り返し配列に代入する、みたいなことを考えてました。
scanfを使い試してみたのですが、一行の個数が変動する数値の入力に対して記述の仕方が全く分かりませんでした。

mallocによる記述も試みましたが、最初の個数を入力する段階で改行されている必要があり、このように一行の入力に対して上手く記述できませんでした…。

文字列として読み取り、数値変換することも考えましたが、

  • スペースが含まれる
  • 二桁の入力もある
  • 結合して数値としたいわけではなく、個別に値を扱いたい

等の理由から気が遠くなり、もし数値としてそのまま格納する方法があるのならば、と思い質問させていただきました。

解答よろしくお願いします。

###環境
Eclipse Cpp Mars
MinGW

≪追記≫
どれも非常に興味深く、大変勉強になり、どれをベストアンサーにすべきか悩みましたが、最終的にコードが一番短く、分かりやすい記述となった解答をベストアンサーとさせていただきました。
ありがとうございました!

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

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

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

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

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

guest

回答4

0

読み込んだ1行分にstrtokかませばいいのでは?
C言語関数辞典 - C言語Tips集 文字列を指定文字で分割する

投稿2016/03/15 01:56

編集2016/03/15 02:25
tkturbo

総合スコア5572

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

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

Sophian

2016/03/16 05:52 編集

解答ありがとうございます!こんな便利な関数があったのですね! tkturboさんのstrtok、vohhoyさんのダンプ、cateyeさんのfgets、皆さんからいただいたアドバイスを元に記述してみました。 入力をコピペし”input.txt”に保存し、以下を実行したら無事に出力されました! #include <stdio.h> #include <stdlib.h> #include <string.h> #define N 256 int main(void) { int i=0,j,data[10][10],num[3]; FILE *fp; char *filename = "input.txt"; char readline[N] = {'\0'}; if ((fp = fopen(filename, "r")) == NULL) { fprintf(stderr, "Failed to open %s.\n", filename); exit(EXIT_FAILURE); } while ( fgets(readline, N, fp) != NULL ) { num[i] = atoi(strtok(readline," ")); for(j=0;j<num[i];j++){ data[i][j]= atoi(strtok(NULL," ")); } i++; } fclose(fp); //確認用 for (i = 0;i<3; i++) { printf("data[%d]=", i); for (j = 0; j < num[i]; j++) printf("%c%d", (j?',':'{'),data[i][j]); printf("}\n"); } return EXIT_SUCCESS; } ありがとうございました!!
guest

0

一行の最大文字数が分かっているなら、fgets()で一行丸ごと読み込んで処理するほうが問題が少ないように思います。
1.数値はstrtol()で取得。
2.戻ってきた変換不能文字列のアドレスから空白を読み飛ばす。
3.行末でなければ1に戻る。
参考:strtol

投稿2016/03/15 02:45

編集2016/03/15 02:46
cateye

総合スコア6851

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

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

Sophian

2016/03/16 23:04 編集

またしても目から鱗な関数が…。私のコードの場合、strtok()を使用するよりもstrtol()を使用したほうがatoi()関数を省けるため、すっきりした形になりました! また、strtol()の場合、数字ではない変換不可能な文字が先頭に来ていた場合はエラーを返す、との記述がありましたので、②はそのためにスペースを飛ばすのだと認識したのですが、読み取る文字列の最初のスペースは自動的に省いてくれるようです。なので②の手順は記述することなく済みました。 [数字1][スペース][数字2][スペース][数字3][スペース][数字4] という入力に対して、strtol()により 数値=[数字1] 文字列=[スペース][数字2][スペース][数字3][スペース][数字4] となり、なぜかこの後 [スペース][数字2][スペース][数字3][スペース][数字4] をstrtol()すると、最初のスペースを無視し、 数値=[数字2] 文字列=[スペース][数字3][スペース][数字4] となりました。 なので入力と、文字列の出力を同じ場所にしてForで繰り返し数字だけ抽出することができました! #include <stdio.h> #include <stdlib.h> #include <string.h> #define N 256 int main(void) { int i=0,j; long data[10][10],num[3]; FILE *fp; char *filename = "input.txt"; char readline[N] = {'\0'}; char *temp; if ((fp = fopen(filename, "r")) == NULL) { fprintf(stderr, "Failed to open %s.\n", filename); exit(EXIT_FAILURE); } while ( fgets(readline, N, fp) != NULL ) { num[i] = strtol(readline,&temp,0); for(j=0;j<num[i];j++){ data[i][j]= strtol(temp,&temp,0); } i++; } fclose(fp); //確認用 for (i = 0;i<3; i++) { printf("data[%d]=", i); for (j = 0; j < num[i]; j++) printf("%c%d", (j?',':'{'),data[i][j]); printf("}\n"); } return EXIT_SUCCESS; } ありがとうございました!
guest

0

ベストアンサー

こんにちは。

改行ミスのチェックをしないでよければ、単純にscanf("&d", &data);で次々と読みだしていけば良いはずですが、何がうまく行かないのでしょうか?
1行分については下記イメージです。(コンパイルしてませんので、エラーが出たら適宜修正下さい。)

C

1int num=0; 2if (scanf("%d", &num) == 1) 3{ 4 // ここで必要な個数のデータ配列を獲得する。 5 // malloc/freeはリークの原因になるので使わないで済む時は使わない方が良い。 6 // 望ましくはC++でstd::vectorを使う。 7 // Cに拘るならVLAも有り。 8 for (int i=0; i < num; ++i) 9 { 10 int data=0; 11 if (scanf("%d", &data) == 1) 12 { 13 // ここで、i番目のdataについて処理する。 14 } 15 else 16 { 17 // 読出エラー 18 } 19 } 20 // ここで、1行分のデータの処理を行う。 21} 22else 23{ 24 // 読出エラー 25}

【追記】

mallocによる記述も試みましたが、最初の個数を入力する段階で改行されている必要があり、このように一行の入力に対して上手く記述できませんでした…。

あ、これ勘違いです。1行を最後まで入力してEnterを押せば、ちゃんと読まれます。
入力バッファがあって、Enterが押されるまでそこに溜まっているのですよ。
Enterが押されたら、入力バッファの内容が先頭から順番に読み出されます。

投稿2016/03/15 02:05

編集2016/03/15 02:14
Chironian

総合スコア23272

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

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

Sophian

2016/08/25 03:04 編集

解答ありがとうございます! 正直なところ、そもそもprintfの基本的なことすらわかっていない部分がありました…。 Chironianさんのアドバイスを元に記述してみました!無事に入力を読み取ることができました! 追記、ありがとうございます!なるほど…、バッファについても学習したいと思います。あとでmallocでの記述も試してみたいと思います! #include <stdio.h> int main(void){ int num[10],i,j; int data[10][10]; for(i=0;i<3;i++){ if (scanf("%d", &num[i]) == 1) { for (j=0; j < num[i]; j++) { if (scanf("%d", &data[i][j]) == 1) { } else { // 読出エラー } } } else { // 読出エラー } } for (i = 0;i<3; i++) {       //確認用 printf("data[%d]=", i); for (j = 0; j < num[i]; j++) printf("%c%d", (j?',':'{'),data[i][j]); printf("}\n"); } return 0; } ありがとうございました!
Sophian

2016/03/16 06:02

あと、バグ回避のためにif内にscanfを記述している事にも驚きました!積極的に使っていこうと思います。
guest

0

1回のscanf関数で不定個数の数値を読み取ることはできないので、素直にforループを回すのが良いと思います。

#include <stdio.h> #define GROUPNUM 5 #define SLOTNUM 10 int main() { int nums[GROUPNUM]; int data[GROUPNUM][SLOTNUM]; int ret, v, i, j; /* データ読み込み */ for (i = 0; i < GROUPNUM; i++) { ret = scanf("%d", &nums[i]); if (ret < 1) goto exit_read; for (j = 0; j < nums[i]; j++) { ret = scanf("%d", &v); if (ret < 1) { /* データ不足: 有効個数を減らす */ nums[i] = j; goto exit_read; } if (j < SLOTNUM) data[i][j] = v; } } exit_read: /* 読み込めた値をダンプ(確認用) */ for (i = 0; 0 < nums[i] && i < GROUPNUM; i++) { printf("data[%d]=", i); for (j = 0; j < nums[i] && j < SLOTNUM; j++) printf("%c%d", (j?',':'{'),data[i][j]); printf("}\n"); } }

投稿2016/03/15 01:57

yohhoy

総合スコア6191

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

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

Sophian

2016/03/16 05:45 編集

なるほど!わざわざコードまで書いてくださり、ありがとうございます!特に printf("%c%d", (j?',':'{'),data[i][j]); この書き方は大変勉強になりました!! 当方基礎がしっかりできていないのもあり、Google片手にしばらく睨めっこしてましたが、非常にいい経験になりました!使わせていただきます!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問