C++/STLでCSVファイルの読み込み

久しぶりのC++/STLネタということで、CSVファイルの読み込みです。
もともと、Apacheが出力するアクセスログの読み込みの為に作ったのですが、せっかくなので、Excelが出力するCSVにも対応しました。
CSVファイルの読み込みは他の言語ならsplit一発でお茶を濁すのですが、カンマ(,)やダブルクオート(")、改行があるデータにも対応してます。

/**********************************************************************
 CSVファイルの読み込みサンプル
 試したコンパイル環境
    VC++ .NET 2003 / WINDOWS XP Professional 64 bit edition.
    GCC C++ 3.3.6 / glibc 2.3.4 / Vine Linux 4.2
**********************************************************************/

#include <functional>
#include <algorithm>
#include <iostream>
#include <fstream>
#include <vector>
#include <string>

/**********************************************************************
 クオート文字の定義構造体
**********************************************************************/
struct quote{
    char    start;  // 開始文字
    char    end;    // 終了文字
    char    escape; // エスケープ文字
    quote( char start_, char end_, char escape_) :
         start(start_), end(end_), escape(escape_) {}
};

struct cmpStartQuote : public std::binary_function<quote,char,bool> {
    bool operator()(const quote &l_s1, const char &l_s2) const {
        return l_s1.start == l_s2;
    }
};

/**********************************************************************
 CSVの読取
**********************************************************************/
bool readCSV(
    std::vector< std::string > &result, // 結果格納ベクター
    std::istreambuf_iterator<char> &i,  // 入力文字イテレータ
    std::string &separator,             // 区切り文字
    std::vector<quote> &quotes)         // クオート文字ベクター
{
    bool retval = false;
    result.clear();
    std::string item;
    char    bc = '\0';
    bool    first = true;
    std::vector<quote>::iterator    ff = quotes.end();

    while ( i != std::istreambuf_iterator<char>() &&
                    (ff != quotes.end() || *i != '\n') ) {
        if ( ff == quotes.end() &&
                            strchr( separator.c_str(), *i) != 0 ) {
            result.push_back(item);
            item.clear();
            first = true;
            retval = true;
        } else {
            if ( first &&
                (ff = find_if( quotes.begin(),
                            quotes.end(),
                            bind2nd( cmpStartQuote(), *i)))
                    != quotes.end() ) {
                first = false;
            } else if ( ff != quotes.end() && ff->end == *i ) {
                if ( ff->end == ff->escape ) {
                    bc = *i++;
                    if ( i == std::istreambuf_iterator<char>() )
                        break;
                    if ( *i == ff->end ) {
                        item.push_back(*i);
                    } else {
                        ff = quotes.end();
                        continue;
                    }
                } else {
                    if ( bc == ff->escape ) {
                        item.push_back(*i);
                    } else {
                        ff = quotes.end();
                    }
                }
            } else {
                item.push_back(*i);
                first = false;
            }
        }
        bc = *i++;
    }
    result.push_back(item);
    if ( i != std::istreambuf_iterator<char>() ) {
        retval = true;
        i++;
    }
    return retval;
}

using namespace std;


int main(int argc, char* argv[])
{
    basic_ifstream<char>        inputFile("access_log");
    istreambuf_iterator<char>   sin(inputFile);
    vector< string >            rec;

    // APACHEのアクセスログ
    string                      separator(" ");
    vector< quote >             quotes;

    // クオート文字(")の設定("を表示したいときは\"になる)
    quotes.push_back( quote( '"', '"', '\\') );
    // 時間部分のクオート文字([]で括る)
    quotes.push_back( quote( '[', ']', 0 ) );

    while ( readCSV( rec, sin, separator, quotes) ) {
        vector< string >::iterator i;
        for ( i = rec.begin(); i < rec.end(); i++ ) {
            cout << i - rec.begin() << ":" << *i << endl;
        }
        cout << endl;
    }

    return 0;
}
 
上記コードは、Apacheのログを読み込むサンプルで、CSVファイルの読み込みの場合は、
separatorとquotesの部分が、
 
    string                      separator(",");
    vector< quote >             quotes;

    quotes.push_back( quote( '"', '"', '"') );
 
になります。

2008-08-04 | コメント:0件

コメントをどうぞ


『不適切なコメントへの対応』を一読下さい。
現在コメントは承認制となっております。

Previous Page | Next Page