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

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

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

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

Q&A

解決済

4回答

4814閲覧

fgetsを使った文字列の分割

tento65

総合スコア11

C

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

0グッド

0クリップ

投稿2017/06/06 04:58

###前提・実現したいこと
AOJ 1_5Aの問題で、よくないとされるscanf以外を使用した解決を図りたいです。

問題内容は、
トランプの枚数が足りないので現在持っているカードを入力して、そこから何が足りないかを出力するというものです。

###発生している問題
入力された文字を分割ができない

###該当のソースコード

#include <stdio.h> #include <string.h> #include <stdlib.h> int main(void) { char tmp[1000]; //現在のカード枚数・数字に変換 fgets(tmp, sizeof(tmp), stdin); int loop=atoi(tmp); int spade[13] = { 1,2,3,4,5,6,7,8,9,10,11,12,13 }; int heart[13] = { 1,2,3,4,5,6,7,8,9,10,11,12,13 }; int dia[13] = { 1,2,3,4,5,6,7,8,9,10,11,12,13 }; int club[13] = { 1,2,3,4,5,6,7,8,9,10,11,12,13 }; int i; //カード枚数ループ for (i = 0; i < loop;i++) { //マークと数字を入力・分割 char card[10]; fgets(card, sizeof(card), stdin); char mark; int num; sscanf(card,"%c%d",mark,&num); //マークで判断 //対応した数字を引くことで不足ではないことを確認 switch (mark) { case 'S': spade[num - 1] -= num; break; case 'H': heart[num - 1] -= num; break; case 'C': club[num - 1] -= num; break; case 'D': dia[num - 1] -= num; break; } } //カード枚数ループ終了 //出力は完成しているので省略

###試したこと
strtokによる文字列の分割

数字の分割はatoi(strtok())で行えましたが、fgetsによる分割がどうしてもできませんでした。
fgetsとsscanfについて調べても、元々入力されていたものからの分割、ファイル読み込みからの分割方法しかわかりませんでした。

###補足情報(言語/FW/ツール等のバージョンなど)
paiza.io
visual studio

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

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

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

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

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

guest

回答4

0

よくないとされるscanf以外を使用した解決を図りたいです。

こういった競技プログラミングに限って言えば、scanfは適切な選択肢だと考えます。というのも、入力の形が厳密に決まっているので、エラーハンドリングが不要だからです。

もちろん、scanfでもうまく使えばきっちりエラー処理もできますが、1行読んでパースしたほうが確実だとは思います。

AOJの主眼はアルゴリズムにありますので、入出力部分にこだわるよりは別な問題を進めていったほうがいいのかなというのが、個人的な感覚です。

投稿2017/06/06 05:39

maisumakun

総合スコア145183

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

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

0

こんにちは。

回答ではないのですが、追記・修正依頼欄では狭いのでこちらから。

よくないとされるscanf以外を使用した解決を図りたいです。

その時の代表的な対処が fgets → sscanfと思います。
その部分はできているような印象を受けますが、何が問題なのでしょうか?
「期待する動作」と「実際の動作」を具体的に書いて頂けると回答しやすいです。


ところで、scanfを安易に使うとバッファオーバーフローが起きるので良くないとされます。
しかし、読み込んだ数値numを使ってなんのチェックもしないまま配列アクセスするとscanfを安易に使った時と同様に不正メモリ・アクセスにつながりますよ。

投稿2017/06/06 05:46

Chironian

総合スコア23272

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

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

tento65

2017/06/06 05:53

このコードでmarkとnumを出力すると、markはすべて空白、numにはアドレス(?)が入っているだけの状態になっていて、switchによる判定はできなかったです。
Chironian

2017/06/06 07:00

あ、なるほど。 誤>sscanf(card,"%c%d",mark,&num); 正>sscanf(card,"%c%d",&mark,&num); ですね。markへ値を読み出すためには、markのアドレスをsscanfへ渡す必要が有ります。 > numにはアドレス(?)が入っているだけ numの表示方法を間違ったのだと思います。 例えば下記のようにして表示すれば適切な値が入っていることを確認できます。 printf("mark=[%c] num=%d", mark, num); fflush(stdout); (たぶん、&numとしてnumのアドレスを表示されたのではないでしょうか?)
guest

0

ベストアンサー

fgetsによる分割、の意味がよくわかりませんが、fgetsで取得した文字列を分割する方法で良いでしょうか。
現状のsscanfを見ると、書式としては、1文字+数値、になっていると思うので、次のような処理で良いのではと思います。

c

1// 先頭1文字を取得 2mark = card[0]; 3// 2文字目以降を数値に変換 4num = atoi(&card[1]);

投稿2017/06/06 05:32

ttyp03

総合スコア16998

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

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

tento65

2017/06/06 05:45

ありがとうございます。端折って文を書いてしまい分かりづらくなってしまいましたが希望通りの動きができました。 関数を使わないと抜き出せないと思っていました。 card[1]は空白ではないんですね。
ttyp03

2017/06/06 05:49

card[1]が空白かどうかは、何を入力したかによります。 S13 と入れれば'1'ですし、 S 13 と入れれば空白です。 入力の書式が厳密に、文字+空白+数値、であって、その通りに厳密に処理したいのであれば、 num = atoi(&card[2]); としてください。 ただ、atoiは先頭に空白があっても無視してくれるようなので、そこまで気にしなくても良いかもしれません。
guest

0

fgetsで作成してみました

#include <stdio.h> #include <string.h> #include <stdlib.h> int main(void) { char tmp[1000]; //現在のカード枚数・数字に変換 fgets(tmp, sizeof(tmp), stdin); int loop=atoi(tmp); int spade[13] = { 1,2,3,4,5,6,7,8,9,10,11,12,13 }; int heart[13] = { 1,2,3,4,5,6,7,8,9,10,11,12,13 }; int dia[13] = { 1,2,3,4,5,6,7,8,9,10,11,12,13 }; int club[13] = { 1,2,3,4,5,6,7,8,9,10,11,12,13 }; int i,j; char mark; int num; char card[10]; //カード枚数ループ for (i = 0; i < loop;i++) { //マークと数字を入力・分割 mark='\0'; num=0; fgets(card, sizeof(card), stdin); for (j = 0; card[j] != '\n'; j++){ if (card[j] == ' ') continue; if (card[j] < '0' || card[j] > '9'){ mark=card[j]; }else{ num*=10; num+=(int)(card[j] - '0'); } } //マークで判断 //対応した数字を引くことで不足ではないことを確認 switch (mark) { case 'S': spade[num - 1] -= num; break; case 'H': heart[num - 1] -= num; break; case 'C': club[num - 1] -= num; break; case 'D': dia[num - 1] -= num; break; } } //カード枚数ループ終了 for (i=0;i<13;i++) if(spade[i] != 0) printf(" S:%d\n",spade[i]); for (i=0;i<13;i++) if(heart[i] != 0) printf(" H:%d\n",heart[i]); for (i=0;i<13;i++) if(club[i] != 0) printf(" C:%d\n",club[i]); for (i=0;i<13;i++) if(dia[i] != 0) printf(" D:%d\n",dia[i]); }

投稿2017/06/06 07:02

編集2017/06/06 07:59
A.Ichi

総合スコア4070

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問