C++ 在循环中使用 getline() 读取 CSV 文件
C++ Using getline() inside loop to read in CSV file
我正在尝试读取包含 3 行的 CSV 文件 people/patients,其中第 1 列是用户 ID,第 2 列是 fname,第 3 列是 lname,第 4 列是保险,第 5 列是看起来像下面的版本。
编辑:抱歉,我只是 copy/pasted 我的 CSV 电子表格在这里,所以它之前没有显示逗号。它不会看起来更像下面的东西吗?下面的约翰还指出版本后没有逗号,这似乎解决了这个问题!非常感谢约翰! (试图弄清楚我如何接受你的回答:))
nm92,Nate,Matthews,Aetna,1
sc91,Steve,Combs,Cigna,2
ml94,Morgan,Lands,BCBS,3
我正在尝试在循环中使用 getline() 来读取所有内容,它在第一次迭代中工作正常,但 getline() 似乎导致它在下一次迭代中跳过一个值。知道我该如何解决这个问题吗?
我也不确定为什么输出如下所示,因为我没有看到代码中打印了带 "sc91" 和 "ml94" 的行。这就是当前代码的输出结果。
userid is: nm92
fname is: Nate
lname is: Matthews
insurance is: Aetna
version is: 1
sc91
userid is: Steve
fname is: Combs
lname is: Cigna
insurance is: 2
ml94
version is: Morgan
userid is: Lands
fname is: BCBS
lname is: 3
insurance is:
version is:
我对 getline() 和 >> 流运算符之间的差异进行了大量研究,但大多数 getline() 材料似乎都围绕着从 cin 获取输入而不是像这里这样从文件中读取,所以我认为 getline() 正在发生某些事情,以及它如何读取我不理解的文件。不幸的是,当我尝试 >> 运算符时,这迫使我使用 strtok() 函数,并且我在处理 C 字符串并将它们分配给 C++ 字符串数组时遇到了很多困难。
#include <iostream>
#include <string> // for strings
#include <cstring> // for strtok()
#include <fstream> // for file streams
using namespace std;
struct enrollee
{
string userid = "";
string fname = "";
string lname = "";
string insurance = "";
string version = "";
};
int main()
{
const int ENROLL_SIZE = 1000; // used const instead of #define since the performance diff is negligible,
const int numCols = 5; // while const allows for greater utility/debugging bc it is known to the compiler ,
// while #define is a preprocessor directive
ifstream inputFile; // create input file stream for reading only
struct enrollee enrollArray[ENROLL_SIZE]; // array of structs to store each enrollee and their respective data
int arrayPos = 0;
// open the input file to read
inputFile.open("input.csv");
// read the file until we reach the end
while(!inputFile.eof())
{
//string inputBuffer; // buffer to store input, which will hold an entire excel row w/ cells delimited by commas
// must be a c string since strtok() only takes c string as input
string tokensArray[numCols];
string userid = "";
string fname = "";
string lname = "";
string insurance = "";
string sversion = "";
//int version = -1;
//getline(inputFile,inputBuffer,',');
//cout << inputBuffer << endl;
getline(inputFile,userid,',');
getline(inputFile,fname,',');
getline(inputFile,lname,',');
getline(inputFile,insurance,',');
getline(inputFile,sversion,',');
enrollArray[0].userid = userid;
enrollArray[0].fname = fname;
enrollArray[0].lname = lname;
enrollArray[0].insurance = insurance;
enrollArray[0].version = sversion;
cout << "userid is: " << enrollArray[0].userid << endl;
cout << "fname is: " << enrollArray[0].fname << endl;
cout << "lname is: " << enrollArray[0].lname << endl;
cout << "insurance is: " << enrollArray[0].insurance << endl;
cout << "version is: " << enrollArray[0].version << endl;
}
}
这只是一个想法,但它可以帮助你。这是我正在从事的一个项目的一段代码:
std::vector<std::string> ARDatabase::split(const std::string& line, char delimiter)
{
std::vector<std::string> tokens;
std::string token;
std::istringstream tokenStream(line);
while (std::getline(tokenStream, token, delimiter))
{
tokens.push_back(token);
}
return tokens;
}
void ARDatabase::read_csv_map(std::string root_csv_map)
{
qDebug() << "Starting to read the people database...";
std::ifstream file(root_csv_map);
std::string str;
while (std::getline(file, str))
{
std::vector<std::string> tokens = split(str, ' ');
std::vector<std::string> splitnames = split(tokens.at(1), '_');
std::string name_w_spaces;
for(auto i: splitnames) name_w_spaces = name_w_spaces + i + " ";
people_names.insert(std::make_pair(stoi(tokens.at(0)), name_w_spaces));
people_images.insert(std::make_pair(stoi(tokens.at(0)), std::string("database/images/" + tokens.at(2))));
}
}
而不是 std::vector,您可能想要使用其他更适合您情况的容器。最后一个示例是针对我的案例的输入格式制作的。您可以轻松修改它以使其适应您的代码。
你的问题是每行最后一个数据项后面没有逗号,所以
getline(inputFile,sversion,',');
是不正确的,因为它读取到下一个逗号,实际上是在下一位患者的用户 ID 之后的下一行。这解释了您看到的输出,其中下一个专利的用户 ID 与版本一起输出。
要解决此问题,只需将上面的代码替换为
getline(inputFile,sversion);
这将根据需要读到行尾。
关于你的功能。如果查看源文件的结构,您会发现它包含 5 个字符串,以“,”分隔。所以一个典型的 CSV 文件。
调用 std::getline
将读取包含 5 个字符串的完整行。在您的代码中,您试图为每个字符串调用 std::getline
,后跟一个逗号。最后一个字符串后不存在逗号。这是行不通的。您还应该使用 getline 来获取完整的行。
您需要阅读整行然后对其进行标记化。
我将向您展示如何使用 std::sregex_token_iterator
执行此操作的示例。那很简单。此外,我们将覆盖插入器和提取器操作符。有了它,您可以轻松读写 "enrollee" 数据,例如 Enrollee e{}; std::cout << e;
此外,我还使用 C++ 算法。这让生活变得非常轻松。 Input 和 Output 是一个 one-liner in main.
请看:
#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
#include <iterator>
#include <regex>
struct Enrollee
{
// Data
std::string userid{};
std::string fname{};
std::string lname{};
std::string insurance{};
std::string version{};
// Overload Extractor Operator to read data from somewhere
friend std::istream& operator >> (std::istream &is, Enrollee& e) {
std::vector<std::string> wordsInLine{}; // Here we will store all words that we read in onle line;
std::string wholeLine; // Temporary storage for the complete line that we will get by getline
std::regex separator("[ \;\,]"); ; // Separator for a CSV file
std::getline(is, wholeLine); // Read one complete line and split it into parts
std::copy(std::sregex_token_iterator(wholeLine.begin(), wholeLine.end(), separator, -1), std::sregex_token_iterator(), std::back_inserter(wordsInLine));
// If we have read all expted strings, then store them in our struct
if (wordsInLine.size() == 5) {
e.userid = wordsInLine[0];
e.fname = wordsInLine[1];
e.lname = wordsInLine[2];
e.insurance = wordsInLine[3];
e.version = wordsInLine[4];
}
return is;
}
// Overload Inserter operator. Insert data into output stream
friend std::ostream& operator << (std::ostream& os, const Enrollee& e) {
return os << "userid is: " << e.userid << "\nfname is: " << e.fname << "\nlname is: " << e.lname << "\ninsurance is: " << e.insurance << "\nversion is: " << e.version << '\n';
}
};
int main()
{
// Her we will store all Enrollee data in a dynamic growing vector
std::vector<Enrollee> enrollmentData{};
// Define inputFileStream and open the csv
std::ifstream inputFileStream("r:\input.csv");
// If we could open the file
if (inputFileStream) {
// Then read all csv data
std::copy(std::istream_iterator<Enrollee>(inputFileStream), std::istream_iterator<Enrollee>(), std::back_inserter(enrollmentData));
// For Debug Purposes: Print all data to cout
std::copy(enrollmentData.begin(), enrollmentData.end(), std::ostream_iterator<Enrollee>(std::cout, "\n"));
}
else {
std::cerr << "Could not open file 'input.csv'\n";
}
}
这将读取包含
的输入文件"input.csv"
nm92,Nate,Matthews,Aetna,1
sc91,Steve,Combs,Cigna,2
ml94,Morgan,Lands,BCBS,3
并显示为输出:
userid is: nm92
fname is: Nate
lname is: Matthews
insurance is: Aetna
version is: 1
userid is: sc91
fname is: Steve
lname is: Combs
insurance is: Cigna
version is: 2
userid is: ml94
fname is: Morgan
lname is: Lands
insurance is: BCBS
version is: 3
我正在尝试读取包含 3 行的 CSV 文件 people/patients,其中第 1 列是用户 ID,第 2 列是 fname,第 3 列是 lname,第 4 列是保险,第 5 列是看起来像下面的版本。
编辑:抱歉,我只是 copy/pasted 我的 CSV 电子表格在这里,所以它之前没有显示逗号。它不会看起来更像下面的东西吗?下面的约翰还指出版本后没有逗号,这似乎解决了这个问题!非常感谢约翰! (试图弄清楚我如何接受你的回答:))
nm92,Nate,Matthews,Aetna,1
sc91,Steve,Combs,Cigna,2
ml94,Morgan,Lands,BCBS,3
我正在尝试在循环中使用 getline() 来读取所有内容,它在第一次迭代中工作正常,但 getline() 似乎导致它在下一次迭代中跳过一个值。知道我该如何解决这个问题吗?
我也不确定为什么输出如下所示,因为我没有看到代码中打印了带 "sc91" 和 "ml94" 的行。这就是当前代码的输出结果。
userid is: nm92
fname is: Nate
lname is: Matthews
insurance is: Aetna
version is: 1
sc91
userid is: Steve
fname is: Combs
lname is: Cigna
insurance is: 2
ml94
version is: Morgan
userid is: Lands
fname is: BCBS
lname is: 3
insurance is:
version is:
我对 getline() 和 >> 流运算符之间的差异进行了大量研究,但大多数 getline() 材料似乎都围绕着从 cin 获取输入而不是像这里这样从文件中读取,所以我认为 getline() 正在发生某些事情,以及它如何读取我不理解的文件。不幸的是,当我尝试 >> 运算符时,这迫使我使用 strtok() 函数,并且我在处理 C 字符串并将它们分配给 C++ 字符串数组时遇到了很多困难。
#include <iostream>
#include <string> // for strings
#include <cstring> // for strtok()
#include <fstream> // for file streams
using namespace std;
struct enrollee
{
string userid = "";
string fname = "";
string lname = "";
string insurance = "";
string version = "";
};
int main()
{
const int ENROLL_SIZE = 1000; // used const instead of #define since the performance diff is negligible,
const int numCols = 5; // while const allows for greater utility/debugging bc it is known to the compiler ,
// while #define is a preprocessor directive
ifstream inputFile; // create input file stream for reading only
struct enrollee enrollArray[ENROLL_SIZE]; // array of structs to store each enrollee and their respective data
int arrayPos = 0;
// open the input file to read
inputFile.open("input.csv");
// read the file until we reach the end
while(!inputFile.eof())
{
//string inputBuffer; // buffer to store input, which will hold an entire excel row w/ cells delimited by commas
// must be a c string since strtok() only takes c string as input
string tokensArray[numCols];
string userid = "";
string fname = "";
string lname = "";
string insurance = "";
string sversion = "";
//int version = -1;
//getline(inputFile,inputBuffer,',');
//cout << inputBuffer << endl;
getline(inputFile,userid,',');
getline(inputFile,fname,',');
getline(inputFile,lname,',');
getline(inputFile,insurance,',');
getline(inputFile,sversion,',');
enrollArray[0].userid = userid;
enrollArray[0].fname = fname;
enrollArray[0].lname = lname;
enrollArray[0].insurance = insurance;
enrollArray[0].version = sversion;
cout << "userid is: " << enrollArray[0].userid << endl;
cout << "fname is: " << enrollArray[0].fname << endl;
cout << "lname is: " << enrollArray[0].lname << endl;
cout << "insurance is: " << enrollArray[0].insurance << endl;
cout << "version is: " << enrollArray[0].version << endl;
}
}
这只是一个想法,但它可以帮助你。这是我正在从事的一个项目的一段代码:
std::vector<std::string> ARDatabase::split(const std::string& line, char delimiter)
{
std::vector<std::string> tokens;
std::string token;
std::istringstream tokenStream(line);
while (std::getline(tokenStream, token, delimiter))
{
tokens.push_back(token);
}
return tokens;
}
void ARDatabase::read_csv_map(std::string root_csv_map)
{
qDebug() << "Starting to read the people database...";
std::ifstream file(root_csv_map);
std::string str;
while (std::getline(file, str))
{
std::vector<std::string> tokens = split(str, ' ');
std::vector<std::string> splitnames = split(tokens.at(1), '_');
std::string name_w_spaces;
for(auto i: splitnames) name_w_spaces = name_w_spaces + i + " ";
people_names.insert(std::make_pair(stoi(tokens.at(0)), name_w_spaces));
people_images.insert(std::make_pair(stoi(tokens.at(0)), std::string("database/images/" + tokens.at(2))));
}
}
而不是 std::vector,您可能想要使用其他更适合您情况的容器。最后一个示例是针对我的案例的输入格式制作的。您可以轻松修改它以使其适应您的代码。
你的问题是每行最后一个数据项后面没有逗号,所以
getline(inputFile,sversion,',');
是不正确的,因为它读取到下一个逗号,实际上是在下一位患者的用户 ID 之后的下一行。这解释了您看到的输出,其中下一个专利的用户 ID 与版本一起输出。
要解决此问题,只需将上面的代码替换为
getline(inputFile,sversion);
这将根据需要读到行尾。
关于你的功能。如果查看源文件的结构,您会发现它包含 5 个字符串,以“,”分隔。所以一个典型的 CSV 文件。
调用 std::getline
将读取包含 5 个字符串的完整行。在您的代码中,您试图为每个字符串调用 std::getline
,后跟一个逗号。最后一个字符串后不存在逗号。这是行不通的。您还应该使用 getline 来获取完整的行。
您需要阅读整行然后对其进行标记化。
我将向您展示如何使用 std::sregex_token_iterator
执行此操作的示例。那很简单。此外,我们将覆盖插入器和提取器操作符。有了它,您可以轻松读写 "enrollee" 数据,例如 Enrollee e{}; std::cout << e;
此外,我还使用 C++ 算法。这让生活变得非常轻松。 Input 和 Output 是一个 one-liner in main.
请看:
#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
#include <iterator>
#include <regex>
struct Enrollee
{
// Data
std::string userid{};
std::string fname{};
std::string lname{};
std::string insurance{};
std::string version{};
// Overload Extractor Operator to read data from somewhere
friend std::istream& operator >> (std::istream &is, Enrollee& e) {
std::vector<std::string> wordsInLine{}; // Here we will store all words that we read in onle line;
std::string wholeLine; // Temporary storage for the complete line that we will get by getline
std::regex separator("[ \;\,]"); ; // Separator for a CSV file
std::getline(is, wholeLine); // Read one complete line and split it into parts
std::copy(std::sregex_token_iterator(wholeLine.begin(), wholeLine.end(), separator, -1), std::sregex_token_iterator(), std::back_inserter(wordsInLine));
// If we have read all expted strings, then store them in our struct
if (wordsInLine.size() == 5) {
e.userid = wordsInLine[0];
e.fname = wordsInLine[1];
e.lname = wordsInLine[2];
e.insurance = wordsInLine[3];
e.version = wordsInLine[4];
}
return is;
}
// Overload Inserter operator. Insert data into output stream
friend std::ostream& operator << (std::ostream& os, const Enrollee& e) {
return os << "userid is: " << e.userid << "\nfname is: " << e.fname << "\nlname is: " << e.lname << "\ninsurance is: " << e.insurance << "\nversion is: " << e.version << '\n';
}
};
int main()
{
// Her we will store all Enrollee data in a dynamic growing vector
std::vector<Enrollee> enrollmentData{};
// Define inputFileStream and open the csv
std::ifstream inputFileStream("r:\input.csv");
// If we could open the file
if (inputFileStream) {
// Then read all csv data
std::copy(std::istream_iterator<Enrollee>(inputFileStream), std::istream_iterator<Enrollee>(), std::back_inserter(enrollmentData));
// For Debug Purposes: Print all data to cout
std::copy(enrollmentData.begin(), enrollmentData.end(), std::ostream_iterator<Enrollee>(std::cout, "\n"));
}
else {
std::cerr << "Could not open file 'input.csv'\n";
}
}
这将读取包含
的输入文件"input.csv"nm92,Nate,Matthews,Aetna,1
sc91,Steve,Combs,Cigna,2
ml94,Morgan,Lands,BCBS,3
并显示为输出:
userid is: nm92
fname is: Nate
lname is: Matthews
insurance is: Aetna
version is: 1
userid is: sc91
fname is: Steve
lname is: Combs
insurance is: Cigna
version is: 2
userid is: ml94
fname is: Morgan
lname is: Lands
insurance is: BCBS
version is: 3