ページ 11

ファイルから_tcstokで分解/代入する処理が上手くいきません

Posted: 2015年4月12日(日) 11:30
by ひじきぱっぱ
はじめまして、ひじきぱっぱと申します。
Win32APIでタイピングアプリを作っているのですが、
fgettsでファイルから1行読み取り,_tcstokで分解/代入する処理が上手くいきません。
どなたか教えて頂けませんか?

<ソースコード>
気になっているのは35-40行目付近の処理です。
※Win32のプロジェクトを作って頂き、この関数を呼び出して頂けると、症状を再現できます。

コード:

void readFileAndSetWords(HWND hWnd)
{
	TCHAR ** wordsArray;//問題を保持する二次元配列
	TCHAR ** rubysArray;//問題のルビを保持する二次元配列
	int wordNum = 0;//ファイルから読み取った問題数
	FILE *fp = NULL;
	TCHAR stringBuff[256];//ファイルから1行読み取った文字列を格納するバッファ
	TCHAR **tmpBuffOne = NULL;//realloc時に使う一時領域
	TCHAR *tmpBuffTwo = NULL;
	TCHAR szBuf[256];//エラー時のメッセージ表示用文字列

	//wordsArrayとrubysArrayの一次元目の領域を確保
	wordsArray = (TCHAR **)malloc(sizeof(TCHAR *)*1024);
	if(wordsArray==NULL)
	{
		wsprintf(szBuf,TEXT("Failed to malloc wordsArray."));
		MessageBox(hWnd,szBuf,TEXT("error"),MB_OK);
	}
	rubysArray = (TCHAR **)malloc(sizeof(TCHAR *)*1024);
	if(rubysArray==NULL)
	{
		wsprintf(szBuf,TEXT("Failed to malloc rubysArray."));
		MessageBox(hWnd,szBuf,TEXT("error"),MB_OK);
	}

	//ファイルをUTF-16で開く
	fp = _tfopen(_T("genre\\genre_test.txt"),_T("r, ccs= UTF-16LE"));
	if(fp==NULL)
	{
		wsprintf(szBuf,TEXT("Failed to open wordsfile."));
		MessageBox(hWnd,szBuf,TEXT("error"),MB_OK);
	}

	//wordsArrayとrubysArrayの二次元目に値を格納
	while(_fgetts(stringBuff,256,fp))
	{
		wordsArray[wordNum] = _tcstok(stringBuff,_T(","));
		rubysArray[wordNum] = _tcstok(NULL,_T(" ,"));
		wordNum++;
	}

	//wordsArrayとrubysArrayの一次元目を、reallocでサイズ調整
	tmpBuffOne = (TCHAR **)realloc(wordsArray,sizeof(TCHAR *)*wordNum);
	if(tmpBuffOne != NULL)
	{	
		wordsArray = tmpBuffOne;
	}else{
		free(wordsArray);
		MessageBox(hWnd,TEXT("Failed to realloc wordsArray."),TEXT("error"),MB_OK);
	}

	tmpBuffOne = (TCHAR **)realloc(rubysArray,sizeof(TCHAR *)*wordNum);
	if(tmpBuffOne!= NULL)
	{	
		rubysArray = tmpBuffOne;
	}else{
		free(rubysArray);
		MessageBox(hWnd,TEXT("Failed to realloc rubysArray."),TEXT("error"),MB_OK);
	}

	//ファイルのクローズ
	fclose(fp);
}
<症状>
_tcstokを使って、文字列を区切って代入する処理が上手くいきません。
1回目の呼び出しは、wordsArray[0]とrubysArray[0]に正しい値が入るのですが、
2回目以降の呼び出しで、不正な値が格納されてしまいます。
また、2回目以降の呼び出しでは、wordsArray[0]等、手前の要素にも不正な値が代入されてしまいます。
(デバッグ時のキャプチャを添付しますので、ご参照ください。)

<ソースから読みだしているテキストファイル>
以下になります。各行にタイピング用の問題とルビをカンマ区切りで格納しています。

コード:

genre,ジャンル
test,テスト
file,ファイル
coffee,コーヒー
TextBook,教科書
Pencil,鉛筆
NoteBook,ノート
Typing,タイピング

Re: ファイルから_tcstokで分解/代入する処理が上手くいきません

Posted: 2015年4月12日(日) 16:24
by みけCAT
_tcstokがstrtokと似た動作をすると仮定すると、返り値は入力のバッファへのポインタであり、次の入力により上書きされる可能性があります。
_tcstokの返り値はそのまま配列に保存するのではなく、一旦変数に入れて、十分な長さの領域を確保し、データをコピーし、その領域へのポインタを配列に保存してください。

Re: ファイルから_tcstokで分解/代入する処理が上手くいきません

Posted: 2015年4月12日(日) 17:47
by ひじきぱっぱ
みけCATさん

コメントありがとうございます。

デバッガで再度動きを確認してみたところ、
ご指摘通りの現象が起こっておりました。

strtokは引数に渡したバッファの領域を指すポインタを返すので、
ポインタが指す領域自体を書き変えてしまえば、
そのポインタを保持している変数も、違う領域を見てしまうということですね。

教えて頂いた通り、データを別領域に移して、別領域のポインタを
配列に保存するようにしたところ、上手く動作しました。

教えて頂き、ありがとうございました。