- 締切済み
C言語でのCSVソートとデータ抽出について
皆様、はじめまして。 この度、急ぎでプログラムをC言語で作成するように命じられました。 C言語は経験が無いと断ったのですが、要員確保が出来ない為、何とかしてくれとのこと。 本来なら自分で学習しながら、作成すべきなのですが、超短納期の為、その時間が取れません。 今回は誠に申し訳ないのですが、皆様のお力をお借り出来ないでしょうか。 宜しくお願いいたします。 仕様概要 ・CSVファイルを読み込み、2カラム目の項目(文字型)で昇順ソート(qsort)を行う。 ・ソートされた2カラム目の同一値毎に1カラム目(数値型)が最大値となるレコードを抽出する。 ・抽出されたレコードを新規CSVファイルに出力する。 入力CSV概要 ・レコード件数は日によって変わる ・カラム数は8つ ・各カラムの項目長は可変長 ・上記に伴いレコード長も可変長 入力ファイル例 39,"AAA3","B1","C1","D1","E1","F1","G1" 100,"AAA1","B2","C2","D2","E2","F2","G2" 101,"AAA2","B3","C3","D3","E3","F3","G3" 105,"AAA1","B4","C4","D4","E4","F4","G4" 102,"AA1","B5","C5","D5","E5","F5","G5" 99,"AAA2","B6","C6","D6","E6","F6","G6" 1019,"AAA3","B7","C7","D7","E7","F7","G7" 処理後に出力されるファイル 102,"AA1","B5","C5","D5","E5","F5","G5" 105,"AAA1","B4","C4","D4","E4","F4","G4" 101,"AAA2","B3","C3","D3","E3","F3","G3" 1019,"AAA3","B7","C7","D7","E7","F7","G7"
- みんなの回答 (6)
- 専門家の回答
みんなの回答
- madman
- ベストアンサー率24% (612/2465)
もう、結構時間がたったから良いかな。 いかにソースを示します。 とりあえず30分くらいで書いてみました。 例のCSVデータは問題なく動作します。 しかし、意図的にバグを埋めています。 解析して勉強してください。 #include <stdio.h> #include <string.h> typedef struct { int c1; char c2[7][2048]; } CSVTBL; CSVTBL *csvtbl; static int cmpstring(const void *p1, const void *p2) { CSVTBL *lp1, *lp2; lp1 = (CSVTBL *)p1; lp2 = (CSVTBL *)p2; int ret = strcmp(lp1->c2[0], lp2->c2[0]); if (ret == 0 ) { if (lp1->c1 == lp2->c1) { ret = 0; } else { ret = lp1->c1>lp2->c1?-1:1; } } return ret; } main (int argc, char *argv[]) { char buf[2049]; char *b; int i; int num=0; if(argc != 3) { fprintf(stderr, "input error !\n"); fprintf(stderr, "usage:%s INFILENAME OUTFILENAME\n", argv[0]); return -1; } csvtbl = (CSVTBL *)malloc(sizeof(CSVTBL)); FILE *fpr = fopen(argv[1], "r"); FILE *fpw = fopen(srgv[2], "w"); if(fpr && fpw) { while(fgets(buf, 2049, fpr)) { if (strlen(buf) <2048) { csvtbl = (CSVTBL *)realloc(csvtbl, sizeof(CSVTBL)*(num+1)); b = strtok(buf, ","); if (b) { csvtbl[num].c1 = atoi(b); for(i=0; i<7; i++) { b = strtok(NULL, ","); if(b) { strcpy(csvtbl[num]c2[i], b); } else { fprintf(stderr, "record error! : [%s]\n", buf); break; } } } num ++; } else { fprintf(stderr, "record error! : [%s]\n", buf); while(fgets(buf, 2049, fpr)) { if (strlen(buf) < 2048) break; } } } qsort(csvtbl, num, sizeof(CSVZTBL),cmpstring); fprintf(fpw, "%d,%s,%s,%s,%s,%s,%s,%s", csvtbl[0].c1,csvtbl[0].c2,csvtbl[0].c3,csvtbl[0].c4,csvtbl[0].c5,csvtbl[0].c6,csvtbl[0].c7); for (i=1; i<num; i++) { if (0!=strcmp(csvtbl[i-1].c2[0], csvtbl[i].c2[0])) { fprintf(fpw, "%d,%s,%s,%s,%s,%s,%s,%s", csvtbl[i].c1,csvtbl[i].c2,csvtbl[i].c3,csvtbl[i].c4,csvtbl[i].c5,csvtbl[i].c6,csvtbl[i].c7); } } fclose(fpw); fclose(fpr); } free(csvtbl); return 0; }
こんにちは。 処理の概略手順だけ考えてみました。 ※下記は一例です。 1)入力元CSVファイルをオープンする。 2)入力元のCSVファイルを行単位に読み込み、その行数のみ取得する。 ・fgets関数で、最大バッファ長を (2048+4)ぐらいとして、行単位に[EOF] になるまで読み込んで(※ここでは文字列バッファに読み込むのみ)、 行数(レコード数)をカウントする。 ・読み込んだ1行分の文字列の末尾の改行文字('\n')を除いた文字数が 2048文字を超えた行は無視する。(※カウントしない) <行数(レコード数)を格納する変数の例> long nRecCnt; ← 取得した行数(レコード数)を格納する。 3)取得した行数分の、データ構造体(1レコード相当)の配列を確保する。 <1レコード分のデータ構造体の例> typedef struct TT_DATA { long nNum; /* 1カラム目の数値データ用 */ char szItem[256]; /* 2カラム目の文字列データ用 */ char szOther[2048]; /* 3カラム目以降のデータ用 */ int nOutFlag; /* CSV出力の識別フラグ */ } T_DATA; ※上記の文字列データのサイズは適当に割り振ってあります。 実際は、仕様に合わせて調整する必要があります。 ※3カラム目以降のデータは、プログラム上で編集する必要がないので、 1つの文字列として取り込むように、1つのバッファにしてあります。 ※CSV出力の識別フラグ(nOutFlag)は、出力CSVファイルに書き込む レコードを識別するためのフラグに使用します。 上記の構造体の場合で、行数分(レコード数分)の配列を確保する場合、 T_DATA *t_data; t_data = (T_DATA *)malloc( sizeof(T_DATA) * nRecCnt ); ※nRecCnt は取得した行数(レコード数) のような感じになります。 4)入力元のCSVファイルのファイルポインタを先頭に戻して(先頭へシークする) 再び、1行毎に読み込みながら、3)で確保した構造体配列に、全行数分の データを格納する。 ・読み込んだ1行分の文字列の末尾の改行文字('\n')を除いた文字数が、 2048文字を超えた行は無視する。(※データは取り込まない) ・1カラム目のデータは、「数字文字列→数値」変換を行い、.nNum に格納する。 ・2カラム目のデータは、.szItem[] に格納する。 ・3カラム目以降のデータは、1つの文字列として、.szOther[] に格納する。 ・データ構造体の「CSV出力の識別フラグ」(.nOutFlag)は、0 でクリアしておく。 5)入力CSVファイルをクローズする。 6)データを取り込んだ、構造体配列に対して、2カラム目のデータ(文字列)をキー にしてソート処理を行い、並び替える。 ・文字列の大小判定は、strcmp関数で判定可能かと思います。 7)ソート後の構造体配列の先頭からデータを参照し、2カラム目の文字列が 同じものについて、1カラム目の数値が最大のものを抽出し、 「CSV出力の識別フラグ」(.nOutFlag)に 1 を立てる 処理を行う。 8)出力CSVファイルをオープンする。 9)データ構造体配列を先頭から参照し、「CSV出力の識別フラグ」が 1 のもの について、出力CSVファイルに書き込む処理を行う。 10)出力CSVファイルをクローズする。 11)確保したメモリを解放して、プログラムを終了する。 以上のような処理が考えられると思います。 的外れだった場合はすみません。 参考になれば幸いです。
- επιστημη(@episteme)
- ベストアンサー率46% (546/1184)
> 皆様のお力をお借り出来ないでしょうか。 で、なにが問題なんですか?
お礼
おはようございます。 言葉足らずで申し訳ありませんでした。 どのような手順でどのようなコードを書けばいいのか さっぱり分からなかった為、その辺りについて お教え頂きたかったのです。 これからは万人に分かるような書き方をするように心がけます。
- jacta
- ベストアンサー率26% (845/3158)
> ・各カラムの項目長は可変長 この「可変長」の部分がひっかかります。 可変長でもかまいませんが、長さの上限はあるのでしょうか? 上限が設定されていないのであれば、reallocでバッファを拡張しながら読み込まないといけませんので、未経験者がいますぐ取り組むのは絶望的です。
お礼
こんばんは。 仕様を再確認しました。 各カラムの項目長は決まってないらしいのですが、 レコード長の上限は2048らしいです。 つまりカラムの項目長上限も決まってるようなものですね! レコード的に2048を超えるレコードが存在した場合、 そのレコードは処理対象外とするそうです。 おいおい、最初と話が違うだろ・・・と言いたかったです。
- toda hiro(@hiro_knigh)
- ベストアンサー率39% (59/151)
とりあえず、昇順に並び替える所まで挑戦してみた。 -------- #include <stdio.h> #include <string.h> #include <stdlib.h> #define STRING_MAX 2048 #define FIELD_MAX 256 #define RECORD_MAX 16384 #define STRING_BUF 16384 typedef struct scsvrec{ char chr[FIELD_MAX][STRING_MAX]; }SCSVREC; typedef struct scsvdat{ long record_cnt; SCSVREC *dat[RECORD_MAX]; }SCSVDAT; bool csvFileRead(char *filename ,SCSVDAT *csvDat); SCSVREC* csvResolution(char *buf); int main(int argc,char *argv[]) { bool bRet = false; SCSVDAT *cdat; SCSVREC *swpwk; long ilp1,ilp2; FILE *fout; cdat = (SCSVDAT*)malloc(sizeof(SCSVDAT)); memset(cdat ,0 ,sizeof(SCSVDAT)); if (argc > 1) { bRet = csvFileRead(argv[1], cdat); } if (bRet == false) return 0; if (cdat->record_cnt >= 2) { swpwk = (SCSVREC*)malloc(sizeof(SCSVREC)); for (ilp1 = cdat->record_cnt - 2 ; ilp1 > 0 ; ilp1--) { for(ilp2 = 0 ; ilp2 < ilp1 ; ilp2 ++) { if (strcmp(cdat->dat[ilp2]->chr[1] ,cdat->dat[ilp2+1]->chr[1]) > 0) { memcpy(swpwk ,cdat->dat[ilp2] ,sizeof(SCSVREC)); memcpy(cdat->dat[ilp2] ,cdat->dat[ilp2+1] ,sizeof(SCSVREC)); memcpy(cdat->dat[ilp2+1] ,swpwk ,sizeof(SCSVREC)); } } } } if (argc > 2) { fout = fopen(argv[2] ,"w"); if(fout == NULL){ printf("出力ファイルが開けませんでした。\n"); return 0; } for (ilp1 = 0 ; ilp1 < cdat->record_cnt ; ilp1++) { fprintf(fout,"%s,%s,%s\n",cdat->dat[ilp1]->chr[0],cdat->dat[ilp1]->chr[1],cdat->dat[ilp1]->chr[2]); } fclose(fout); } return(0); } bool csvFileRead(char *filename ,SCSVDAT *csvDat) { FILE *fp; SCSVREC *onerec; bool ret = false; char buf[STRING_BUF]; memset(csvDat ,0 ,sizeof(SCSVDAT)); fp = fopen(filename ,"r"); if(fp == NULL){ printf("入力ファイルが開けませんでした。\n"); return(false); } while(fgets(buf,sizeof(buf),fp) != NULL){ if (buf[strlen(buf)-1] == '\n'){ buf[strlen(buf)-1] = '\0'; } if (strlen(buf) >0) { onerec = csvResolution(buf); csvDat->dat[csvDat->record_cnt] = onerec; (csvDat->record_cnt)++; } } fclose(fp); return (true); } SCSVREC* csvResolution(char *buf) { int bcnt, rcnt, mcnt; bool dblfld; SCSVREC *onedat; onedat = (SCSVREC*)malloc(sizeof(SCSVREC)); memset(onedat ,0 ,sizeof(SCSVREC)); bcnt = rcnt = mcnt= 0; dblfld = false; for(;;bcnt++) { if (buf[bcnt] == '\0') break; if (mcnt == 0 && buf[bcnt] == '\"') { dblfld = true; continue; } if (dblfld == false && buf[bcnt] == ',') { rcnt++; mcnt = 0; continue; } if (dblfld == true && buf[bcnt] == ',') { onedat->chr[rcnt][mcnt] = buf[bcnt]; mcnt++; continue; } if (dblfld == true && buf[bcnt] == '\"' && buf[bcnt+1] == '\"') { onedat->chr[rcnt][mcnt] = '\"'; mcnt++; bcnt++; continue; } if (dblfld == true && buf[bcnt] == '\"' && (buf[bcnt+1] == ',' || buf[bcnt+1] == '\0')) { rcnt++; mcnt = 0; bcnt++; dblfld = false; continue; } onedat->chr[rcnt][mcnt] = buf[bcnt]; mcnt++; } return onedat; }
お礼
こんばんは。回答有難うございます。 今、出先の為、帰社してから試してみようと思うのですが、かなりやっかいそうですね。取り敢えず少しでも進むように頑張ってみます。
- Tacosan
- ベストアンサー率23% (3656/15482)
「超短納期」で「C言語は経験が無い」人にやらせるって, この時点ですでに無理筋だよなぁ.... さておき, どうしても C じゃないとダメなの? ほかのスクリプト系言語ならもっと簡単にできるはずなのに. 各カラムのデータにカンマ (,) がないなら Perl では %records; while (<>) { $data = [split /,/]; next if exist $records{$data->[1]} && $records{$data->[1]}->[0] > $data->[0]; $records{$data->[1]} = $data; } for (sort { $a <=> $b; } keys %records) { print join(',', @{$records{$_}}); } くらいで終わりなのに.
お礼
こんばんは。回答有難うございます。 確認したところ、C以外は却下と言われました。 ソースを提示頂いたのに申し訳ないです。
お礼
おはようございます。 ご回答有難う御座います。 どのような手順で作成したらいいのかさっぱりでしたので 大変参考になりました。 この週末になんとかご提示頂いた手順でC言語の参考書を読みながら コーディング出来るように努力してみます。 本当に有難う御座いました。