電子書籍の厳選無料作品が豊富!

皆様、はじめまして。
この度、急ぎでプログラムを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"

A 回答 (6件)

もう、結構時間がたったから良いかな。


いかにソースを示します。
とりあえず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;
}
    • good
    • 1

こんにちは。



処理の概略手順だけ考えてみました。
※下記は一例です。

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)確保したメモリを解放して、プログラムを終了する。

以上のような処理が考えられると思います。

的外れだった場合はすみません。
参考になれば幸いです。
    • good
    • 0
この回答へのお礼

おはようございます。
ご回答有難う御座います。
どのような手順で作成したらいいのかさっぱりでしたので
大変参考になりました。
この週末になんとかご提示頂いた手順でC言語の参考書を読みながら
コーディング出来るように努力してみます。

本当に有難う御座いました。

お礼日時:2009/12/05 07:54

> 皆様のお力をお借り出来ないでしょうか。



で、なにが問題なんですか?
    • good
    • 0
この回答へのお礼

おはようございます。
言葉足らずで申し訳ありませんでした。

どのような手順でどのようなコードを書けばいいのか
さっぱり分からなかった為、その辺りについて
お教え頂きたかったのです。

これからは万人に分かるような書き方をするように心がけます。

お礼日時:2009/12/05 07:57

> ・各カラムの項目長は可変長



この「可変長」の部分がひっかかります。
可変長でもかまいませんが、長さの上限はあるのでしょうか?
上限が設定されていないのであれば、reallocでバッファを拡張しながら読み込まないといけませんので、未経験者がいますぐ取り組むのは絶望的です。
    • good
    • 0
この回答へのお礼

こんばんは。
仕様を再確認しました。
各カラムの項目長は決まってないらしいのですが、
レコード長の上限は2048らしいです。
つまりカラムの項目長上限も決まってるようなものですね!
レコード的に2048を超えるレコードが存在した場合、
そのレコードは処理対象外とするそうです。

おいおい、最初と話が違うだろ・・・と言いたかったです。

お礼日時:2009/12/04 01:50

とりあえず、昇順に並び替える所まで挑戦してみた。



--------
#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;
}
    • good
    • 0
この回答へのお礼

こんばんは。回答有難うございます。
今、出先の為、帰社してから試してみようと思うのですが、かなりやっかいそうですね。取り敢えず少しでも進むように頑張ってみます。

お礼日時:2009/12/03 17:53

「超短納期」で「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{$_}});
}
くらいで終わりなのに.
    • good
    • 0
この回答へのお礼

こんばんは。回答有難うございます。
確認したところ、C以外は却下と言われました。
ソースを提示頂いたのに申し訳ないです。

お礼日時:2009/12/03 17:47

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!