end0tknr's kipple - web写経開発

太宰府天満宮の狛犬って、妙にカワイイ

C言語によるCSV読込、ハッシュ、構造体の使用例

久しぶりにCで書いたのでメモ。ポイントは以下。

CSV strstr()&strcpy()とstrtok()による2パターンで記載
ハッシュ ENTRY , hsearch_data , hsearch_r() を使用
構造体 内部に文字列の配列(2次元配列?)を保持
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define __USE_GNU
#include <search.h>

#define SOKUTEI_FILENAME "SOKUTEI_DAT.csv"	/* 測定データ */
#define SOKUTEI_CSV_LEN 512			/* 測定データの1行文字数 */
#define MEMBER_IDS_FILENAME "MB_ID2MB_SID.CSV"  /* 会員ID->会員SID */
#define MEMBER_IDS_SIZE 20000	/* 最大会員数.  TODO:将来の会員増加を考慮 */
#define MID2MSID_CSV_LEN 20	/* 会員ID->会員SIDの1行文字数 */

ENTRY  entry_members;	/* 会員ID->会員SIDを保持するhash map */
ENTRY* entry_members_result;
struct hsearch_data tab;


typedef struct {
  char member_id[11];	/* 会員ID. いずれも \0を格納する為、長さ+1 byte */
  char use_date[9];	/* 測定日 */
  char branch_id[3];	/* 分岐種別 */
  char branch_no[3];	/* 分岐NO */
  char val_h[24][17];	/* 量(時間別) */
  char val_tz[4][17];	/* 量(時間帯別) */
  char val_day[17];	/* 量(終日) */
} SOKUTEI_DATA_COLS;

int parse_sokutei_csv(char *sk_line, SOKUTEI_DATA_COLS *sk_cols);


int main(void) {

  /* 会員ID->会員SID の変換HASHをload */
  load_member_id2sid();

  FILE *sk_fp;
  char sk_fname[] = SOKUTEI_FILENAME;

  if ((sk_fp = fopen(sk_fname, "r")) == NULL) {
    fprintf(stderr, "can't open file %s\n", sk_fname);
    exit(1);
  }

  char sk_line[SOKUTEI_CSV_LEN] = {'\0'};

  int i = 0;
  char member_id_pre[10] = {"\0"};
  char member_sid[10];

  while ( fgets(sk_line, SOKUTEI_CSV_LEN, sk_fp) != NULL ) {

    if (i++ == 0 ||	 /* 1行目はヘッダ行の為、読み飛ばし */
	strlen(sk_line) == 0 ){
      continue;
    }

    sk_line[strlen(sk_line) - 1] = '\0'; /* 行末の改行コード削除 */

    /* 測定データCSVをパース */
    SOKUTEI_DATA_COLS sk_cols;
    parse_sokutei_csv(sk_line, &sk_cols);

    /* 会員ID->会員SID 変換 */
    if(strcmp(sk_cols.member_id, member_id_pre) != 0) {
      entry_members.key =  (char*)malloc(10);
      sprintf(entry_members.key, "%s", sk_cols.member_id);
      int ret = hsearch_r(entry_members, FIND, &entry_members_result, &tab);

      if(ret == 0 ){
	sprintf(member_sid,"%s","\0");
      } else {
	sprintf(member_sid,"%s",entry_members_result->data);
      }
    }

    /* 測定データのメモリ使用量を出力する場合、以下のコメントを解除 */
    /* perlなら、Devel::Size を使用 */
    /* fprintf(stderr,"SOKUTEI DATA SIZE=%d bytes\n", sizeof(sk_cols) ); */

    int h = 0;
    while(h <= 23 ){
      fprintf(stdout,
	      "%s,%s,%s,%s,%s,%d,%s\n",
	      sk_cols.member_id,
	      member_sid,
	      sk_cols.use_date,
	      sk_cols.branch_id,
	      sk_cols.branch_no,
	      h, /* hour */
	      sk_cols.val_h[h]
	      );
      h++;
    }
  }


  fclose(sk_fp);
  return 0;
}

/* 測定データCSVのparse */
int parse_sokutei_csv(char *sk_line, SOKUTEI_DATA_COLS *sk_cols){
  char *p_sep, *p_end; /* セパレータ(,)と最終文字の位置(pointer) */

  p_end = sk_line + strlen(sk_line);

  p_sep = strstr(sk_line,",");
  *(p_sep++) = 0;  /* セパレータをnullにすることで、切り出し */
  strcpy(sk_cols->member_id, sk_line);
  sk_line = p_sep;

  p_sep = strstr(sk_line,",");
  *(p_sep++) = 0;
  strcpy(sk_cols->use_date, sk_line);
  sk_line = p_sep;

  p_sep = strstr(sk_line,",");
  *(p_sep++) = 0;
  strcpy(sk_cols->branch_id, sk_line);
  sk_line = p_sep;

  p_sep = strstr(sk_line,",");
  *(p_sep++) = 0;
  strcpy(sk_cols->branch_no, sk_line);
  sk_line = p_sep;

  /* 時間別の値 */
  int i = 0;
  while(i <= 23 ){
    p_sep = strstr(sk_line,",");
    *(p_sep++) = 0;
    strcpy(sk_cols->val_h[i], sk_line);
    sk_line = p_sep;
    i++;
  }
  /* 時間帯別の値 */
  i = 0;
  while(i <= 3 ){
    p_sep = strstr(sk_line,",");
    *(p_sep++) = 0;
    strcpy(sk_cols->val_tz[i], sk_line);
    sk_line = p_sep;
    i++;
  }
  /* その日の合計値 */
  p_sep = strstr(sk_line,",");
  *(p_sep++) = 0;
  strcpy(sk_cols->val_day, sk_line);

  return 0;
}

/* int parse_sokutei_csv_3_tmp(char *sk_line, SOKUTEI_DATA_COLS *sk_cols){ */
/*   char *p_sep, *p_end; /\* セパレータ(,)と最終文字の位置(pointer) *\/ */

/*   p_end = sk_line + strlen(sk_line); */

/*   while(1){ */
/*     if( !(p_sep = strstr(sk_line,",") ) ){ */
/*       if (pStr >= pEnd) break; */
/*       p_sep = p_end; */
/*     } else { */
/*       *(ptr++) = 0;
/*     } */
/*     printf("@:%s\n",pStr); */
/*     /\* 次の検索位置 *\/ */
/*     pStr = ptr; */
/*   } */
/* } */

/* 会員ID->会員SID のHASH MAP作成 */
/* なんとなくcsv parseには、strtok()を使用 */
int load_member_id2sid() {

  /* おまじないらしい */
  memset(&tab, 0, sizeof(tab));
  hcreate_r(MEMBER_IDS_SIZE, &tab);  /* create hash */

  char *member_ids_filename = MEMBER_IDS_FILENAME;
  FILE *mb_fp;

  if( (mb_fp = fopen(member_ids_filename, "r")) == NULL){
    fprintf(stderr, "can't open file %s\n", member_ids_filename);
    exit(1);
  }

  char readline[MID2MSID_CSV_LEN] = {'\0'};

  while ( fgets(readline, MID2MSID_CSV_LEN, mb_fp) != NULL ) {
    readline[strlen(readline) - 1] = '\0'; /* 行末の改行コード削除 */

    entry_members.key =  (char*)malloc(10);
    entry_members.data = (char*)malloc(10);

    /* split csv by strtok() */
    sprintf(entry_members.key, "%s", strtok(readline, ",")); /* member_id */
    sprintf(entry_members.data,"%s", strtok(NULL, ","));     /* member_sid */

    int ret = hsearch_r(entry_members, ENTER, &entry_members_result, &tab);
    if(ret == 0 ){
      fprintf(stderr, "fail add hash entry\n");
      return 1;
    }
  }

  fclose(mb_fp);
  return 0;
}