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

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

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

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

Q&A

3回答

6602閲覧

scanfで区切り文字を使用した複数入力について

err0r

総合スコア20

C

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

0グッド

2クリップ

投稿2019/07/20 18:03

scanfでの複数入力について教えてください。
下記コードに対して、キーボードからTaro,30,maleと入力すると、
出力が name = taro,30,male,age = 0, gender = p@ となり、
構造体のnameメンバに入力値が全て格納されてしまいます。
scanfの指定子を"%s%d%s"に変更して、入力をTaro 30 male にすると正しく読み込まれます。
入力指定子の間に区切り文字を入れても入力できると教科書には書いてありますが、
何が原因でエラーになっているのでしょうか?

#include <stdio.h> typedef struct{ char name[10]; int age; char gender[10]; }student; void input(student *data){ printf("input name,age,gender :" ); scanf("%s,%d,%s", data->name, &data->age, data->gender); } void output(student *data){ printf("name = %s,age = %d, gender = %s", data->name, data->age, data->gender ); } int main(void){ student data; input(&data); output(&data); return 0; }

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

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

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

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

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

guest

回答3

0

そもそものはなしですが、
scanf系の関数は危険な振る舞いをするため、使用は推奨されません。
業務での使用は禁忌となってるところがほとんだと思います

ということで、そういう関数の使用法を無理して覚えたところで、無駄にしかならない、というのは覚えておきましょう

投稿2019/07/20 23:50

y_waiwai

総合スコア87747

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

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

thkana

2019/07/21 01:13

質問の解決に寄与しない回答のため、低評価します。 適切な代案を示すべきと思います。
y_waiwai

2019/07/21 01:18

どうぞ。 あなたはあなたの信じるように行動し、 私は私の信念に従って行動するのみですw
thkana

2019/07/21 01:27

行動様式はあなたの他の(たくさんの)回答を見てれば大体わかりますが、「信念」というほど強固なものだったというのはちょっと驚きでした。 他人の行動を好きに変えられるなんて期待していませんのでどうぞ続けて下さい。
err0r

2019/07/21 06:30

scanfが業務で推奨されないのは知りませんでした。頭に入れておきます。 代案があると嬉しいのはそうですが、こういった情報も非常にありがたいので引き続きご指導頂けますと幸いです。
episteme

2019/07/21 08:26

評価+1した。scanfこねくり回して解決するのは下策だから。 fgetsあたりで一行分まるっと読んでstetokなり正規表現なりで切り分けるのがオススメ。
err0r

2019/07/21 08:48

scanfしか使ったことのない初心者ですが、fgetsとstrtokを調べてみます。
退会済みユーザー

退会済みユーザー

2019/07/21 09:38

C言語の標準入力はわっけわかんねぇな。 まぁ、episteme が高評価してるってぇことは、それが正しいって事なんだろうな。
guest

0

まず, エラーにはなっていません.scanf はその実装に基づいて正常に動作しているものと思います.
%s は, 空白文字までの全ての文字を取り込みます. 後続に %d があるとかは気にしません.
カンマまでの文字列を取得するのでしたら, "%s," ではなく "%[^,]," といった書式にする必要があります.

投稿2019/07/20 19:37

jimbe

総合スコア12625

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

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

err0r

2019/07/21 07:07

%sはそういう実装になっているんですね。誤解しておりました。"%[^,]"で,を読み取り対象から外す事で動作するようになりました。 %dは","を読み取らない実装になっているのでしょうか?
guest

0

書式の "%s,%d,%s" を " %[^,],%d,%s" に変更してください。
%[ の直前のスペースは重要です。
これがないと、2行以上読み込む場合におかしなことになります。
なぜそうなるのか自分で確かめてください。
自分で考えて調べて、それでも分からない場合は質問してください。
詳しく説明します。
###追記

これがないと、2行以上読み込む場合におかしなことになります。

この意味がうまく伝わらなかったようなので、説明を追加します。

C

1#include <stdio.h> 2 3typedef struct { char name[10]; int age; char gender[10]; } student; 4 5int input(student *data) 6{ 7 printf("input name,age,gender :"); 8 return scanf(" %[^,],%d,%s", data->name, &data->age, data->gender) == 3; 9} 10 11void output(const student *data) 12{ 13 printf("name = %s, age = %d, gender = %s\n", data->name, data->age, data->gender); 14} 15 16int main(void) 17{ 18 student data[100]; 19 for (int i = 0; i < 100 && input(&data[i]); i++) 20 output(&data[i]); 21}

このプログラムを実行して、次の入力を与えてみてください。

taro,30,male aya,18,female hirosi,23,male

上手く行くと思います。
しかし、%[^] の前のスペースを取り除くとどうなるでしょうか?
その結果に満足ですか?
###追加説明
scanf でググっても、入門ページばかりヒットして、そこには簡単な説明しかないようです。
man scanf でググると、マニュアルページが出てきて、scanf の仕様の詳細を知ることができるでしょう。

%d が何を読み込むかご存知でしょうか?
ほとんどの人が数字の列だと思っているでしょう。
実は、数字の前に + か - の符号があってもいいのです。
さらに、0個以上の空白があってもいいのです。
空白というのは、スペース、タブ、改行などです。

%d に限らず、%f でも %s でもほとんどすべての変換指定が
0個以上の空白を読み飛ばしてから指定の文字列を読み取り値に変換します。
例外は %c と %[ です。
この 2つの変換指定は先頭の空白の読み飛ばしを行いません。

scanf の書式には、0個以上の指令が含まれます。
指令には、次の 3種類があります。
(1) 空白(スペース、タブ、改行など。isspace参照)、
(2) %でも空白でもない通常の文字、
(3) %で始まる変換指定。

"30,male\n" という入力が、"%d,%s" で読み込めるのは
変換指定 "%d" が "30"を読み込み、
通常の文字 "," が "," を読み飛ばし、
変換指定 "%s" が "male" を読み込むからです。

"\n" は入力バッファに残されています。
次の行の入力の書式にはこれを読み飛ばす指令がないといけません。

"%s" なら、空白(改行)を読み飛ばしますから問題ありません。
"%[" なら、空白(改行)を読み飛ばさないので問題です。
そこで、指令の(1) のスペースをつけて明示的に空白(改行)を読み飛ばします。

scanf は仕様が複雑なため、ほとんどの人がその詳細を理解せず、いい加減に
使用してトラブルに巻き込まれ、使わないほうが良いと結論付けています。

scanf は、「指定した書式に従う入力しか読み込まない」という素直で頑固な
入力関数なのです。

投稿2019/07/21 04:28

編集2019/07/21 10:31
kazuma-s

総合スコア8224

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

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

err0r

2019/07/21 06:57

[^,]を使用する事で”,”以降の文字は読み取らなくなる事は理解できました。 %dの方は、なぜ不要なのでしょうか? %[ の直前のスペースについてですが、入力を途中に改行を入れて下記のように2行にして確認しましたが、スペースの有無に寄らず、改行も含めて入力通りに読み取られました。(char name [64]に変更しました。) taro is single guy who is very rich ,30,male
err0r

2019/07/21 08:43

ありがとうございます。 スペースを除くと、2行目以降は改行が余計に読み込まれていました。 %[ の直前のスペースは改行文字をskipする効果があるのでしょうか? すいません、ネットで検索しても良い解説が見つけられず。。 また入力指定子を"%s%d%s"として、入力をカンマではなく下記のように空白で区切る場合は、余計な改行が読み込まれる事はありませんでした。 なぜそうなるのでしょうか? 思考停止で質問ばかりで申し訳ないですが、教えていただけると嬉しいです。 taro 30 male aya 18 female hirosi 23 male
err0r

2019/07/21 16:13

詳細な解説ありがとうございます。おかげさまでスッキリ理解できました。 %dは数字だけ読み取りすると思っていましたし、空白が指令になるのも理解しておりませんでした。 お恥ずかしながらscanfの入力指定子について、よく理解せずに使っておりました。 またほかの方がおっしゃる用にscanfが推奨されない理由もよくわかりました。 また下記例外入力処理の部分も参考にさせていただきます。 return scanf(" %[^,],%d,%s", data->name, &data->age, data->gender) == 3;
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問