用fstream读取csv,只有fstream

时间:2018-04-08 18:12:07

标签: c++ 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 >> ...

  3. 如果我只需要读取一个特定的行,或者只需要读取一行,一种方法可能会以某种方式计算\n或行的出现次数。我怎么能这样做?

  4. (编辑)

    1. 不是最后,正如标题所提到的那样,是否可以在不涉及其他标题的情况下执行此操作,仅使用fstream(例如,目前为止)?

1 个答案:

答案 0 :(得分:2)

  

顺序是已知的,因为每一行都跟随a的变量   结构。线的数量可能会有所不同,但是,现在,让我们假设它是   也被称为。同样为了举例,我们只考虑这一点   订单,以及这些类型:

int, double, double, int

如果已知字段的数量和顺序,则可以根据需要使用>>getline分隔符,使用',''\n'进行阅读。虽然使用面向行的输入来读取整行然后stringstream来解析字段要明智得多,但是没有理由你不能仅使用{ {1}}正如您所指出的那样是您的目标。它不是一个优雅的解决方案,但仍然是一个有效的解决方案。

使用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语句将正确的值读入相应的变量,并在读取所有字段时输出(或者store)构成结构的所有4个值。 (显然,你可以在阅读时填写每个成员)。没有什么特别之处,例如

switch

示例输入文件

使用您提供的示例输入。

#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 getline分隔的','

您可以使用逻辑略有变化来使用'\n'。在这里,您使用getline阅读前3个字段,当找到第3个字段时,您会使用f.getline(buf, MAXC, ',')读取最终字段。例如,

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(); } 运算符不同,如上所述使用>>时,每个{{1}后面都不会有空格 }}。)

示例使用/输出

输出相同。

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,逻辑都是相同的。您需要某种方式来协调您的读取与正确的字段。保持简单的现场计数器就像其他任何事情一样简单。仔细看看,如果您有其他问题,请告诉我。