『プログラミング作法』 第4章

/* csv.c: csvライブラリ本体 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "csv.h"

enum { NOMEM = -2 };				/* メモリ不足のシグナル */

/* 独立csvフィールド構造体 */
typedef struct csv_field {
	int csv_num;				/* csv独立ナンバー */
	char *line;					/* 入力文字 */
	char *sline;				/* splitで使われる行のコピー */
	int maxline;				/* line[]とsline[]のサイズ */
	char **field;				/* フィールドポインタ */
	int maxfield;				/* field[]のサイズ */
	int nfield;					/* field[]中のフィールド数 */
	char *fieldsep;				/* フィールドセパレート文字 */
} CsvFld;

// typedef int CsvNum;					/* 独立csvフィールドナンバー */

static CsvFld* pcsvfld = NULL;			/* 独立csvフィールド構造体配列へのポインタ */
static int ncsvfld = 0;				/* 独立csvフィールド構造体の現個数 */
static CsvNum unicsvnum = 0;			/* 独立csvフィールドナンバー変数 */

/* pcf_elem: pcsvfld配列のアドレスを返す */
CsvFld* pcf_elem(CsvNum cn)
{
	int i;
	
	if (cn < 0) return NULL;
	for (i = 0; i < ncsvfld; i++) {
		if (pcsvfld[i].csv_num == cn) break;
	}
	if (i >= ncsvfld) return NULL;	/* 該当なし */
	
	return &(pcsvfld[i]);
}

/* reset: 変数を最初の値に戻す */
static int reset(CsvNum cn)
{
	CsvFld* p;
	
	if((p=pcf_elem(cn)) == NULL) return -1;

	free(p->line);		/* free(NULL)はANSI Cで容認されている */
	free(p->sline);
	free(p->field);
	free(p->fieldsep);
	p->line = NULL;
	p->sline = NULL;
	p->field = NULL;
	p->maxline = p->maxfield = p->nfield = 0;
	return 0;
}

/* init: 独立csvフィールドの初期化 */
void init(CsvNum cn)
{
	int i;
	
	if (cn < 0) return;			/* 不正な値 */
	for (i = 0; i < ncsvfld; i++) {
		if (pcsvfld[i].csv_num == cn) break;
	}
	if (i >= ncsvfld) i = ncsvfld-1;	/* 該当なし */
	
	printf("elements:%d address:%p\n", ncsvfld, &pcsvfld[i]);
	pcsvfld[i].csv_num = cn;
	pcsvfld[i].line = NULL;
	pcsvfld[i].sline = NULL;
	pcsvfld[i].maxline = 0;
	pcsvfld[i].field = NULL;
	pcsvfld[i].maxfield = 0;
	pcsvfld[i].nfield = 0;
	pcsvfld[i].fieldsep = NULL;
}

/* csvnew: 独自csvフィールドを生成 */
CsvNum csvnew(char* fldsp)
{
	CsvFld* newpcf;

	if (pcsvfld == NULL || ncsvfld == 0) {
		newpcf = (CsvFld*) malloc(sizeof(CsvFld));
		if (newpcf == NULL) return NOMEM;
		ncsvfld++;
	}
	else {
		newpcf = (CsvFld*) realloc(pcsvfld, sizeof(CsvFld) * ++ncsvfld);
		if (newpcf == NULL) return NOMEM;
	}
	pcsvfld = newpcf;
	unicsvnum++;
	init(unicsvnum);
	pcsvfld[ncsvfld-1].fieldsep = (char*) malloc(strlen(fldsp)+1);
	if (pcsvfld[ncsvfld-1].fieldsep == NULL) return NOMEM;
	memmove(pcsvfld[ncsvfld-1].fieldsep, fldsp, strlen(fldsp)+1);
	return unicsvnum;
}

/* csvfree: 独自csvフィールドの開放 */
int csvfree(CsvNum cn)
{
	int i;
	CsvFld* newpcf;

	for (i = 0; i < ncsvfld; i++) {
		if (pcsvfld[i].csv_num == cn) break;
	}
	if (i >= ncsvfld) return -1;
	reset(cn);
	memmove(pcsvfld+i, pcsvfld+i+1, (sizeof(CsvFld)*(ncsvfld-i-1)));
	newpcf = (CsvFld*)realloc(pcsvfld, sizeof(CsvFld) * --ncsvfld);
	if (newpcf == NULL) return NOMEM;

	pcsvfld = newpcf;

	return 0;
}

/* advquoted: クォートでくくられたフィールド:次のセパレータのポインタを返す */
static char *advquoted(CsvNum cn, char *p)
{
	int i, j, elm;
	
	for (i = j = 0; p[j] != '\0'; i++, j++) {
		if (p[j] == '"' && p[++j] != '"') {
			/* 次のセパレータか'\0'までコピー */
			int k = strcspn(p+j, pcf_elem(cn)->fieldsep);
			memmove(p+i, p+j, k);
			i += k;
			j += k;
			break;
		}
		p[i] = p[j];
	}
	p[i] = '\0';
	return p + j;
}

/* isfldsep: フィールドセパレート文字判定 */
int isfldsep(CsvNum cn, char c)
{
	char *p;
	p = pcf_elem(cn)->fieldsep;
	do {
		if (*(p++) == c) return 1;
	} while (*p);
	return 0;
}
/* split: 行をフィールド単位に分割 */
static int split(CsvNum cn)
{
	char *p, **newf;
	char *sepp;			/* 一時セパレータ文字を指すポインタ */
	int sepc;			/* 一時セパレータ文字 */
	CsvFld* pcf;

	if ((pcf=pcf_elem(cn)) == NULL) return -1;	/* フィールドナンバーが見つからない */

	pcf->nfield = 0;
	if (pcf->line[0] == '\0')
		return 0;
	strcpy(pcf->sline, pcf->line);
	p = pcf->sline;

	do {
		if (pcf->nfield >= pcf->maxfield) {
			pcf->maxfield *= 2;				/* 現在のサイズを倍に */
			newf = (char **) realloc(pcf->field, pcf->maxfield * sizeof(pcf->field[0]));
			if (newf == NULL)
				return NOMEM;
			pcf->field = newf;
		}
		if (*p == '"')
			sepp = advquoted(cn, ++p);		/* 最初のクォートをスキップ */
		else
			sepp = p + strcspn(p, pcf->fieldsep);
		sepc = sepp[0];
		sepp[0] = '\0';					/* フィールドを終了させる */
		pcf->field[(pcf->nfield)++] = p;
		p = sepp + 1;
	} while (isfldsep(cn, sepc));

	return pcf->nfield;
}

/* endofline: \r, \n \r\n, EOFをチェックして捨てる */
static int endofline(FILE *fin, int c)
{
	int eol;
	eol = (c=='\r' || c=='\n');
	if (c == '\r') {
		c = getc(fin);
		if (c != '\n' && c != EOF)
			ungetc(c, fin);	/* 読みすぎたのでcを戻す */
	}
	return eol;
}

/* csvgetline: 1行取得し、必要に応じて伸張 */
/* サンプル入力: "LU",86.25,"11/1/1998","2:19PM",+4.0625 */
char *csvgetline(FILE *fin, CsvNum cn)
{
	int i, c;
	char *newl, *news;
	CsvFld* pcf;

	if ((pcf=pcf_elem(cn)) == NULL) return NULL;	/* フィールドナンバーが見つからない */

	if (pcf->line == NULL) {			/* 最初の呼び出し時に割り当て */
		pcf->maxline = pcf->maxfield = 1;
		pcf->line = (char *) malloc(pcf->maxline);
		pcf->sline = (char *) malloc(pcf->maxline);
		pcf->field = (char **) malloc(pcf->maxfield*sizeof(pcf->field[0]));
		if (pcf->line == NULL || pcf->sline == NULL || pcf->field == NULL) {
			reset(cn);
			return NULL;		/* メモリ不足 */
		}
	}
	for (i=0; (c=getc(fin))!=EOF && !endofline(fin, c); i++) {
		if (i >= pcf->maxline-1) {	/* 行を伸張 */
			pcf->maxline *= 2;		/* 現在のサイズを2倍に */
			newl = (char *) realloc(pcf->line, pcf->maxline);
			news = (char *) realloc(pcf->sline, pcf->maxline);
			if (newl == NULL || news == NULL) {
				reset(cn);
				return NULL;	/* メモリ不足 */
			}
			pcf->line = newl;
			pcf->sline = news;
		}
		pcf->line[i] = c;
	}
	pcf->line[i] = '\0';
	if (split(cn) == NOMEM) {
		reset(cn);
		return NULL;			/* メモリ不足 */
	}
	return (c == EOF && i == 0) ? NULL : pcf->line;
}

/* csvfield: n番目のフィールドのポインタを返す */
char *csvfield(CsvNum cn, int n)
{
	CsvFld* p;
	
	if ((p=pcf_elem(cn)) == NULL) return NULL;

	if (n < 0 || n >= p->nfield)
		return NULL;
	return p->field[n];
}

/* csvnfield: フィールド数を返す */
int csvnfield(CsvNum cn)
{
	CsvFld* p;
	
	if ((p=pcf_elem(cn)) == NULL) return -1;
	
	return p->nfield;
}
/* csvtest main: CSVライブラリのテスト */
#include <stdio.h>
#include "csv.h"

int main(void)
{
	int i, j;
	char *line;
	CsvNum cn;

	for (j = 0; j < 3; j++) {

		cn = csvnew(",");		/* 新規独自csvフィールドを確保 */
		printf("独自csvフィールド取得値%d\n",cn);

		while ((line = csvgetline(stdin, cn)) != NULL) {
			printf("line = '%s'\n", line);
			for (i = 0; i < csvnfield(cn); i++)
				printf("field[%d] = '%s'\n", i , csvfield(cn, i));
		}
		if(csvfree(cn)==-1) printf("error\n");
	}
	return 0;
}
/* csv.h: csvライブラリのインターフェース */
typedef int CsvNum;

extern char *csvgetline(FILE *F, CsvNum cn);	/* 次の入力行を読む */
extern char *csvfield(CsvNum cn, int n);		/* フィールドnを返す */
extern int csvnfield(CsvNum cn);				/* フィールド数を返す */
extern CsvNum csvnew(char* fieldsep);			/* 独自csvフィールドを確保 */
extern int csvfree(CsvNum cn);					/* 独自csvフィールドの解放 */