sscanf_sの使い方について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
はーむ

sscanf_sの使い方について

#1

投稿記事 by はーむ » 5年前

初めまして、Microsoft Visual Studio Express 2013 for Windows Desktopにて、C++でプログラミングを作っていて困ったことがあったので質問させていただきました。知識としては、学校で構造体の辺りまでのC言語の基礎を学んだところです。

今回作ろうとしていたプログラムは、ファイルに書き込まれている問題と答えを読み込んでクイズをするプログラムです。
そのプログラムを作る過程で
10
1+1=?,2
1+2=?,3
1+3=?,4
2+1=?,3
2+2=?,4
1+5=?,6
4+9=?,13
3+4=?,7
10+1=?,11
10-4=?,6
と書き込まれたファイルから、最初の一行で問題の数を取得し、カンマでquesとanswerの構造体のメンバに代入していこうというプログラムを以下のように書きました。

コード:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <fstream>
#include <string>
using namespace std;

#define MAX 140

/* 構造体の宣言 */
typedef struct Quiz {
	char ques[MAX], answer[MAX];
public:
} Quiz;

/* 関数のプロトタイプ宣言 */
void load(Quiz *pQ, FILE *fp);


/* ファイルを読み込んで構造体へ入れる */
void load(Quiz *pQ, char *fp){
	int i,line;
	ifstream ifs(fp);
	char str[256];

	/*エラー処理*/
	if (!ifs){
		cout << "Error: cannot open file" << endl;
		exit(1);
	}

	ifs.getline(str, 260);
	line = atoi(str); /* 行数を取得 */
	pQ = (Quiz *)malloc(sizeof(Quiz)*(line + 1));
	for (i = 0; i < line; i++){
		ifs.getline(str, 260);
		sscanf_s(str, "%s,%s", (pQ + i)->ques, (pQ + i)->answer); /* 構造体へ代入 */
	}	
	
}

int main(void){

	Quiz quiz[MAX];
	char file[256];
	
	cin >> file;
	
	load(quiz, file);

	return 0;
}
コンパイルしたところ、ファイル名を入力した直後に動作が止まってしまいました。
ブレークポイントを使ってデバックしたところ、ques側に1+1=?,2と代入されていて、answer側には何も代入されていないという状況でした。

おそらくsscanf_sでの処理が上手くいっていなかったのだと推測しましたが、調べてみてもこの関数の使い方がよくわからなかったので質問しました。
sscanfはセキュリティが甘いとのエラーが出てきてつかえなかったのですが、sscanf_sでも同じ引数の指定の仕方ではいけなかったのでしょうか…

または、もっと効率の良い方法がある場合は教えていただけるとありがたいです。

アバター
nullptr
記事: 239
登録日時: 8年前

Re: sscanf_sの使い方について

#2

投稿記事 by nullptr » 5年前

質問するのは結構ですが、まず自分でドキュメントを読む癖を付けましょう。嫌味ではなく、問題が起きた時にまず行うべき対応を身につけることが大事だからです。

_sのついた関数はMicrosoftが標準ライブラリをセキュリティ面で強化した関数で、元の関数とは使い方に必ず差があります。
公式ドキュメントは以下です。
http://msdn.microsoft.com/ja-jp/library/t6z7bya3.aspx
英語版からの引用になりますが、こう書かれています。
Unlike the less secure version sscanf, a buffer size parameter is required when you use the type field characters c, C, s, S, or string control sets that are enclosed in [].
日本語版では、
より安全なバージョンである sscanf とは異なり、型フィールド文字 c、C、s、S、または [] で囲まれた文字列のコントロール セットを使用する場合にバッファー サイズのパラメーターが必要です。
なかなか頓珍漢な変換ですが、要は、%s、%S、%c、%Cを使う場合は、引数に文字のサイズを指定する必要がある、ということです。
例えば、

コード:

char aa[10];
sscanf_s(a,"%s",aa,sizeof(aa));
のように、ポインタの直後にバッファーサイズを指定します。
 
 
✜ で C ご ✜
: す + 注 :
¦ か + 文 ¦
?
Is the は :
order C++? ✜
     糸冬   
  ――――――――
  制作・著作 NHK
 
 

アバター
みけCAT
記事: 6273
登録日時: 9年前
住所: 千葉県
連絡を取る:

Re: sscanf_sの使い方について

#3

投稿記事 by みけCAT » 5年前

はーむ さんが書きました:ブレークポイントを使ってデバックしたところ、ques側に1+1=?,2と代入されていて、answer側には何も代入されていないという状況でした。
これはscanfでも同じはずです。
書式文字列を"%s,%s"ではなく、"%[^,],%s"とするといいかもしれません。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
みけCAT
記事: 6273
登録日時: 9年前
住所: 千葉県
連絡を取る:

Re: sscanf_sの使い方について

#4

投稿記事 by みけCAT » 5年前

はーむ さんが書きました:

コード:

	char str[256];

// 中略

	ifs.getline(str, 260);
	line = atoi(str); /* 行数を取得 */
	pQ = (Quiz *)malloc(sizeof(Quiz)*(line + 1));
	for (i = 0; i < line; i++){
		ifs.getline(str, 260);
		sscanf_s(str, "%s,%s", (pQ + i)->ques, (pQ + i)->answer); /* 構造体へ代入 */
	}
ここのgetlineの使い方は危険です。
strは実際には256バイトしか確保されていないにもかかわらず、
ifs.getlineに260バイトまで入れていいよ、と伝えているので、バッファオーバーランのリスクがあります。

コード:

ifs.getline(str, sizeof(str));
とするのがいいでしょう。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

はーむ

Re: sscanf_sの使い方について

#5

投稿記事 by はーむ » 5年前

皆さんの仰るとおりにやったら無事成功しました。ありがとうございます。

>>新ゝ月様
確かにmsdnライブラリは読み飛ばしがちになっていたことが多かったので反省します…もう少し根気強く読めばしっかり知りたいことは書いてあるのですね、大変良い勉強になりました。

>>みけCAT様
"%[^,],%s"とした方が、c++との相互性がよいのですね、そのあたりも改めて調べてみようと思います。
今回、私は要素数のケアレスミスをしてしまいましたが、sizeofを使えばその間違えも少なくなることも教えていただきありがとうございました。

閉鎖

“C言語何でも質問掲示板” へ戻る