我正在尝试使用get行读取csv以提取由逗号分隔的三个变量。姓名,课程和成绩。
我正在第一行阅读,但是它会产生奇怪的新换行符并将格式发送到群集中。
这是我的代码:
#include "header.h"
string student::GetCourse() {
return course;
}
string student::GetName() {
return name;
}
string student::GetGrade() {
return grade;
}
void student::setname(string n) {
name = n;
}
void student::setCourse(string c) {
course = c;
}
void student::setGrade(string g) {
grade = g;
}
void sort (vector <student> &List) {
student temp;
int first = 1;
int vectorLength = List.size() - 1;
for (int i = vectorLength; i > 0; i--) {
first = i;
for (int j = 0; j < i; j++) {
if (List[j].GetName() > List[first].GetName())
first = j;
}
temp = List[first];
List[first] = List[i];
List[i] = temp;
}
}
void main () {
ifstream file;
vector <student> StudentList;
file.open("short.txt");
while (!file.eof()) {
file.ignore(8196,'\n');
string tempname, tempgrade, tempcourse = "";
if (file != "\n") {
getline(file, tempname, ',');
getline(file, tempcourse, ',');
getline(file, tempgrade, ',');
}
student s;
s.setCourse(tempcourse);
s.setname (tempname);
s.setGrade (tempgrade);
StudentList.push_back(s);
}
//sort (StudentList);
for (int i = 0; i < StudentList.size(); i++) {
cout << StudentList[i].GetName() << " " << StudentList[i].GetCourse() << " " << StudentList[i].GetGrade() << endl;
}
}
任何想法,我都在阅读这个文件真的很难。
答案 0 :(得分:7)
嗯,这里是
if (file != "\n")
比较是荒谬的。它没有按照你的想法做到。','
,而是'\n'
。while (!file.eof())
不正确。只有在EOF发生后才检查它。您应该检查getline()
的返回值另外
std::ifstream file("short.txt");
。您无需单独拨打open()
。您无需将std::string
初始化为“”。这会自动发生。即使你必须这样做,那么你应该写
std::string a = "", b = "", c = "";
。
如果你std::string a, b, c = "something"
,那么只有c被初始化为某种东西。
答案 1 :(得分:4)
一些意见:
STL有自己的内置排序算法 您所要做的就是指定对象之间的关系:
bool operator<(student const& lhs,student const& rhs)
{
return lhs.GetName() < rhs.GetName();
}
// Now a sort is:
std::sort(list.begin(),list.end());
这是用于读取文件的标准反模式 问题是测试要么太早还是两晚。如果你还没有读过任何东西那么早就没发生任何事了。如果你已经阅读了一些东西,那么你已经对你读过的项目进行了处理(但是失败了)已经太晚了。
最好的方法是将读取放入while循环中。这是因为read的结果返回对流的引用。这可以自动转换为可以在布尔上下文中使用的对象(转换测试以查看流是否有问题)。因此,读取失败会使流处于一种状态,导致它在布尔上下文中转换为等效的false。
std::string line;
while(std::getline(file,line))
{
// loop only entered if getline() worked.
// Thus we know that we have a good value in line.
// use line
}
你是真的忽略了8000个字符还是只是想放弃一条线?
file.ignore(8196,'\n');
您有两种选择:
std::string ignoreLine;
std::getline(file,ignoreLine);
// Dont use a magic number but use a number that is guranteed not to fail.
file.ignore(std::numeric_limits<std::streamsize>::max(), '\n')
编程的主要内容是编写可维护的代码 使用这种初始化(相对普遍)被视为懒惰。将每个声明放在一个单独的行上。它使代码更容易阅读。
string tempname, tempgrade, tempcourse = "";
// Like this:
std::string tempname;
std::string tempgrade;
std::string tempcourse;
我不确定你在这里尝试了什么?
if (file != "\n")
{ getline(file, tempname, ',');
getline(file, tempcourse, ',');
getline(file, tempgrade, ',');
}
我认为如果我们将它与上面的循环结合起来会更容易阅读:
std::string line;
while(std::getline(file,line))
{
std::stringstream linestr(line);
if (getline(linestr, tempname, ',') &&
getline(linestr, tempcourse, ',') &&
getline(linestr, tempgrade, ',')
)
{
// Here we have read a line.
// And successfully retrieved three comma separated values from the line
}
}
此打印循环可以替换为std :: copy()
for (int i = 0; i < StudentList.size(); i++)
{ cout << StudentList[i].GetName() << " "
<< StudentList[i].GetCourse() << " "
<< StudentList[i].GetGrade() << endl;
}
您需要做的就是为您的班级定义一个输出运算符。
std::ostream& operator<<(std::ostream& str,student const& data)
{
str << data.getName() << " "
<< data.getCourse() << " "
<< data.getGrade() << " "; // No newline here.
return str;
}
现在我们可以将矢量复制到std :: cout
std::copy(StudentList.begin(),StudentList.end(),
std::ostream_iterator<student>(std::cout,"\n")
);
我看到的主要错误是这一行:
if (file != "\n")
这里你将文件与'C-string'进行比较。编译器如何设法编译这个我不确定 我想到了几个选项,但它不明显的事实使它成为错误的可能来源。另请注意,这不是您比较两个字符串的方式(除非一个是std :: string)。
我认为编译器会将文件转换为指针并将其与“C-String”进行比较(因为这也只是一个指针)。您可能认为这有点奇怪,但有一个运算符会将文件转换为void *。指针不指向任何有意义的但是NULL或非NULL并且可以与char *指针进行比较,从而产生true(因为它永远不等于字符串“\ n”)。
答案 2 :(得分:2)
首先:您没有检查输入是否在任何地方成功。哎呀,你甚至不检查文件是否可以打开:
int main () { // it's int main()!
ifstream file("short.txt");
if(!file.good()) {
std::cerr << "couldn't open \"short.txt\"\n";
return 1;
}
vector <student> StudentList;
for(;;) {
// ...
}
if( !file.eof() ) {
std::cerr << "error reading before eof!\n";
return 2;
}
// ...
}
然后:通常在该循环中首先读取行更容易:
for(;;) {
std::string line;
std::getline(file, line);
if(!file) break;
// ...
}
然后通过字符串流从这些行读取。我会将代码读入行放入自己的函数中:
std::istream& read_line(std::istream& is, StudentList& list)
{
std::string value1, value2, value3;
std::getline(is, value1, ',');
std::getline(is, value2, ',');
std::getline(is, value3, ',');
if(is)
StudentList.push_back(...);
}
// ...
for(;;) {
std::string line;
std::getline(file, line);
if(!file) break;
std::istringstream iss(line);
read_line(iss, StudentList);
if(!iss) break;
}
// ...
HTH。
答案 3 :(得分:1)
你已经得到了很多答案。虽然他们的建议肯定会改善你现在所做的事情,但我的处理方式与他们的建议有所不同。
现在你的student
类基本上尽力模仿“哑数据”(即只是一个简单的结构)但是语法更加丑陋 - 你为每个成员使用了一个get / set对,但他们没有添加任何东西。 student
类本身就像“哑”一样,就像它只是一个简单的结构一样。 student
的所有逻辑仍然在student
类之外。
为了使其有用,student
类应包含相当多的逻辑,例如如何从流中读取student
或显示student
不同的流:
class student {
std::string name, course, grade;
public:
bool operator<(student const &other) const {
return name < other.name;
}
friend std::ostream &operator<<(std::ostream &os, student const &st) {
return os << st.name << " " << st.course << " " << st.grade;
}
friend std::istream &operator>>(std::istream &is, student &st) {
std::string temp;
is >> temp;
std::istringstream t(temp);
std::getline(t, st.name, ',');
std::getline(t, st.course, ',');
std::getline(t, st.grade);
return is;
}
};
这使得主要更简单:
int main() {
std::ifstream in("short.txt");
std::vector<student> students;
std::copy(std::istream_iterator<student>(in),
std::istream_itertor<student>(),
std::back_inserter(students));
std::sort(students.begin(), students.end());
std::copy(students.begin(), students.end(),
std::ostream_iterator<student>(std::cout, "\n"));
return 0;
}
请注意,特别是,主要只处理“整个”student
作为逻辑实体 - 它永远一旦看起来在student
对象的“内部”在其组成部分。
答案 4 :(得分:0)
通过在调用
中将分隔符设置为','getline(file, tempname, ',');
你不是一次读一整行。 '\ n'是默认分隔符,使用默认值,您将获得整行而不仅仅是它的一部分。
我建议使用默认分隔符读取整行,然后使用','作为分隔符将行拆分为标记并使用if(!file.eof)
确定何时完成阅读文件。