C ++读取csv文件并将值分配给数组

时间:2018-11-05 04:26:10

标签: c++

我试图读取一个csv文件并将其值分配给一个2d数组,但是我得到了奇怪的结果和一些垃圾值。尽管第一行是正确的,但第二和第三行却变得很奇怪。

下面是代码:

#include "pch.h"
#include <iostream>
#include <fstream>
#include <string>

using namespace std;

int main()
{
    ifstream myFile;
    myFile.open("test.csv");

    int _data[3][3];
    int i = 0;
    int j = 0;

    while (myFile.good())
    {
        string line;
        getline(myFile, line, ',');

        _data[i][j] = stoi(line);
        j++;
        if (j > 3) {
            i++;
            j = 0;
        }
    }

    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            cout << _data[i][j] << " ";
        }
        cout << endl;
    }
}

然后我创建了一个csv文件,其中包含以下数据:

1,1,1
1,2,3
3,1,3

我从代码中得到的输出结果是:

1 1 3
3 1 3
-858993460 -858993460 -858993460

我正在尝试查看循环是否出错,但对我来说似乎不错。

3 个答案:

答案 0 :(得分:2)

一个问题:

if (j > 3) 

应为j == 3,因为3无效。

您的主要问题是:

getline(myFile, line, ',');

这遗漏了行的最后一个数字,因为它没有以逗号结尾。

答案 1 :(得分:2)

对于2D数组,使用固定数组而不是vector的{​​{1}}会使您的工作变得更加困难。此外,要解析vector<int>文件,请读取每一行,然后从该行创建一个.csv,然后使用stringstream终止符然后使用{{1 }}将字段转换为整数值(C ++ 11)使过程非常简单。

例如,将要读取的文件名作为程序的第一个参数,则可以将上述内容实现为:

getline

注意','stoi提供的自动内存管理功能可让您读取任何大小的数组(最大为虚拟内存的限制)无需事先知道字段或行的数量。您可以添加简单的计数器来验证每行中包含相等数量的值,等等...)

示例输入文件

#include <iostream>
#include <fstream>
#include <sstream>

#include <string>
#include <vector>

using namespace std;

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

    string line;                    /* string to hold each line */
    vector<vector<int>> array;      /* vector of vector<int> for 2d array */

    if (argc < 2) { /* validate at least 1 argument given */
        cerr << "error: insufficient input.\n"
                "usage: " << argv[0] << " filename\n";
        return 1;
    }

    ifstream f (argv[1]);   /* open file */
    if (!f.is_open()) {     /* validate file open for reading */
        perror (("error while opening file " + string(argv[1])).c_str());
        return 1;
    }

    while (getline (f, line)) {         /* read each line */
        string val;                     /* string to hold value */
        vector<int> row;                /* vector for row of values */
        stringstream s (line);          /* stringstream to parse csv */
        while (getline (s, val, ','))   /* for each value */
            row.push_back (stoi(val));  /* convert to int, add to row */
        array.push_back (row);          /* add row to array */
    }
    f.close();

    cout << "complete array\n\n";
    for (auto& row : array) {           /* iterate over rows */
        for (auto& val : row)           /* iterate over vals */
            cout << val << "  ";        /* output value      */
        cout << "\n";                   /* tidy up with '\n' */
    }
    return 0;
}

使用/输出示例

string

仔细检查一下,如果还有其他问题,请告诉我。

答案 2 :(得分:0)

我已经回答了一组类似的问题herehere。正如我之前所说;解析简单文件时,通常更容易先获取内容。然后,一旦从文件中获取了所有需要的信息,就通常会将内容存储到字符串,某些流或缓冲区中。存储完文件后,您将需要在完成操作后将其关闭。然后,在关闭文件句柄之后,这就是您要解析该信息并将其转换为所需的数据类型的时候。使用这种方法,可以更轻松地创建一个用户定义的结构,该结构将代表您从文件中检索的数据。然后,编写函数可以帮助您完成所有这些工作。每个功能应有其自己的职责和任务。我将使用从CSV文件中提供的数据示例向您展示我的代码版本。

  

test.txt

1,1,1
1,2,3
3,1,3
#include <vector>
#include <string>
#include <sstream>
#include <iostream>
#include <fstream>
#include <exception>

struct Vector {
    int x;
    int y;
    int z;
    Vector() = default;
    Vector( int xIn, int yIn, int zIn ) :
        x( xIn ), y( yIn ), z( zIn ) 
    {}
};

std::vector<std::string> splitString( const std::string& s, char delimiter ) {
    std::vector<std::string> tokens;
    std::string token;
    std::istringstream tokenStream( s );
    while( std::getline( tokenStream, token, delimiter ) ) {
        tokens.push_back( token );
    }

    return tokens;
}

// NOTE: This function is not being used however
// it shows how to read a single line from the beginning of the file.
std::string getLineFromFile( const char* filename ) {
    std::ifstream file( filename );
    if( !file ) {
        std::stringstream stream;
        stream << "failed to open file " << filename << '\n';
        throw std::runtime_error( stream.str() );
    }

    std::string line;
    std::getline( file, line );

    file.close();

    return line;
}

void getDataFromFile( const char* filename, std::vector<std::string>& output ) {
    std::ifstream file( filename );
    if( !file ) {
        std::stringstream stream;
        stream << "failed to open file " << filename << '\n';
        throw std::runtime_error( stream.str() );
    }

    std::string line;

    while( std::getline( file, line ) ) {
        if ( line.size() > 0 ) 
            lines.push_back( line );
    }
    file.close();    
}

Vector parseDataAsVector( std::string& line ) {
    std::vector<std::string> tokens = splitString( line, ',' ); // Parse Line    
    return Vector( std::stoi( tokens[0] ), std::stoi( tokens[1] ), std::stoi( tokens[2] ) );
}

void generateVectors( std::vector <std::string>& lines, std::vector<Vector>& vectors ) {
    for( auto& l : lines ) {
        vectors.push_back( parseDataAsVector( l ) );
    }
    return vectors;
}

int main() {
    try {
        std::vector<std::string> fileConents; 
        getDataFromFile( "test.txt", fileContents );
        std::vector<Vector> data;
        generateVectors( fileConents, data );

        // test to see if info is correct
        for( auto& d : data ) {
            std::cout << d.x << " " << d.y << " " << d.z << '\n';
        }

    } catch( const std::runtime_error& e ) {
        std::cerr << e.what() << '\n';
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

控制台输出

1,1,1
1,2,3
3,1,3

这使代码更易于调试,使其可读性,可移植性和可靠性以及在某种程度上甚至在一定程度上是通用的。上面的程序要注意一件事;它与解析功能的实现有关,该解析功能与用户定义的数据类型Vector有关。您可以在同一行上有一个文本文件,该文件具有3个以上的值,后跟逗号,并且该文本文件仍会基于从该行读取的前3个值生成一个向量,其余的将被忽略。字符串被分割并存储之后,数据就存在于字符串向量中,但是当我们使用其构造函数创建Vector时,我们仅引用该std::vector<std::string>的前三个位置来创建和将其返回给调用者。另外,如果您的行多于三行,则只要数据适合我们解析的数据结构的当前格式,它就会执行与文件中一样多的行。