mikan's technical note

仕事&趣味で実験した小技の備忘録です(Linux,windows,DOS等)

MENU

【C言語】SymfowareのSELECT結果をCSV形式に変換する

//
// SymfowareのSELECT結果をCSV形式へ変換 ※Solarisで動作確認済(1行32kまで対応)
// (c) 2018 mikan
// ※使用にあたっては利用者の自己責任でお願いします。
//
// 使い方:SQLtoCSV input [output]
//         例)SQLtoCSV select.log
//         例)SQLtoCSV select.log select.txt
//
//         おまけ機能)INSERT SQL を作成する方法
//         SQLtoCSV select.log select.txt -insert
//

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>

#define BUFF_MAX    32768    // 入力ファイルの1行の最大サイズ
#define FIELD_MAX    1024    // 項目の最大数

int MakeCsvHead(char *, char *, int);
int MakeCsvData(char *, char *, int, int, int);

char fullpath1[2048];
char fullpath2[2048];

int main(int argc, char **argv) 
{
    int        len;
    int        head_count;
    int        data_count;
    int        dc_flag;
    int        sp_flag;
    int        insert_flag;
    struct     stat    st;
    char       work[1024];

    dc_flag = 1;
    sp_flag = 0;
    insert_flag = 0;

    if(argc <= 1 || argc >= 4) {
        if(argc == 4 && strcmp(argv[3], "-insert") == 0) {
            printf("-insert オプション検出! insert.sql を作成します\n");
            insert_flag = 1;
        } else {
            printf("パラメータが不正です\n");
            return -1;
        }
    }

    // ----- 入出力ファイル名の取得
    strcpy(fullpath1, argv[1]);

    // ----- 入力ファイルのチェック
    if(stat(fullpath1, &st) != 0) {
        printf("%s\n入力ファイルが存在しません\n", fullpath1);
        return -1;
    }

    // ----- 出力ファイル名の自動生成
    if(argc == 3) {
        strcpy(fullpath2, argv[2]);
    } else {
        strcpy(fullpath2, fullpath1);
        strcat(fullpath2, ".txt");
    }

    // ----- 入出力ファイル名のチェック
    if(strcmp(fullpath1, fullpath2) == 0) {
        printf("変換元/変換先ファイル名が同じ\n");
        return -1;
    }

    printf("SELECT結果変換 - 開始\n");

    // ----- CSVファイル作成
    head_count = MakeCsvHead(fullpath1, fullpath2, dc_flag);
    data_count = MakeCsvData(fullpath1, fullpath2, dc_flag, sp_flag, insert_flag);

    printf("SELECT結果変換 - 完了っ[%d件] [%d項目]\n", data_count, head_count);
}

// NUMERICチェック(先頭が'+'または'-'で、
//                 後続が全て数値('0'-'9')または'.'で、
//                 '.'が1個の場合、0を返す)
int numeric_check(char *str)
{
    int        i;
    int        len;
    int        count;

    len = 0;
    count = 0;

    // 先頭が'+'でも'-'でもない場合は、NUMERIC型ではない
    if(str[0] != '-' && str[0] != '+') {
        return -1;
    }

    // 全桁チェック
    len = strlen(str);
    for(i=1; i < len ; i++) {
        if(str[i] == 0x00) {
            break;
        }

        // 数値でも'.'でもない場合は、NUMERIC型ではない
        if(str[i] < 0x30 || str[i] > 0x39) {
            if(str[i] == '.') {
                count ++;
            } else {
                return -1;
            }
        }
    }

    // '.'が1個でない場合は、NUMERIC型ではない
    if(count != 1) {
        return -1;
    }

    // 数値('+9999.' or '-9999.' or '+9999.9999' or '-9999.9999')
    return 0;
}

int DeleteSpace(char *str1, char *str2, int size)
{
    int    i;
    int    len;

    strcpy(str2, str1);

    for(i=0; i<size; i++) {
        if(str1[i] == 0x20) {
            strcpy(str2, &str1[i+1]);
        } else {
            break;
        }
    }

    len = strlen(str2);    
    for(i=len-1; i>0; i--) {
        if(str2[i] == 0x20) {
            str2[i] = 0x00;
        } else {
            break;
        }
    }

    len = strlen(str2);    
    return len;
}

int CheckRecord(char *buff, char *tablename)
{
    int     i;
    char    *p;

    // ----- テーブル名の取得(xxxx を 環境に合わせて書き換えてください)
    if((p = strstr(buff, "xxxx.")) != NULL) {
        for(i=0; ; i++) {
            if(p[i] == ' ' || p[i] == 0x0a) {
                memset(tablename, 0x00, sizeof(tablename));
                memcpy(tablename, p, i);
                break;
            }
        }
    }

    if(strncmp(buff, "Number of records", 17) == 0) {
        return -1;
    }
    if(strncmp(buff, "SQLSTATE", 8) == 0) {
        return -1;
    }
    if(strncmp(buff, "SQLMSG", 6) == 0) {
        return -1;
    }
    if(strncmp(buff, "SELECT", 6) == 0) {
        return -1;
    }
    if(strncmp(buff, "select", 6) == 0) {
        return -1;
    }
    if(strncmp(buff, "Select", 6) == 0) {
        return -1;
    }

    return 0;
}

// ----- CSVヘッダ作成処理
int MakeCsvHead(char *in_file, char *out_file, int dc_flag)
{
    FILE    *fp1, *fp2;        // ファイルポインタ
    int     i;                 // インデックス
    int     len;               // 文字列のサイズ取得変数
    int     count_record;      // 出力レコード数
    int     count_field;       // 出力フィールド数
    char    buff[BUFF_MAX];    // 入力ファイルの1行読込エリア
    char    work[BUFF_MAX];    // ワークエリア

    // ----- パラメータチェック
    if(in_file == NULL || out_file == NULL) {
        return(-1);
    }

    // ----- 入力ファイルオープン(読込み専用)
    fp1 = fopen(in_file, "r");
    if(fp1 == NULL) {
        return(-1);
    }

    // ----- 出力ファイルオープン(上書き)
    fp2 = fopen(out_file, "w");
    if(fp2 == NULL) {
        fclose(fp1);
        return(-1);
    }

    // ----- 初期化
    count_record = 0;
    count_field = 0;

    // ----- 入力ファイルの先頭レコードぶん繰り返し
    while(1) {
        if(count_field >= FIELD_MAX) {
            break;
        }

        // ----- 1行読込(EOFで抜ける)
        memset(buff, 0x00, sizeof(buff));
        if(fgets(buff, BUFF_MAX-1, fp1) == NULL) {
            break;
        }

        // ----- 読み飛ばし
        if(CheckRecord(buff, work) != 0) {
            continue;
        }

        // ----- 行末の改行コードを取る
        len = strlen(buff);
        buff[len-1] = NULL;

        // ----- <<xxx>> を探す。(レコードの始まり)
        if(strncmp(buff, "<<", 2) == 0) {
            count_record ++;
            if(count_record > 1) {
                break;
            }
            continue;
        }

        // ----- ファイル出力(ヘッダ部)
        for(i = 0; i<BUFF_MAX; i ++) {
            if(buff[i] == ':') {
                count_field ++;
                if(count_field > 1) {
                    fprintf(fp2, ",");
                }
                buff[i]=0x00;

                // 前後の空白削除
                len = strlen(buff);
                memset(work, 0x00, sizeof(work));
                DeleteSpace(buff, work, len);

                // ヘッダは強制的にダブルコーテーションなしとする
                fprintf(fp2, "%s", work);
                break;
            }
        }
    }

    fprintf(fp2, "\n");

    // ----- ファイルクローズ
    fclose(fp1);
    fclose(fp2);

    return(count_field);
}

// ----- CSVデータ作成処理
int MakeCsvData(char *in_file, char *out_file, int dc_flag, int sp_flag, int insert_flag)
{
    FILE    *fp1, *fp2;        // ファイルポインタ
    FILE    *fp3;              // ファイルポインタ
    int     i;                 // インデックス
    int     len;               // 文字列のサイズ取得変数
    int     count_record;      // 出力レコード数
    int     count_field;       // 出力フィールド数
    char    buff[BUFF_MAX];    // 入力ファイルの1行読込エリア
    char    work[BUFF_MAX];    // ワークエリア
    char    tname[BUFF_MAX];   // テーブル名

    // ----- パラメータチェック
    if(in_file == NULL || out_file == NULL) {
        return(-1);
    }

    // ----- 入力ファイルオープン(読込み専用)
    fp1 = fopen(in_file, "r");
    if(fp1 == NULL) {
        return(-1);
    }

    // ----- 出力ファイルオープン(追記)
    fp2 = fopen(out_file, "a");
    if(fp2 == NULL) {
        fclose(fp1);
        return(-1);
    }

    if(insert_flag == 1) {
        // ----- 出力ファイルオープン
        fp3 = fopen("insert.sql", "w");
        if(fp3 == NULL) {
            fclose(fp1);
            fclose(fp2);
            return(-1);
        }
    }

    // ----- 初期化
    count_record = 0;
    count_field = 0;

    // ----- 入力ファイルの全レコードぶん繰り返し
    while(1) {
        if(count_field >= FIELD_MAX) {
            break;
        }

        // ----- 1行読込(EOFで抜ける)
        memset(buff, 0x00, sizeof(buff));
        if(fgets(buff, BUFF_MAX-1, fp1) == NULL) {
            break;
        }

        // ----- 読み飛ばし
        if(CheckRecord(buff, tname) != 0) {
            continue;
        }

        // ----- 行末の改行コードを取る
        len = strlen(buff);
        buff[len-1] = NULL;

        // ----- <<xxx>> を探す。(レコードの始まり)
        if(strncmp(buff, "<<", 2) == 0) {
            count_record ++;
            count_field = 0;
            if(count_record > 1) {
                fprintf(fp2, "\n");
            }
            if(insert_flag == 1) {
                if(count_record >= 2) {
                    fprintf(fp3, ");\nCOMMIT WORK;\n");
                    fprintf(fp3, "INSERT INTO %s VALUES (", tname);
                } else {
                    fprintf(fp3, "INSERT INTO %s VALUES (", tname);
                }
            }

            continue;
        }

        // ----- ファイル出力(データ部)
        for(i = 0; i<BUFF_MAX; i ++) {
            if(buff[i] == ':') {
                count_field ++;
                if(count_field > 1) {
                    fprintf(fp2, ",");
                    if(insert_flag == 1) {
                        fprintf(fp3, ",");
                    }
                }

                // 前後の空白削除
                if(sp_flag == 1) {
                    len = strlen(&buff[i+1]);
                    memset(work, 0x00, sizeof(work));
                    DeleteSpace(&buff[i+1], work, len);
                } else {
                    strcpy(work, &buff[i+1]);
                }

                    if(dc_flag == 1) {
                    if(numeric_check(work) != 0) {
                        fprintf(fp2, "\"%s\"", work);
                        if(insert_flag == 1) {
                            fprintf(fp3, "'%s'", work);
                        }
                    } else {
                        fprintf(fp2, "%s", work);
                        if(insert_flag == 1) {
                            fprintf(fp3, "%s", work);
                        }
                    }
                } else {
                    fprintf(fp2, "%s", work);
                    if(insert_flag == 1) {
                        fprintf(fp3, "%s", work);
                    }
                }
                break;
            }
        }
    }

    fprintf(fp2, "\n");
    if(insert_flag == 1) {
        fprintf(fp3, ");\nCOMMIT WORK;\n");
    }

    // ----- ファイルクローズ
    fclose(fp1);
    fclose(fp2);
    if(insert_flag == 1) {
        fclose(fp3);
    }

    return(count_record);
}