C++ 如何有效地使用get line()?
我想从一个文本文件中读入数据并输出到一个包含更多数据的新文件。所有的数据都被正确地传输了,但只是出了问题。当我使用C++ 如何有效地使用get line()?,c++,C++,我想从一个文本文件中读入数据并输出到一个包含更多数据的新文件。所有的数据都被正确地传输了,但只是出了问题。当我使用>运算符(提取器)时,一切正常,但逗号保留在中间。当我使用getline时,逗号消失了,但数据顺序不正确 以下是文本文件中的内容: Astronomy, 34684, MoWed, 7:15pm-9:55pm, JC16 ComputerScience, 36822, MoWed, 9:00am-10:40am, E137 Calculus, 32700, MoTuTh, 11:00
>
运算符(提取器)时,一切正常,但逗号保留在中间。当我使用getline
时,逗号消失了,但数据顺序不正确
以下是文本文件中的内容:
Astronomy, 34684, MoWed, 7:15pm-9:55pm, JC16
ComputerScience, 36822, MoWed, 9:00am-10:40am, E137
Calculus, 32700, MoTuTh, 11:00am-12:15am, CW134
ComputerOrganization, 45665, Th, 7:20pm-9:55pm, E137
下面是我在使用getline时得到的信息
Class: Astronomy
-Class ID: 34684
-Meeting Days: MoWed
-Class Time: 7:15pm-9:55pm
-Class Location: JC16
ComputerScience
Class: 36822
-Class ID: MoWed
-Meeting Days: 9:00am-10:40am
-Class Time: E137
Calculus
-Class Location: 32700
Class: MoTuTh
-Class ID: 11:00am-12:15am
-Meeting Days: CW134
ComputerOrganization
-Class Time: 45665
-Class Location: Th
这是我使用提取器时得到的结果
Class: Astronomy,
-Class ID: 34684,
-Meeting Days: MoWed,
-Class Time: 7:15pm-9:55pm,
-Class Location: JC16
Class: ComputerScience,
-Class ID: 36822,
-Meeting Days: MoWed,
-Class Time: 9:00am-10:40am,
-Class Location: E137
Class: Calculus,
-Class ID: 32700,
-Meeting Days: MoTuTh,
-Class Time: 11:00am-12:15am,
-Class Location: CW134
Class: ComputerOrganization,
-Class ID: 45665,
-Meeting Days: Th,
-Class Time: 7:20pm-9:55pm,
-Class Location: E137
有什么建议吗?
这是密码
#include <iostream>
#include <fstream>
#include <string>
#include <iomanip>
using namespace std;
//Holds all the class information
struct Course{
string courseName;
string courseNum;
string courseDay;
string courseTime;
string courseLoc;
};
//Extracts data from the file with the course information
Course getInfo(ifstream &inFile);
//Creates a file with the data from 'getInfo'
void writeInfo(ofstream &outFile, Course);
int main(){
ifstream inFile; //link to input file
ofstream outFile; //link to output file
Course course; //holds all course info
inFile.open("Courses.txt"); //opens textfile
outFile.open("Courses.dat"); //creates new file
course = getInfo(inFile); //priming read
while (inFile) {
writeInfo(outFile, course); //write info to output file
course = getInfo(inFile); //get info from input file
}
inFile.close();
outFile.close();
}
Course getInfo(ifstream &inFile){
Course course;
getline(inFile, course.courseName, ',');
getline(inFile, course.courseNum, ',');
getline(inFile, course.courseDay, ',');
getline(inFile, course.courseTime, ',');
getline(inFile, course.courseLoc, ',');
// inFile >> course.courseName >> course.courseNum >> course.courseDay;
// inFile >> course.courseTime >> course.courseLoc;
return course;
}
void writeInfo(ofstream &outFile, Course course){
outFile << "Class: " << course.courseName << endl;
outFile << " -Class ID: " << course.courseNum << endl;
outFile << " -Meeting Days: " << course.courseDay << endl;
outFile << " -Class Time: " << course.courseTime << endl;
outFile << " -Class Location: " << course.courseLoc << endl;
}
#包括
#包括
#包括
#包括
使用名称空间std;
//保存所有类信息
结构课程{
字符串courseName;
字符串枚举;
串道;
弦乐时间;
弦乐;
};
//从包含课程信息的文件中提取数据
课程获取信息(ifstream&infle);
//使用“getInfo”中的数据创建文件
无效写入信息(流和流出文件,课程);
int main(){
ifstream infle;//链接到输入文件
ofstream outFile;//链接到输出文件
课程;//保存所有课程信息
inFile.open(“Courses.txt”);//打开文本文件
outFile.open(“Courses.dat”);//创建新文件
课程=获取信息(填充);//读取
while(填充){
writeInfo(输出文件,课程);//将信息写入输出文件
course=getInfo(infle);//从输入文件获取信息
}
infle.close();
outFile.close();
}
课程获取信息(ifstream和infle){
课程;
getline(填充,course.courseName,,);
getline(infle,course.courseNum,',');
getline(infle,course.courseDay,,);
getline(infle,course.courseTime,',');
getline(infle,course.courseLoc,,);
//填充>>课程.courseName>>课程.CourseEnum>>课程.courseDay;
//infle>>course.courseTime>>course.courseLoc;
返回路线;
}
无效写入信息(流和输出文件,课程){
outFile人们在阅读时对函数有什么期望
getline
大多数人会说,嗯,我想它会从某个地方读取完整的一行。猜猜看,这是这个函数的基本意图。从流中读取一行并将其放入字符串中。正如您所看到的std::getline
具有一些附加功能
这导致了对该函数的严重误用,将std::string
s拆分为令牌
将字符串分割成令牌是一项非常古老的任务。在非常早期的C中,函数“代码> Strutok <代码>,即使在C++中也存在,<>代码> STD::Strutok < /C> > /P>
但是由于
std::getline
的附加功能,is被严重滥用于字符串的标记化。如果您查看顶部关于如何解析CSV文件的问题/答案(请参见),那么您将了解我的意思
人们使用std::getline
从原始流中读取文本行、字符串,然后将其填充到std::istringstream
中,并再次使用带分隔符的std::getline
将字符串解析为标记。奇怪
但是,多年以来,我们有一个专门的、特殊的函数来标记字符串,特别是为此而明确设计的
std::sregex\u令牌\u迭代器
既然我们有这样一个专用功能,我们就应该简单地使用它
这是一个迭代器。对于在字符串上进行迭代,因此函数名以s开头。开始部分定义了我们应该在什么输入范围内进行操作,然后在输入字符串中有一个std::regex来表示应该匹配/不应该匹配的内容。匹配策略的类型由最后一个参数给出
- 0-->提供我在正则表达式中定义的内容,并(可选)
- -1-->根据正则表达式给出不匹配的内容
我们可以使用该迭代器将令牌存储在std::vector
中。std::vector
有一个范围构造函数,它将2个迭代器作为参数,并将第一个迭代器和第二个迭代器之间的数据复制到std::vector
std::vector tokens(std::sregex_token_iterator(s.begin(), s.end(), re, -1), {});
将变量“tokens”定义为std::vector,并使用std::vector的所谓范围构造函数。请注意:我使用的是C++17,可以在不使用模板参数的情况下定义std::vector
。编译器可以从给定的函数参数推断参数。此功能称为CTAD(“类模板参数推导”)
此外,您可以看到我没有显式地使用“end()”-迭代器
此迭代器将从空大括号中包含的默认初始值设定项列表中构造,其类型正确,因为由于std::vector
构造函数要求它,它将被推断为与第一个参数的类型相同
您可以读取一行中任意数量的令牌,并将其放入std::vector
但您可以做得更多。您可以验证您的输入。如果您使用0作为最后一个参数,您可以定义一个甚至验证您的输入的std::regex
。并且您只能获得有效的令牌
此外,它还可以帮助您避免在最后一条getline语句中出现错误
总的来说,专用功能的使用优于误用的std::getline
,人们应该简单地使用它
有些人可能会抱怨函数开销,但他们中有多少人在使用大数据。即使这样,方法也可能是使用string.find
和string.substring
或std::stringview
或其他什么
那么,现在来进一步探讨话题
你不应该使用:使用名称空间std;
。你会在这里找到1000条提示,为什么不呢
<>你应该开始使用C++中的面向对象的特性。在C++中,你可以把数据和方法操作在这些数据上。
#include <iostream>
#include <string>
#include <vector>
#include <iterator>
#include <regex>
#include <fstream>
// This will validate your input
std::regex re{ "," };
struct Course {
std::string name;
std::string num;
std::string day;
std::string time;
std::string loc;
friend std::istream& operator >> (std::istream& is, Course& c) {
// Read a complete line from a stream and check, if that worked
if (std::string s{}; std::getline(is, s)) {
// Split it into tokens and validate the input
std::vector token(std::sregex_token_iterator(s.begin(), s.end(), re,-1), {});
// Sanity check: We should have 5 entries
if (5 == token.size()) {
// Then copy the tokens to our internal data members
c.name = token[0]; c.num = token[1]; c.day = token[2]; c.time = token[3]; c.loc = token[4];
}
}
return is;
}
friend std::ostream& operator << (std::ostream& os, const Course& c) {
return os << "\nClass: " << c.name << "\n -Class ID: " << c.num << "\n -Meeting Days: " << c.day
<< "\n -Class Time: " << c.time << "\n -Class Location: " << c.loc;
}
};
int main() {
// Open input file and check, if it is open
if (std::ifstream inFile("r:\\Courses.txt"); inFile) {
// Open the output file
if (std::ofstream outFile("r:\\Courses.dat"); outFile) {
// Read all lines with course data, split them and put them into the below vector
std::vector courses(std::istream_iterator<Course>(inFile), {});
// Now that we have read the complete input data, we sho the result to the user
for (const Course& c : courses) outFile << c << "\n";
}
else {
std::cerr << "\n*** Error: Could not open output file 'Courses.dat'\n";
}
}
else {
std::cerr << "\n*** Error: Could not open input file 'Courses.txt'\n";
}
return 0;
}