ページ 1 / 1
構造体メンバへの格納法
Posted: 2014年1月09日(木) 18:44
by kopi
こんにちは。またお世話になります。
コード:
typedef struct node_tag{
int a;
double b;
struct node_tag *next;
}node;
例えば上のような構造体を宣言し、ファイルからデータを読み込み格納したい場合どのようにすればよいですか?
構造体で配列を宣言し、格納する方法はネットにもあったのですが、ポインタの場合はどうすればいのわからず・・・。
とりあえず下のように書いてみたのですが。
コード:
node *p;
FILE *fp;
fp = fopen("filename.txt", "r");
fscanf(fp, "%d %f", &p->a, &p->b); // pのメモリ領域は確保済みとします
ご教授お願いいたします。
環境
OS : windows 7 64bit
コンパイラ : Borland C++ Compiler 5.5.1
Re: 構造体メンバへの格納法
Posted: 2014年1月09日(木) 21:40
by softya(ソフト屋)
ポインタの場合は、自分でバイナリのデータ構造に展開するしかありません。
要素数も保存して配列に変えて保存するといったイメージです。実際には一要素ごとにファイルに書きだすのですが。
※ 自分の作るファイルですから自由に設計してもらってかまいません。
Re: 構造体メンバへの格納法
Posted: 2014年1月09日(木) 21:44
by softya(ソフト屋)
あれ? 読み込み元はテキストファイルなのでしょうか?
【補足】
テキストファイルから読むなら、一旦構造体の変数に格納してリスト用のメモリをmallocして内容をコピー、前のリストの最後にポインタを継ぎ足す感じです(要はリスト構造の作り方)。
Re: 構造体メンバへの格納法
Posted: 2014年1月09日(木) 22:00
by kopi
softya(ソフト屋) さんが書きました:あれ? 読み込み元はテキストファイルなのでしょうか?
はい、テキストファイルを想定しています。
データ数が仮に10000あったとすると、配列でその分を確保するのは困難かなーと思い、リスト構造をうまく使ってできないものかと考えたわけです。
しかし、この辺がまだはっきり理解できていないので、
データ数がこのように多い場合、どのようにして格納していけばよいのか、ご教授いただけないでしょうか。
オフトピック
実は今悩んでいるこの問題は、過去にこの掲示板で課題丸投げされたもので、
自分の練習がてら興味本位で解こうと思ってみたら、初っ端から躓いてしまったというわけです。
Re: 構造体メンバへの格納法
Posted: 2014年1月09日(木) 22:02
by softya(ソフト屋)
補足で書きましたが、ポインタによるリスト構造が理解できるならさほど難しくはないですよ。
Re: 構造体メンバへの格納法
Posted: 2014年1月09日(木) 22:39
by kopi
まだまだ改善の余地は大いにあると思いますが、ファイルからの数値を読むことができました。
コード:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define N 10
typedef struct node_tag{
int id[N];
int sub_a[N];
double sub_b[N], sub_c[N];
struct node_tag *next;
}node;
/* エラー表示用 */
void error(char *str)
{
printf("%s", str);
exit(1);
}
/* メモリ確保関数 */
node *getmemory(void)
{
node *p;
p = (node *)malloc(sizeof(node));
if(p == NULL)
error("Not enough memory");
return p;
}
/* ここは気にしないでください
node *gentree(node *p, int n)
{
if(p == NULL)
p = getmemory();
strcpy(p->id, n)
else if()
p->left = gentree(p->left, n)
}
*/
int main(void)
{
node *p, *head, *old;
FILE *fp;
char filename[41];
int i;
printf("Enter filename : ");
scanf("%40s", filename);
fp = fopen(filename, "r");
if(fp == NULL)
error("File not find");
head = getmemory(); // ダミーノード
old = head;
while( p = getmemory(), fscanf(fp, "%d %d %lf %lf", p->id, p->sub_a, p->sub_b, p->sub_c) != EOF){
old->next = p;
old = p;
}
old->next = NULL;
fclose(fp);
p = head->next;
while(p != NULL){
printf("%d %d %.3f %.3f\n", *p->id, *p->sub_a, *p->sub_b, *p->sub_c);
p = p->next;
}
return 0;
}
コメントアウトしてある関数は気にしないでください。何をしようとしているのかばれてしまうと思いますが(笑)
結果を出力した際、一つ気になることがあります。
今現在データの行数が1000で、各行に4つの要素(int型, int型, double型, double型の順)を持つデータを用意しています。
ポインタがNULLになるまでループするはずなので、出力結果としては1行目のデータから1000行目までが一気に出力されるのかと思いきや、
300行程度しか出力されません。
その部分の結果と元のデータは同じ値なので、一応リスト構造はできていると思うのですが、ご指摘していただけないでしょうか。
Re: 構造体メンバへの格納法
Posted: 2014年1月09日(木) 22:56
by softya(ソフト屋)
まず、
int id[N];
int sub_a[N];
double sub_b[N], sub_c[N];
は配列にするのが意味不明です。
fscnafならポインタを渡せば良いので、 int id;で宣言して&演算子( &(p->id) )で良いはずですが。
それと
リダイレクトでファイル出力してみましたが入力しただけファイルに出力されているようです。
入力でエラーが有った場合は、その限りではありません。
Re: 構造体メンバへの格納法
Posted: 2014年1月10日(金) 00:47
by kopi
softya(ソフト屋) さんが書きました:まず、
int id[N];
int sub_a[N];
double sub_b[N], sub_c[N];
は配列にするのが意味不明です。
fscnafならポインタを渡せば良いので、 int id;で宣言して&演算子( &(p->id) )で良いはずですが。
なるほど、私がfscanfの仕組みを理解せずに使っていたことが良く分かりました。ありがとうございます。
softya(ソフト屋) さんが書きました:
それと
リダイレクトでファイル出力してみましたが入力しただけファイルに出力されているようです。
入力でエラーが有った場合は、その限りではありません。
つまり、何らかのエラーがあるため、本来入力したい分のデータ数が入力できていないということですか?
もしそうだとしたら、どの辺が原因なのでしょうか。
Re: 構造体メンバへの格納法
Posted: 2014年1月10日(金) 04:39
by shirokosi
はじめまして。
参考になりそうなコードを書いてみました。
よろしければお使いください。
環境
OS : Windows 7 64bit
コンパイラ名 : VC++ 2012
コード:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
//#include <conio.h>
// 次のnodeへのポインタを持つ構造体
struct node_t {
int id;
int sub_a;
double sub_b, sub_c;
node_t *next; // 次のnode_tへのポインタ
};
// nodeの先頭と一番後ろを保持する構造体
// nodeの管理のために必要になる構造体
struct node_manager_t {
node_t *head; // 先頭
node_t *tail; // 末尾
};
node_manager_t* create_node_manager();
void delete_node_manager(node_manager_t* nmt);
int count_node_manager(node_manager_t* nmt);
bool input_node(node_manager_t* nmt, node_t *node);
void nmt_print(node_manager_t* nmt);
// node 管理用構造体用にメモリを確保
node_manager_t* create_node_manager()
{
node_manager_t *tmp;
tmp = (node_manager_t*) malloc(sizeof(node_manager_t));
if( tmp == NULL )
return tmp;
tmp->head = NULL;
tmp->tail = NULL;
return tmp;
}
// mallocで確保したものは開放する。
void delete_node_manager(node_manager_t* nmt)
{
if( nmt == NULL ) return; // 入力値検査
// まずnode_tを消す
for( node_t *p = nmt->head;
p!=NULL; )
{
node_t *temp = p; // いつ消すか・・・?
p = p->next; // 次のデータへ移動
free( temp );// ここで消す
}
// node_manager_tを消す
free( nmt );
}
// ノードの個数を数えます
int count_node_manager(node_manager_t* nmt)
{
if( nmt == NULL ) return 0;// 入力値検査
int counter=0;
for( node_t *p = nmt->head;
p!=NULL;
p=p->next )
{
counter++;
}
return counter;
}
// ノードの一番後ろにデータを突っ込みます
bool input_node(node_manager_t* nmt, node_t *node)
{
if( nmt == NULL ) return false; // 入力値検査
if( node == NULL ) return false; // 入力値検査
node_t *new_node = (node_t*)malloc(sizeof(node_t));
if( new_node == NULL )
return false;
// 単純にコピー
new_node->id = node->id;
new_node->sub_a = node->sub_a;
new_node->sub_b = node->sub_b;
new_node->sub_c = node->sub_c;
new_node->next = NULL;
// 初回入力
if( nmt->head == NULL &&
nmt->tail == NULL)
{
nmt->head = new_node;
nmt->tail = new_node;
}
else if( nmt->head && nmt->tail )
{
nmt->tail->next = new_node;
nmt->tail = new_node;
}
else // この条件にはならないはず。けど一応書く
{
free( new_node );
return false;
}
return true;
}
// データを表示してみる
void nmt_print(node_manager_t* nmt)
{
if( nmt == NULL ) return;
int node_num = count_node_manager(nmt);
printf("ノードの数:%d\n", node_num);
printf("表示開始!\n");
for( node_t *p = nmt->head;
p!=NULL;
p=p->next )
{
printf("%d %d %.3f %.3f\n", p->id, p->sub_a, p->sub_b, p->sub_c );
// _getch();
}
printf("表示おわり!\n");
}
/* エラー表示用 */
void error(char *str)
{
printf("%s", str);
exit(1);
}
node_manager_t *node_manager;
// テスト用のデータを作ります
void data_gen()
{
FILE *fp;
fp = fopen("test_data.txt", "w");
for(int i=0; i<1000; i++)
{
fprintf(fp, "%d %d %.3f %.3f\n", i, rand()%2000, (rand()%10000)*0.001, (rand()%10000)*0.001);
}
fclose(fp);
}
int main()
{
//data_gen();
node_manager = create_node_manager();
if( node_manager == NULL )
return EXIT_FAILURE;
FILE *fp;
char filename[41];
printf("Enter filename : ");
scanf("%40s", filename);
fp = fopen(filename, "r");
if(fp == NULL)
error("File not find");
while(1) {
node_t tmp;
int i = fscanf( fp, "%d%d%lf%lf", &(tmp.id), &(tmp.sub_a), &(tmp.sub_b), &(tmp.sub_c) );
if( i < 4 ) break; // 入力数がおかしかったらとめる
if( input_node(node_manager, &tmp) == false ) break; // 読み込んだものを入力
}
fclose(fp);
nmt_print( node_manager );
delete_node_manager( node_manager );
//_getch();
}
Re: 構造体メンバへの格納法
Posted: 2014年1月10日(金) 09:46
by softya(ソフト屋)
kopi さんが書きました:
つまり、何らかのエラーがあるため、本来入力したい分のデータ数が入力できていないということですか?
もしそうだとしたら、どの辺が原因なのでしょうか。
fscanfは入力エラーの場合でも戻り値でEOFを返しますので、EOFで終了と判定するのは本来間違いです。
Re: 構造体メンバへの格納法
Posted: 2014年1月10日(金) 14:13
by kopi
shirokosi さんが書きました:
はじめまして。
参考になりそうなコードを書いてみました。
よろしければお使いください。
ありがとうございます。ぜひ参考にさせていただきます。
softya(ソフト屋) さんが書きました:
fscanfは入力エラーの場合でも戻り値でEOFを返しますので、EOFで終了と判定するのは本来間違いです。
エラーでもファイルの終端でもEOFを返すのなら、ファイル終端とエラーを区別できればいいのでは、と考え
コード:
while( p = getmemory(), fscanf(fp, "%d %d %lf %lf", p->id, p->sub_a, p->sub_b, p->sub_c) != EOF){
old->next = p;
old = p;
}
と書いていたところを、下のように書いてみたのですが
コード:
while(!feof(fp)){
p = getmemory();
fscanf( fp, "%d %d %lf %lf", &(p->id), &(p->sub_a), &(p->sub_b), &(p->sub_c) );
old->next = p;
old = p;
}
結果は相変わらず700行~1000行の間の300行程度しか読み込めないまま。
例えば、1行目から100行目まで読み込んでいるのであれば、101行目のデータがおかしいのかなと、なんとなく想像がつくのですが
スタートが700行目からになっている原因がよく分かりません。それまでのデータはどこへ・・・。
データであるtxtファイルを添付しておきますので、ご確認していただければ幸いです。
Re: 構造体メンバへの格納法
Posted: 2014年1月10日(金) 14:17
by softya(ソフト屋)
それはコンソールに最後の300行しか表示されていないのでは?
ファイルにリダイレクトされましたか?
xxx.exe > out.txt
ってやつです。
VC++だとプロジェクトのプロパティでデバッグの引数に「> out.txt」と書いて下さい。
Re: 構造体メンバへの格納法
Posted: 2014年1月10日(金) 15:56
by kopi
softya(ソフト屋) さんが書きました:それはコンソールに最後の300行しか表示されていないのでは?
ファイルにリダイレクトされましたか?
xxx.exe > out.txt
ってやつです。
VC++だとプロジェクトのプロパティでデバッグの引数に「> out.txt」と書いて下さい。
ファイルに出力して試してみたところ、無事、全データを出力していることを確認できました。
コンソール上で表示する行数には制限等があるんですかね。。。
とりあえずは問題が解決いたしましたので、一旦は解決とさせていただきます。
まだまだ先はあるので、つまずき次第また質問させていただきます。
質問に答えていただきありがとうございました。
特に丁寧に説明していただいたsoftya(ソフト屋)さん、ありがとうございます。