C言語の勉強をしています。
test.csvというファイル名のCSVファイルで
項目,名前,身長,体重,血液型,合否(合格なら○不合格なら空欄)
1,太郎,150,55.6,A,○
2,二郎,165.5,60,B
3,三郎,160.2,59.5,AB,○
と書かれたファイルを読もうと思いまして
#include <stdio.h>
#define i 1000
#define j 1000
main()
{
int d[i][j];
double...
FILE *fp;
fp=fopen(fp,"test.csv","r");
for(i=0;i<=2;i++)
{
for(j=0;j<=6;j++)
{
fscanf(fp,"%d",d[i][j]);
}
}...
という感じで書いても読み込みません。
どのように書いたら読み込むでしょうか。
Cをはじめて間もないのでどなたか教えていただけないでしょうか
よろしくお願いします。
A 回答 (9件)
- 最新から表示
- 回答順に表示
No.9
- 回答日時:
>各行内に空のセルがある場合
空のセルがある場合、strtokは使えません。
sscanfなら、空のセルにも対応できますが、カンマを含むセルがある場合はやはり対応できません。
1,あ,"a,b"
というのです。
さらに、規格上は、改行を含むセル、なんてのも存在します。こうなると、fgetsすら使えません。
なので、本気でやろうと思ったら、既存の関数はあてにならず、全部自力で処理する必要があります。
とはいえ、実際にはある程度フォーマットを限定できる場合が多いです。
ご提示の例であれば、fgetsとsscanfの組み合わせが現実的だと思います。
No.8
- 回答日時:
#7 です。
間違えました。誤: ret = sscanf(buf, "%d,%[^,],%lf,%lf,%[^,],%[^,^\n]", &n, s1, &x, &y, s2, s3);
正: ret = sscanf(buf, "%d,%[^,],%lf,%lf,%[^,],%[^,\n]", &n, s1, &x, &y, s2, s3);
# あと、カンマ入の要素は言うまでもなく、空の要素があったりすると破綻します。
# 個人的なちょっとしたデータ処理ぐらいになら使えるかも。
No.7
- 回答日時:
scanf 系の関数でも一応できます。
書式指定文字列中の "%[^,]" は ',' 以外の文字にマッチします。char buf[256] = "1,太郎,150,55.6,A,○\n";// fgets で読んだつもりのデータ
//char buf[256] = "2,二郎,165.5,60,B";
int n, ret;
double x, y;
char s1[256], s2[256], s3[256] = "";
ret = sscanf(buf, "%d,%[^,],%lf,%lf,%[^,],%[^,^\n]", &n, s1, &x, &y, s2, s3);
// 表示してみます
printf("%d\n", ret);
printf("%d\n", n);
puts(s1);
printf("%f\n", x);
printf("%f\n", y);
puts(s2);
puts(s3);
上では手抜きしてますが、最低限、文字列を格納するバッファがオーバーフローしないようにすることと、sscanf の戻り値のチェックぐらいは必要です。
No.6
- 回答日時:
ちなみに、データがない可能性があるのが最後の合否部分だけなら、こんな邪道回答もあります。
前提として構造体・共用体を知っている必要はあります。
#include <stdio.h>
#define LINE_LEN 256
typedef union
{
struct {
char *num;
char *name;
char *height;
char *weight;
char *blood;
char *result;
} param;
char *array[6];
} record;
int main()
{
FILE *fp = fopen("test.csv", "r");
char line[LINE_LEN];
record data;
char *tmp;
int i;
while(fgets(line, LINE_LEN, fp) != NULL)
{
i = 0;
tmp = strtok(line, ",");
do
{
data.array[i] = tmp;
tmp = strtok(NULL, ",\n");
i ++;
} while(tmp != NULL);
if(i < 6) data.result = "×";
printf("項番:%s 名前:%s 身長:%s 体重:%s 血液型:%s 結果:%s\n",
data.param.num, data.param.name, data.param.height, data.param.weight, data.param.blood, data.param.result);
}
}
一行ずつ読んではstrtok()で分解して表示、を繰り返しています。
複数のデータを持ちたい場合にはdataを配列にしましょう。
全て文字列で持っているので数値で扱いたい場合にはそれなりの対応が必要です。
また要素が4個以下(血液型がない場合)などのデータ自体の整合性は見ていないので不正データがあった場合には、このままではコケます。
なお、strtok()は「返すべきトークンが存在しない」状況になったらNULLを返します。
常に返り値チェックをして「返り値をチェックしないまま扱おうとしてAccess Violation」なんて事にならないように気をつけましょう。
No.5
- 回答日時:
「strtok()に苦戦している」とのことですが、「2,二郎,165.5,60,B,」の末尾がメモリにどのように展開されているかを知ることが strtok() をマスターするキーポイントではないでしょうか。
CSVファイルは fgets()を使って読み込みますが、読み込まれた「2,二郎,165.5,60,B,」の行の末尾の展開は
2,二郎,165.5,60,B,\n\0
となっています。
これをクリアするために strtok(NULL,",\n") とすると bus error が出てうまくいきません。
アドレスが \n を認知できず、読み込んだメモリ領域を超えてしまったことによる error と考えられます。
そこで \n の次は \0 にメモリ展開されていますから、strtok(NULL,"\0") とすると error は起こりません。
しかし、改行コードの \n が読み込まれるため、これを排除する新たな作業が必要となります。
*(ptr->judge+strlen(ptr->judge)-1)='\0';
----- データファイル -----
1,太郎,150,55.6,A,○
2,二郎,165.5,60,B,
3,三郎,160.2,59.5,AB,○
#include <stdio.h>
#include <string.h>//strtok(),strcpy()
#include <stdlib.h>//exit(),atoi()
#define SIZE 256
#define NUMBER 50
struct set1 {
int code;
char name[32];
float height;
float weight;
char blood[3];
char judge[3];
};
int main(int argc, char *argv[])
{
struct set1 member[NUMBER], *ptr;
char buffer[SIZE], work[12];//作業用バッファ
char *read_file;//読み込みファイル名
int lines;//読み込み行数
FILE *fp;
int j;
if (argc!=2){
printf("parameter error.\n");
exit(1);
}
read_file=argv[1];
if ((fp=fopen(read_file, "r"))==NULL){
printf("%s file can't open.\n",read_file);
exit(1);
}
lines=0;
while(fgets(buffer, SIZE, fp)!=NULL) {
if(lines>= NUMBER){
printf("buffer overflow error!");
exit(1);
}
ptr = &member[lines];
ptr->code=atoi(strcpy(work,strtok(buffer,",")));
strcpy(ptr->name,strtok(NULL,","));
ptr->height=atof(strcpy(work,strtok(NULL,",")));
ptr->weight=atof(strcpy(work,strtok(NULL,",")));
strcpy(ptr->blood,strtok(NULL,","));
strcpy(ptr->judge,strtok(NULL,"\0"));
*(ptr->judge+strlen(ptr->judge)-1)='\0';
lines++;
}
fclose(fp);
for (j=0; j<lines; j++) {
ptr=&member[j];
printf("\t番号= %d, ", ptr->code);
printf("名前= %s, ", ptr->name);
printf("身長= %.1f, ", ptr->height);
printf("体重= %.1f, ", ptr->weight);
printf("血液型= %s, ", ptr->blood);
printf("合否= %s\n", ptr->judge);
}
return 0;
}
No.4
- 回答日時:
一箇所書き間違えました。
> 4.検索開始位置をカンマの次に移動して3に戻る、以下カンマが見つからなくなるまで繰り返し
4.検索開始位置をカンマの次に移動して”2”に戻る、以下カンマが見つからなくなるまで繰り返し
です。
No.3
- 回答日時:
認識されている通りcsvのエレメント分割は単純にはいかない訳です。
#2で指摘されている部分の他にもエレメント内の"の扱いとかありますしね。
まぁ今回は「csvそのもの」ではなく「csvっぽいもの」として、ファイルフォーマットを以下に限定して考えることにしましょう。
・要素に改行文字・ダブルクォーテーションは含まない(必ず1行でデータが完結する)
・空要素はある
このとき、要素を取るためにはどうすればいいかという事を考える訳ですが、
1.1行読む(fgets())
2.検索開始位置を行頭に置く
2.検索開始位置から最初にあるカンマ位置を取得する(strchr(), strcspn()など)
3.検索開始位置からカンマ位置までを切り出す(strncpy()など、終端処理を忘れずに)
4.検索開始位置をカンマの次に移動して3に戻る、以下カンマが見つからなくなるまで繰り返し
5.1に戻る、以下行が読めなくなるまで繰り返し
という手順を踏むことになるでしょう。
本来のちゃんとしたcsvに対応するにはこれでは駄目ですが、質問の内容についてはこの手順で問題ないはずです。
No.2
- 回答日時:
すでに書かれているとおり、"%d"で読み込もうとしているから…です。
","は数字ではありませんから、ココで止まるでしょうね。
"太郎"も"A"も"○"も数字ではありませんけどね…。
しかも例の場合CSVとして正しいのか…
二郎の行は
2,二郎,165.5,60,B,
となるハズです。
1行読み込みして、strtok()でカンマで区切る。
なんてのがCSV読み込みでよくあるパターンですが……コレもアウトです。
# 「入門者向けの難易度とは言い難い」と言われる由縁。
なぜか初心者向けでCSV読み込みが例題に出されるパターンをよく見かけますが…
「ちゃんと」処理しようとした場合、どう見ても初心者向けではありません。
# データの入っていない場合は? データ中に","が含まれる場合は? データ中に改行が含まれている場合は? などなど。
以下、蛇足ですが…
このコードはコンパイル通りますかね?
>#define i 1000
>#define j 1000
>int d[i][j];
ここまではよいでしょう。
# iとかjをdefine定義することの是非は置いておきますが。
>for(i=0;i<=2;i++)
>for(j=0;j<=6;j++)
定数をfor文のカウンタに使うのはいかがかと。
ちなみに、上のは…
for(1000=0;1000<=2;1000++)
for(1000=0;1000<=6;1000++)
と同等になります。
# 1000=0と1000++が微妙。1000<=2は常に偽なので…最適化で丸ごと消える??
>fscanf(fp,"%d",d[i][j]);
バッファオーバーランしてます。iとjは定数ですが…二次元配列dはそんな添え字は許可されません。
# まぁ、for文の条件からココには来ないハズですが。
No.1
- 回答日時:
scanf系の%dは数値がいない場合そこで解析を止めます。
また、「csvを読む標準関数」は存在しないのでいずれにせよその方法ではカンマで区切ることができません。
Cを始めて間もないのなら、まずは基礎を固めるのに時間をかけるべきです。
この課題自体は入門者向けの難易度とは言い難いので、例を提示すること自体は難しくはないのですが恐らくそちらが理解できないでしょう。
コメントありがとうございます。
文字と数値の入り混じったCSVファイルの読み方、また、各行内に空のセルがある場合
どうしたらいいのかわからなく投稿しました。
strtok関数とかも自分なりに使用してみたのですがなかなかうまくいかず、苦戦してます。
もう一度勉強してTRYしてみます。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
このQ&Aを見た人はこんなQ&Aも見ています
-
あるあるbotに投稿したけど採用されなかったあるある募集
あるあるbotに投稿したけど採用されなかったあるあるをこちらに投稿してください
-
フォロワー20万人のアカウントであなたのあるあるを披露してみませんか?
あなたが普段思っている「これまだ誰も言ってなかったけど共感されるだろうな」というあるあるを教えてください
-
映画のエンドロール観る派?観ない派?
映画が終わった後、すぐに席を立って帰る方もちらほら見かけます。皆さんはエンドロールの最後まで観ていきますか?
-
海外旅行から帰ってきたら、まず何を食べる?
帰国して1番食べたくなるもの、食べたくなるだろうなと思うもの、皆さんはありますか?
-
天使と悪魔選手権
悪魔がこんなささやきをしていたら、天使のあなたはなんと言って止めますか?
-
カンマ区切りのデータを配列に読み込みたい
C言語・C++・C#
-
C言語で複数列のデータを1列のみ読み込みたい
C言語・C++・C#
関連するカテゴリからQ&Aを探す
おすすめ情報
- ・漫画をレンタルでお得に読める!
- ・人生のプチ美学を教えてください!!
- ・10秒目をつむったら…
- ・あなたの習慣について教えてください!!
- ・牛、豚、鶏、どれか一つ食べられなくなるとしたら?
- ・【大喜利】【投稿~9/18】 おとぎ話『桃太郎』の知られざるエピソード
- ・街中で見かけて「グッときた人」の思い出
- ・「一気に最後まで読んだ」本、教えて下さい!
- ・幼稚園時代「何組」でしたか?
- ・激凹みから立ち直る方法
- ・1つだけ過去を変えられるとしたら?
- ・【あるあるbot連動企画】あるあるbotに投稿したけど採用されなかったあるある募集
- ・【あるあるbot連動企画】フォロワー20万人のアカウントであなたのあるあるを披露してみませんか?
- ・映画のエンドロール観る派?観ない派?
- ・海外旅行から帰ってきたら、まず何を食べる?
- ・誕生日にもらった意外なもの
- ・天使と悪魔選手権
- ・ちょっと先の未来クイズ第2問
- ・【大喜利】【投稿~9/7】 ロボットの住む世界で流行ってる罰ゲームとは?
- ・推しミネラルウォーターはありますか?
- ・都道府県穴埋めゲーム
- ・この人頭いいなと思ったエピソード
- ・準・究極の選択
このQ&Aを見た人がよく見るQ&A
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
Enterキーを押されたら次の処理...
-
printf による16進表示について
-
#defineが使用するメモリ領域に...
-
空白を含んだ文字列がうまく格...
-
大量のファイルを読み込み、そ...
-
警告 W8075 問題のあるポインタ...
-
char型2つを結合し、short型に...
-
static付き宣言の初期化
-
2進数の表示
-
C言語でのCSVファイルの読み出...
-
WinInetのInternetOpenUrl関数...
-
VC++でSQLへSELECT文を送ったの...
-
C言語 プログラム
-
2÷3などの余りについて
-
マイナスからプラスへ転じた時...
-
10個出力で改行したいのですが...
-
Aの値からBの値を除するとは??
-
C言語での引数の省略方法
-
数字以外が入力されたらエラー...
-
「指定されたキャストは有効で...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
Enterキーを押されたら次の処理...
-
#defineが使用するメモリ領域に...
-
printf による16進表示について
-
【C言語】全角文字の配列を、全...
-
空白を含んだ文字列がうまく格...
-
C言語で複数列のデータを1列の...
-
構造体メンバの初期化
-
char型2つを結合し、short型に...
-
reallocでエラー
-
C++で指定文字列のカウント方法...
-
終了条件Ctrl+zについて,結果表...
-
C言語でのCSVファイルの読み出...
-
矢印キーを押下してコンソール...
-
VC++でSQLへSELECT文を送ったの...
-
WinInetのInternetOpenUrl関数...
-
fread(),fwrite()等について
-
C言語のプログラムで、途中で止...
-
大量のファイルを読み込み、そ...
-
fscanfの使い方
-
ビルドエラーの対処がわからな...
おすすめ情報