ここから本文です

C言語でcsvファイルの読み込みについて質問です。

質問者

umepachi0428さん

2011/1/1503:19:45

C言語でcsvファイルの読み込みについて質問です。

条件が次の通りです。
・csvファイルの中身は何行あるか不明。
・メモリをポインタを使って動的に確保する。
・任意の行だけを表示させる。

プログラミング初心者なもので解説サイトや参考書を読みつつ粘ってみましたが分かりません。
以下に作成したプログラムを載せておきますが、まったくプログラムとして成り立ってないと思います。
ダメな点などご教授願います。


#include<stdio.h>
#include<stdlib.h>

main(){
FILE *fp;
char *fname = "test1.csv";
int n = 0;
int n1;
int *pt;
int i;

fp = fopen( fname, "r" );
if ( fp == NULL ){
printf( "Not open file\n");
return -1;
}

while(( fscanf( fp, "%d", &n1)) != EOF){
n++;
}

pt = (int *)malloc(sizeof(int) * n);

while(( fscanf( fp, "%s", &pt)) != EOF){
pt++;
}

printf("Please line = "); scanf("%d",&i);
printf("Choice line is %s\n",*pt+i);

fclose(fp);
return 0;
}

補足たくさんの回答ありがとうございます。補足します。

・csvファイルの先頭10行はこの様な感じです。
1,0.61524,0.61524
2,0.20088,0.40176
3,0.69432,2.08296
4,0.60188,2.40754
5,0.04719,0.23598
6,0.82239,4.93435
7,0.58617,4.10319
8,0.96115,7.68924
9,0.85486,7.69378
10,0.53497,5.34977

・確保したいメモリは行です。扱うのも行単位で扱います。

・繰り返し選んだ行を表示させたいため、1度配列で保存したいと思います。

この質問は、活躍中のチエリアンに回答をリクエストしました。

閲覧数:
5,221
回答数:
5
お礼:
50枚

違反報告

ベストアンサーに選ばれた回答

編集あり2011/1/1609:44:32

リスト連結を用いてみるのもいいかもしれません。
リスト連結とは、動的に構造体を用いてデータを処理する手法です。
私は観測機器のデータ(中にいくらデータがはいっているかわからない)処理でよくこれを使います。便利ですよ。覚えておいて損はありません。
構造体のメンバの中に、次の構造体(リスト連結の先ですね。)のポインタ(このケースの場合はnext)がメンバとしてはいっています。これをたどることにより、どの構造体にもたどりつくことができます。
けっして、むずかしいものではありません。初心者のわたしでも理解できました。

リスト連結の利点は、連結に制限がないことです。
いくらでも、連結を伸ばすことができます。
行数(この場合の連結数)が不明な質問者さんのケースには適していると思います。
1行の内容も不明な場合は、構造体のメンバを別に動的にとればいいだけです。
2次元配列をポインタで処理する方法は、わかりやすいですが行数上限に制限がついてしまう場合があります。
質問者さんの利用方法に応じて、処理されればよろしいでしょうね。


下に簡単な案を示します。
単純な連結リストです。
一度読み込んでしまうと、無限ループをとっているために
何度でも行を表示できます。
作動は確認済みです。お試しください。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef struct tag{ /*リスト構造体を作成*/
char list[255];/*データを入れる入れ子*/
struct tag *next;/*次に連結している構造体のアドレスをいれる。*/
}_tag;




int main(){

char name[255];
char data[255]; /*1行文字列数に合わせて配列規模を決めてください。*/

char num[255];
_tag *tag_header;
_tag *tag_1;
_tag *tag_next;
_tag *temp;


int n=0;
int num_1;
int nn=0;
FILE *f;
tag_1=tag_header=malloc(sizeof(_tag));/*リストヘッダー構造体を作成します。*/


printf("Input name of csv.file\n");/*処理目的とするファイル名をインプット。任意のファイル名にも対応。*/
fgets(name,sizeof(name),stdin);
name[strlen(name)-1]='\0';
printf("%s\n",name);/*ファイル名の確認*/

f=fopen(name,"r");


while(fgets(data,sizeof(data),f)){
data[strlen(data)-1]='\0';
// printf("%s\n",data);
strcpy(tag_1->list,data);/*リスト内文字列に1行挿入*/
tag_next=malloc(sizeof(_tag));/*次のリスト構造体を作成*/
tag_1=tag_1->next=tag_next;/*作業用リスト構造体を今の構造体から次の構造体に移す*/



nn++;/*行数を数えます*/


}
tag_1=tag_header;/*リスト構造体を頭に戻す*/


while(1){
printf("Final numer=%d\n Input number of record you select \nif you want to end input E\n",nn);
fgets(num,sizeof(num),stdin);/*表示したい行ナンバーを挿入*/
num[strlen(num)-1]='\0';
if(strstr(num,"E")) break;/*無限ループから出たいときはEを挿入*/

num_1=atoi(num);
for(tag_1;tag_1!=NULL;tag_1=tag_1->next){/*リスト頭のポインタから次のリストポインタへ移るループ。ポインタがヌルになったらおしまい。*/
if(n==num_1-1) {
printf("Record is %s\n",(tag_1->list));/*行を表示*/
break;
}

n++;
}
n=0;
tag_1=tag_header;
}

tag_1=tag_header;

while(!tag_1){/*メモリを消してしまいましょう*/
temp=tag_1;
tag_1=temp->next;
free(temp);

}





fclose(f);

return 0;
}

質問した人からのコメント

2011/1/19 16:02:24

希望通りのプログラムができました。
いくつか知らないプログラムの書き方もあったのでこれからも勉強していきます。
本当にありがとうございました。

ちょい足しを取り消しますが
よろしいですか?

  • 取り消す
  • キャンセル

このQ&Aで解決しましたか?質問する

閉じる

ベストアンサー以外の回答

1〜4件/4件中

並び替え:回答日時の
新しい順
|古い順

編集あり2011/1/1605:39:04

補足情報をもとに、書き換えました。

/***********************************************
csvファイルというのは、カンマで区切られた文字列で、エクセルなど表計算ソフトの入出力ファイルになりえる形式のファイルですね。
中身は何行あるかが不明というだけでなく、どんなデータが入っているかということも不明なので、とにかく全体を一つの文字列とみなして動的に確保するところまでやりました。

プログラムの中で、 bufに読み込んだ1行の中のカンマの数を調べれば、1行のデータの数が分かります。データの型に応じて必要なサイズがきまりますので、文字列としてメモリにとるかわりに、バイナリのデータとしてメモリにとることもできます。それはあなたの仕事として残しておきます。

行単位で読みだしてきた時点で、atoi() とか atof() を使えば文字列を計算対象となるデータ型に変換する
こともできそうです。とにかくどう使うかはumepachi0428さん自身でお考えください。

各行を表示しないで、その番号だけ指定するというのは、エクセルで開いた原データを手元にみながら、プログラムがちゃんとまともに動くかどうかのチェックの意味しかないような気がします。


■原プログラムに対するコメント:

while ((fscanf(fp, "%s", &pt)) != EOF) {
// 文法的には、fscanf(fp, "%s", pt) != EOF とかくべきだが
// そのあと、ptの値を一つずつ増やし続けても意味はない
// 全体を文字列として読み取るには、 fscanf()より、fgets()のほうがよい
// pt は整数値を指すポインタのはずだが、、%s は文字列の変換指定子、何がやりたのか分からない
pt++;
}

printf("Please line = ");
scanf("%d", &i);
printf("Choice line is %s\n", *pt + i);
// これでは、ptが指すアドレスに入っている値にiを足したものを文字列表示開始アドレスとすることなる。n何がやりたいのかよく分からない。
// pt + i にかえれば pt からi番目の整数データを指すことになるが、 %s でなく、%d にしなければ整数として表示されない。

************************************************/


#define MOJIRETU 1

#include<stdio.h>
#include<stdlib.h>
#include <string.h> //strlen()


main()
{
FILE *fp;
char *fname = "test1.csv";

int n;// 初期化は使用直前にしたほうが処理が分かりやすい
int *pt[3000];//整数データの入っている行を指すポインタが必要ならこれが要る。
int i;
char buf[2048];//1行に読み込むデータのバイト数の上限値 状況に応じて決める
char *p[3000];// 確保したポインタを格納する場所、読み込む行の数の上限値が3000


fp = fopen(fname, "r");
if (fp == NULL) {
printf("Can't open %s\n", fname);
return -1;
}

n=0;// ここで初期化したほうがよい
while (fgets(buf, sizeof(buf), fp) != NULL){
buf[strlen(buf)-1]='\0';//行末の改行コードを消す fgets()は改行コードも読み込むため
if((p[n]=(char *)malloc(strlen(buf))) != NULL){
strcpy(p[n], buf);//確保したアドレスにbufの内容(1行分のデータ)を書き込む)
printf("[%2d] %s\n", n+1, p[n]);//これはデバッグ用なので後で消す/////////////////
n++;
}
else{
puts("メモリが確保できません");
for(i=0; i<n; i++)//いままで確保したメモリの解放
free(p[i]);
exit(EXIT_FAILURE);
}
}
if(n==0) return 0; // nが0というのは、何も読み込めなかったということ

printf("\nEnter 0 when you want to quit.\n");
i=-1;
while (i){
do {
printf("Choice the line number you want to display (0: for quit or (1~%d)):", n);
scanf("%d", &i);
}while(!(0 <= i && i <= n));
if(i == 0) break;
printf("%s\n\n", p[i-1]);
}
for(i=0; i<n; i++)
free(p[i]);//メモリの解放
fclose(fp);

return 0;

}

/*****************************************************
実行例

[ 1] 1,0.61524,0.61524
[ 2] 2,0.20088,0.40176
[ 3] 3,0.69432,2.08296
[ 4] 4,0.60188,2.40754
[ 5] 5,0.04719,0.23598
[ 6] 6,0.82239,4.93435
[ 7] 7,0.58617,4.10319
[ 8] 8,0.96115,7.68924
[ 9] 9,0.85486,7.69378
[10] 10,0.53497,5.34977

Enter 0 when you want to quit.
Choice the line number you want to display (0: for quit or (1~10)):1
1,0.61524,0.61524

Choice the line number you want to display (0: for quit or (1~10)):5
5,0.04719,0.23598

Choice the line number you want to display (0: for quit or (1~10)):20
Choice the line number you want to display (0: for quit or (1~10)):10
10,0.53497,5.34977

Choice the line number you want to display (0: for quit or (1~10)):0

******************************************************/

ちょい足しを取り消しますが
よろしいですか?

  • 取り消す
  • キャンセル

kuontoさん

編集あり2011/1/1606:54:06

csvファイルの内容が不明だが

int *pt; --> int *pt,*p;

while(( fscanf( fp, "%s", &pt) != EOF){
pt++;
}



p = pt;
rewind(fp);
while(fscanf( fp, "%d", &*p) != EOF){
p++;
}

printf("Choice line is %s\n",*pt+i); --> printf("Choice line is %d\n",*pt+i-1);

[補足]
#include <string.h>を追加

int *pt; --> char **pt,**p,b[4096];

while(( fscanf( fp, "%d", &n1)) != EOF){ --> while(fscanf(fp,"%d%*[^\n]",&n1) != EOF){

pt = (int *)malloc(sizeof(int) * n); --> pt = (char **)malloc(sizeof(char *) * n);

while(( fscanf( fp, "%s", &pt) != EOF){
pt++;
}

p = pt;
rewind(fp);
while(fscanf( fp, "%s", b) != EOF){
*p = strdup(b);
p++;
}

printf("Choice line is %s\n",*pt+i);

printf("Choice line is %s\n",*(pt+i-1));
p = pt;
for(i = 0;i < n;i++) free(*p++);
free(pt);

ちょい足しを取り消しますが
よろしいですか?

  • 取り消す
  • キャンセル

rhinosugarさん

編集あり2011/1/1619:14:40

/*
要求はCSV独特の問題がなく、単なるテキストファイル
の行入力、保管、照会表示のみ。
ここのdupchs相当の機能はwindousではstrdupがある。
(windousマシン以外は?)
*/
#include_<stdio.h>
#include_<stdlib.h>
#include_<string.h>

#define___MBFF__1024__//_入力行バッファ
#define___MLNS__4096__//_入力行テーブル寸法

char_*dupchs(char_*buff);

int_main(void){
//char____*fnm="data.csv";
__char____*fnm="test.txt";
__FILE____*fi;____________//_入力ファイル
__char____buff[MBFF];_____//_行バッファ
__char____*p=buff;________//_バッファ先頭
__char____*tab[MLNS];_____//_格納テーブル
__int_____n,nl=0;_________//_行インデクス、数
__int_____quit;___________//_繰返し中止のとき真

__//_オープン
__printf("入力オープン:%s\n",fnm);
__fi_=_fopen(fnm,"r");
__if(fi==NULL)_exit(1);
__//_読み込み格納:ポインタのみテーブル化
__while(fgets(buff,MBFF,fi)!=NULL){
____strtok(buff,"\n");
____tab[nl++]_=_dupchs(buff);
__}
__fclose(fi);
__//_格納情報の全リスト
__printf("Table_of_stored_lines\n");
__for(n=0;n<nl;n++){
____printf("%2d:_%s\n",n+1,tab[n]);
__}
__//_指定行の繰返し表示
__printf("\n");
__printf("繰返し指定行表示開始:\n");
__do{
____do{
______printf("line_No._?=_");
______scanf_("%d",&n);
______getchar();
____}while(n<1_||_nl<n);
____n--;
____printf("Your_request_=_%s\n",tab[n]);
____printf("q_or_Q_to_quit:_");
____p_=_gets(buff);
____quit_=_*p=='q'||*p=='Q';
__}while(!quit);
__//_領域解放
__for(n=0;n<nl;n++){
____free(tab[n]);
__}
__return_0;
}

//_領域確保付文字列格納
char_*dupchs(char_*buff){
__char__*p;
__int___ln;

__ln_=_strlen(buff);
__p_=_(char*)malloc(ln+1);
__if(p==NULL){
____printf("p=%p_/dupchs\n",p);
____exit(2);
__}
__strcpy(p,buff);
__return_p;
}
/*_出力
入力オープン:test.txt
Table_of_stored_lines
_1:_1,0.61524,0.61524
_2:_2,0.20088,0.40176
_3:_3,0.69432,2.08296
_4:_4,0.60188,2.40754
_5:_5,0.04719,0.23598
_6:_6,0.82239,4.93435
_7:_7,0.58617,4.10319
_8:_8,0.96115,7.68924
_9:_9,0.85486,7.69378
10:_10,0.53497,5.34977

繰返し指定行表示開始:
line_No._?=_1
Your_request_=_1,0.61524,0.61524
q_or_Q_to_quit:
line_No._?=_10
Your_request_=_10,0.53497,5.34977
q_or_Q_to_quit:
line_No._?=_1
Your_request_=_1,0.61524,0.61524
q_or_Q_to_quit:_q
*/

ちょい足しを取り消しますが
よろしいですか?

  • 取り消す
  • キャンセル

編集あり2011/1/1516:14:11

何のメモリを動的に確保するのか、を書くこと。

わざわざメモリを動的に確保する必要なんか無いけど。

どうしてもmallocを使いたいなら、こんな感じ?

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
FILE *fp = fopen("test.csv", "r");
int c, lf = 0, lc = 0, ln, i = 0;
char *s = malloc(128);

if(!s || !fp) exit(EXIT_FAILURE);
fputs("Please line = ", stdout);
if(scanf("%d", &ln) < 1) exit(EXIT_FAILURE);
while((c = fgetc(fp)) != EOF && (lc += !lf) <= ln){
if((lf = (c != '\n')) && lc == ln) s[i ++] = c;
}
fclose(fp);
s[i] = '\0';
if(ln < 1 || lc < ln) puts(" * Line not exist. *");
else printf("Choice line is \"%s\"\n", s);
free(s);
return 0;
}

ちょい足しを取り消しますが
よろしいですか?

  • 取り消す
  • キャンセル

Q&Aをキーワードで検索:

Yahoo! JAPANは、回答に記載された内容の信ぴょう性、正確性を保証しておりません。
お客様自身の責任と判断で、ご利用ください。
本文はここまでです このページの先頭へ

ID/ニックネームを選択し、「追加する」ボタンを押してください。

閉じる

※知恵コレクションに追加された質問や知恵ノートは選択されたID/ニックネームのMy知恵袋で確認できます。

ほかのID/ニックネームで利用登録する