4

C++ CSV データ パーサーを構築しています。ファイルの 1 列目と 15 列目にアクセスし、コマンドを使用して 2 つの配列に読み込もうとしていgetlineます。例えば:

for(int j=0;j<i;j++)
{
    getline(posts2,postIDs[j],',');
    for(int k=0;k<14;k++)
    {
        getline(posts2,tossout,',');
    }
    getline(posts2,answerIDs[j],',');
    getline(posts2,tossout,'\r');

ただし、最初の列と 15 番目の列の間には、引用符で囲まれた列があり、さまざまなコンマと緩やかな引用符が含まれています。例えば:

...,"abc, defghijk."Lmnopqrs, "tuv,"" wxyz.",... <

この列を回避する最善の方法は何ですか? 引用符とコンマが含まれているため、取得できません。引用に出くわした後、 「、」が見つかるまで、引用されたジャンクを1文字ずつ読む必要があり ますか?

また、他のソリューションを見てきましたが、それらはすべて Windows/Visual Studio に限定されています。私はMac OSX版を実行しています。10.8.3 と Xcode 3.2.3。

前もって感謝します!ドリュー

4

2 に答える 2

10

CSV形式の正式な標準はありませんが、最初にあなたが引用した醜い列に注意してください:

"abc, defghijk. "Lmnopqrs, "tuv,"" wxyz.",

CSVの基本ルールと見なされるものに準拠していません。

  • 1) コンマが埋め込まれたフィールドは引用符で囲む必要があります。

  • 2) 埋め込まれた二重引用符文字はそれぞれ、二重引用符のペアで表す必要があります。

問題の列がルール 1) に従う場合、ルール 2) には従いません。しかし、ルール 1) に従うようにそれを解釈することができます。つまり、どこで終わるかを言うことができます。

[abc, defghijk. [Lmnopqrs, ]tuv,[] wxyz.],

バランスの取れた最も外側の引用符で列を囲みます。バランスの取れた内部クオートは、バランシングがそれらを内部にすることを除いて、内部であることの他の兆候を欠いている可能性があります.

このテキストをルール 1) と一貫して 1 つの列として解析し、ルール 2) にも従う列も解析する ルールが必要です。両方のルールに従う列は必然的にバランス調整も可能になるため、先ほど示したバランス調整は、これが可能であることを示唆しています。

推奨されるルールは次のとおりです。

  • 列は、0 個の二重引用符が先行するか、偶数個の二重引用符の最後のカンマが先行する最初のコンマまで実行されます。

コンマまでの二重引用符が偶数個ある場合、少なくとも 1 つの方法で、引用符を囲み、残りの部分のバランスを取ることができることがわかります。

あなたが検討しているより単純なルール:

引用に出くわした後、「、」が見つかるまで、引用されたジャンクを1文字ずつ読む必要がありますか?

ルール 2) に従う特定の列に一致すると失敗します。

「スーパー、「豪華」」、トラック」、

より単純なルールは、 の後に列を終了します""luxurious""。ただし、この列はルール 2) に準拠しているため、隣接する二重引用符は「エスケープされた」二重引用符であり、区切りの意味はありません。一方、提案されたルールは引き続き列を正しく解析し、 の後に終了しtruck"ます。

get_csv_column関数が提案されたルールによって列を解析するデモ プログラムを次に示します。

#include <iostream>
#include <fstream>
#include <cstdlib>  

using namespace std;

/*
    Assume `in` is positioned at start of column.
    Accumulates chars from `in` as long as `in` is good
    until either:-
        - Have consumed a comma preceded by 0 quotes,or
        - Have consumed a comma immediately preceded by
        the last of an even number of quotes.
*/
std::string get_csv_column(ifstream & in)
{
    std::string col;
    unsigned quotes = 0;
    char prev = 0;
    bool finis = false;
    for (int ch; !finis && (ch = in.get()) != EOF; ) {
        switch(ch) {
        case '"':
            ++quotes;
            break;
        case ',':
            if (quotes == 0 || (prev == '"' && (quotes & 1) == 0)) {
                finis = true;
            }
            break;
        default:;
        }
        col += prev = ch;
    }
    return col;
}

int main()
{
    ifstream in("csv.txt");
    if (!in) {
        cout << "Open error :(" << endl;
        exit(EXIT_FAILURE);
    }
    for (std::string col; in; ) {
        col = get_csv_column(in),
        cout << "<[" << col << "]>" << std::endl;
    }
    if (!in && !in.eof()) {
        cout << "Read error :(" << endl;
        exit(EXIT_FAILURE);
    }
    exit(EXIT_SUCCESS);
}

各列を で囲み<[...]>、改行を無視せず、各列に終端の ',' を含めます。

ファイルcsv.txtは次のとおりです。

...,"abc, defghijk. "Lmnopqrs, "tuv,"" wxyz.",...,
",","",
Year,Make,Model,Description,Price,
1997,Ford,E350,"Super, ""luxurious"", truck",
1997,Ford,E350,"Super, ""luxurious"" truck",
1997,Ford,E350,"ac, abs, moon",3000.00,
1999,Chevy,"Venture ""Extended Edition""","",4900.00,
1999,Chevy,"Venture ""Extended Edition, Very Large""",,5000.00,
1996,Jeep,Grand Cherokee,"MUST SELL!
air, moon roof, loaded",4799.00,

出力は次のとおりです。

<[...,]>
<["abc, defghijk. "Lmnopqrs, "tuv,"" wxyz.",]>
<[...,]>
<[
",",]>
<["",]>
<[
Year,]>
<[Make,]>
<[Model,]>
<[Description,]>
<[Price,]>
<[
1997,]>
<[Ford,]>
<[E350,]>
<["Super, ""luxurious"", truck",]>
<[
1997,]>
<[Ford,]>
<[E350,]>
<["Super, ""luxurious"" truck",]>
<[
1997,]>
<[Ford,]>
<[E350,]>
<["ac, abs, moon",]>
<[3000.00,]>
<[
1999,]>
<[Chevy,]>
<["Venture ""Extended Edition""",]>
<["",]>
<[4900.00,]>
<[
1999,]>
<[Chevy,]>
<["Venture ""Extended Edition, Very Large""",]>
<[,]>
<[5000.00,]>
<[
1996,]>
<[Jeep,]>
<[Grand Cherokee,]>
<["MUST SELL!
air, moon roof, loaded",]>
<[4799.00]>
于 2013-07-19T15:55:59.317 に答える