我试图读取一个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
我正在尝试查看循环是否出错,但对我来说似乎不错。
答案 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)
我已经回答了一组类似的问题here和here。正如我之前所说;解析简单文件时,通常更容易先获取内容。然后,一旦从文件中获取了所有需要的信息,就通常会将内容存储到字符串,某些流或缓冲区中。存储完文件后,您将需要在完成操作后将其关闭。然后,在关闭文件句柄之后,这就是您要解析该信息并将其转换为所需的数据类型的时候。使用这种方法,可以更轻松地创建一个用户定义的结构,该结构将代表您从文件中检索的数据。然后,编写函数可以帮助您完成所有这些工作。每个功能应有其自己的职责和任务。我将使用从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>
的前三个位置来创建和将其返回给调用者。另外,如果您的行多于三行,则只要数据适合我们解析的数据结构的当前格式,它就会执行与文件中一样多的行。