簡體   English   中英

使用fstream讀取csv,並且僅使用fstream

[英]Reading csv with fstream, and only fstream

我在此問題上還看到了其他答案,但是所有這些答案都處理std::stringstream或臨時charstd::string數組,各種其他外部庫,但我想嘗試僅使用fstream標頭,以嘗試讀取僅包含數字charshort以及float (以逗號分隔)的文件,該文本用多行分隔; 一些可能是數組或向量。 例:

1,1.1,11.1,11
2,2.2,22.2,22
3,3.3,33.3,33
...

順序是已知的,因為每一行都遵循struct的變量。 行數可能會有所不同,但是現在讓我們假設它也是已知的。 同樣為了示例,我們僅考慮此順序以及以下類型:

int, double, double, int

伴隨着我所看到的一段代碼,我嘗試了這種簡單的(並且很可能是幼稚的)方法:

int a, d;
double b, c;
char fileName {"file.txt"};
std::fstream fs {fileName};
if(!fs.is_open())
    // open with fs.out, write some defaults; this works, no need to mention
else
{
    char comma;
    while(fs.getline(fileName, 100, '\n'))
    {
        fs >> a >> comma >> b >> comma >> c >> comma >> d;
        std::cout << 2*a << ", " << 2*b << ", " << 2*c << ", " << 2*d << '\n';
    }
}

如果文件包含以上三行,並加上一個\\n ,它將輸出以下內容:

4, 4.4, 44.4, 44
6, 6.6, 66.6, 66
6, 6.6, 66.6, 66
*** stack smashing detected ***: <unknown> terminated
Aborted (core dumped)

如果在文件開頭添加\\n ,則輸出:

2, 2.2, 22.2, 22
4, 4.4, 44.4, 44
6, 6.6, 66.6, 66
6, 6.6, 66.6, 66

如果刪除最后一個\\n ,它將按預期工作。 我有幾個問題:

  1. 除了添加開頭\\n並且不插入終止文件以按預期工作之外,寫文件時還能做什么?

  2. 如果變量的數量較長,例如每行100個,我該怎么做才能避免“用fs >> a >> c >> ...繞地球fs >> a >> c >> ...

  3. 如果我只需要讀取一行或幾行,則一種方法可能會以某種方式計算\\n或行的出現。 我該怎么辦?

(編輯)

  1. 最后,正如標題所提到的,僅使用fstream (例如,目前如此),是否可以在不涉及其他標頭的情況下做到這一點?

順序是已知的,因為每一行都遵循結構中的變量。 行數可能會有所不同,但是現在讓我們假設它也是已知的。 同樣為了示例,我們僅考慮此順序以及以下類型:

 int, double, double, int 

如果知道字段的數量和順序,則可以根據需要使用>>getline並同時使用',''\\n'分隔符來進行讀取。 盡管使用面向行的輸入來讀取整行然后使用stringstream來解析字段是更明智的選擇,但是沒有理由您不能僅使用fstream做同樣的事情,正如您所指出的那樣。 這不是一個優雅的解決方案,但是仍然是一個有效的解決方案。

使用>>運算符

您的數據有4個字段,前3個字段以comma分隔,最后一個字段由newline 您可以簡單地連續循環並使用>>運算符進行讀取,並在每次讀取后測試一下fail()eof() ,例如

#include <iostream>
#include <fstream>

#define NFIELD 4
#define MAXW 128

int main (int argc, char **argv) {

    int a, d;
    double b, c;
    char comma;

    std::fstream f (argv[1]);
    if (!f.is_open()) {
        std::cerr << "error: file open failed " << argv[1] << ".\n";
        return 1;
    }

    for (;;) {          /* loop continually */
        f >> a >> comma >> b >> comma >> c >> comma >> d;
        if (f.fail() || f.eof())   
            break;
        std::cout << 2*a << "," << 2*b << "," << 2*c << "," << 2*d << '\n';
        f.ignore (MAXW, '\n');
    }
    f.close();
}

保留一個簡單的字段計數器n ,您可以使用基於字段號的簡單switch語句將正確的值讀取到相應的變量中,並在讀取所有字段時輸出(或存儲)構成結構的所有4個值。 (顯然,您也可以在閱讀時填充每個成員)。 不需要任何特殊要求,例如

#include <iostream>
#include <fstream>

#define NFIELD 4

int main (int argc, char **argv) {

    int a, d, n = 0;
    double b, c;
    char comma;

    std::fstream f (argv[1]);
    if (!f.is_open()) {
        std::cerr << "error: file open failed " << argv[1] << ".\n";
        return 1;
    }

    for (;;) {          /* loop continually */
        switch (n) {    /* coordinate read based on field number */
            case 0: f >> a >> comma; if (f.eof()) goto done; break;
            case 1: f >> b >> comma; if (f.eof()) goto done; break;
            case 2: f >> c >> comma; if (f.eof()) goto done; break;
            case 3: f >> d; if (f.eof()) goto done; break;
        }
        if (++n == NFIELD) {    /* if all fields read */
            std::cout << 2*a << "," << 2*b << "," << 2*c << "," << 2*d << '\n';
            n = 0;      /* reset field number */
        }
    }
    done:;
    f.close();
}

輸入文件示例

使用提供的樣本輸入。

$ cat dat/mixed.csv
1,1.1,11.1,11
2,2.2,22.2,22
3,3.3,33.3,33

使用/輸出示例

您只需將輸出中的每個字段加倍即可獲得所需的輸出:

$ ./bin/csv_mixed_read dat/mixed.csv
2,2.2,22.2,22
4,4.4,44.4,44
6,6.6,66.6,66

(以上兩者的輸出是相同的)

使用以',''\\n'分隔的getline

您可以在使用getline的邏輯上稍加getline 在這里,您使用f.getline(buf, MAXC, ',')讀取前3個字段,找到第三個字段時,使用f.getline(buf, MAXC)讀取最后一個字段。 例如,

#include <iostream>
#include <fstream>

#define NFIELD  4
#define MAXC  128

int main (int argc, char **argv) {

    int a = 0, d = 0, n = 0;
    double b = 0.0, c = 0.0;
    char buf[MAXC];

    std::fstream f (argv[1]);
    if (!f.is_open()) {
        std::cerr << "error: file open failed " << argv[1] << ".\n";
        return 1;
    }

    while (f.getline(buf, MAXC, ',')) { /* read each field */
        switch (n) {    /* coordinate read based on field number */
            case 0: a = std::stoi (buf); break;
            case 1: b = std::stod (buf); break;
            case 2: c = std::stod (buf); 
                if (!f.getline(buf, MAXC))  /* read d with '\n' delimiter */
                    goto done;
                d = std::stoi (buf);
                break;
        }
        if (++n == NFIELD - 1) {    /* if all fields read */
            std::cout << 2*a << "," << 2*b << "," << 2*c << "," << 2*d << '\n';
            n = 0;      /* reset field number */
        }
    }
    done:;
    f.close();
}

注意:與使用>>運算符不同,在如上所述使用getline時,每個comma后面不能有空格 。)

使用/輸出示例

輸出是相同的。

$ ./bin/csv_mixed_read2 dat/mixed.csv
2,2.2,22.2,22
4,4.4,44.4,44
6,6.6,66.6,66

無論您使用的是上面的示例還是stringstream ,您都必須知道字段的數量和順序。 無論使用循環還是if..else if..elseswitch邏輯都是相同的。 您需要某種方式來協調您的閱讀與正確的字段。 保持簡單的字段計數器幾乎和其他任何事情一樣簡單。 仔細檢查一下,如果您還有其他問題,請告訴我。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM