ぼくのかんがえた csvread(内容は double に限る)
Visual C++ 2008 Express Edition で確認した.
main() の data に CSV ファイルの内容が入る.後に示すコードでは,各列の平均値を画面表示している.行列数はそれぞれ決め打ち.コマンドライン引数で指定できるように拡張したらいいんじゃないの.
変数説明
変数名 | 説明 |
---|---|
DATA_COLS | 列数.表の横方向 |
DATA_ROWS | 行数.表の縦方向 |
LINE_BUFFER_SIZE | 読み込む CSV ファイルの,1行のバイト数.fgets() で使う |
SEP_CAHR | 区切り文字.タブなら '\t' となる.strchr() の仕様に合わせ,int にキャストした |
argv[1] | 読み込むファイル.( argc == 1 ) が偽の時は default_filename を読む |
メモ
- atof() に与えるポインタを変えていけばいいじゃないと考えた
- SEP_CHAR を探し,その位置に '\0' と上書きするコードを書いたら,別のところに書き込まれる事態となった
- atof() は '\0' まで判定をするわけじゃなかった.「数字でない文字まで」読み込むわけで,'\0' を書き込む必要が無かった
- それならば fgets() 後の '\n' 削除も要らないなあ.なんとなく残している
- SEP_CHAR を探し,その位置に '\0' と上書きするコードを書いたら,別のところに書き込まれる事態となった
- 2次元配列の動的確保方法が分からなかった.「2次元配列 malloc」を検索して解決した
- 読み込みファイルが想定外のフォーマットだと困る
- ダブルクオートがあると困る
- 行列の過不足は困る
- realloc() すれば良いんだろうけれど,毎行やるのはなんか嫌だなあ
- ファイル読み込み失敗したとき free( data ) すべきかも.やべえ
- fclose( fp ) が抜けている.読み込み時は別にいいってどっかで見たんだけど,その理由は何だったっけなあ
コード
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <error.h> #include <malloc.h> const int DATA_COLS = 3; const int DATA_ROWS = 52040; const int LINE_BUFFER_SIZE = 128; const int SEP_CHAR = (int)','; void csvread(const char *filename, double **data, const int COLS, const int ROWS, const int SEP_CHAR) { FILE *fp; errno_t err = fopen_s( &fp, filename, "r"); if ( err == EINVAL ) { puts("file open error"); exit(0); } char line_buffer[LINE_BUFFER_SIZE]; for(int row = 0; ( (int)fgets( line_buffer, LINE_BUFFER_SIZE - 1, fp ) != EOF ) && row < ROWS; row++) { data[row] = (double *)malloc( sizeof( double ) * COLS ); if ( strchr( line_buffer, '\n') != NULL ) { line_buffer[strlen(line_buffer)-1] = '\0'; } char *sep_char_pointer; char *data_pointer = line_buffer; for ( int col = 0; col < COLS; col++ ) { data[row][col] = atof( data_pointer ); sep_char_pointer = strchr( data_pointer, SEP_CHAR); data_pointer = sep_char_pointer + 1; } } } void csvfree(double **data, const int ROWS) { for ( int row = 0; row < ROWS; row++ ) { free( data[row] ); } } int main(int argc, char **argv) { char *default_filename = "c:\\you.csv"; char *filename; if ( argc > 1 ) { filename = argv[1]; }else{ filename = default_filename; } double **data; data = (double **)malloc( sizeof( double *) * DATA_ROWS ); csvread(filename, data, DATA_COLS, DATA_ROWS, SEP_CHAR); double average[] = {0.0, 0.0, 0.0}; for ( int row = 0; row < DATA_ROWS; row++) { for ( int col = 0; col < DATA_COLS; col++ ) { average[col] += data[row][col]; } } for ( int col = 0; col < DATA_COLS; col++ ) { printf("%.10lf\n", average[col] / (double)DATA_ROWS ); } csvfree(data, DATA_ROWS); free( data ); return 0; }