我有一个函数可以从文件中获取一些信息并将其放在一个结构中。我设置了一个循环(为和而两者),条件是它一直持续到达文件末尾。 但它不起作用。
void FileToStructProfessors()
{
int n;
fstream CurrentFile("professor.txt");
if (!CurrentFile)
{
cout <<"can't open file";
}
else
{
CurrentFile.seekp(5);
/*for(n=0;CurrentFile.eof();n++)
{
CurrentFile>>P1[n].FirstName;
CurrentFile>>P1[n].LastName;
CurrentFile>>P1[n].PID;
CurrentFile>>P1[n].Major;
CurrentFile>>P1[n].Level;
CurrentFile.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
cout<<P1[0].FirstName<<endl;
}*/
n=0;
while(!CurrentFile.eof())
{
CurrentFile>>P1[n].FirstName;
CurrentFile>>P1[n].LastName;
CurrentFile>>P1[n].PID;
CurrentFile>>P1[n].Major;
CurrentFile>>P1[n].Level;
CurrentFile>>P1[n].Date;
CurrentFile.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
cout<<P1[3].FirstName<<endl;
n++;
}
}
CurrentFile.close();
}
professor.txt
4
hamed
hamidi
1001
civil
associate professor
91/02/15
morteza
jafari
1005
computer
assistant professor
91/01/20
mahdi
foladi
1006
computer
professor
90/10/10
mostafa
mohammadi
1009
electronic
assistant professor
91/05/5
教授结构的
struct Professors
{
string FirstName,LastName;
long long int PID;
string Major,Level,Date;
}P1[100];
答案 0 :(得分:3)
您的问题是您正在阅读尝试阅读行,但CurrentFile >>
忽略所有空格,包括空格。所以,当你读到第一位教授时:
med
hamidi
1001
civil
associate professor
91/02/15
你会得到:
FirstName = med
LastName = hamidi
PID = 1001
Major = civil
Level = associate
Date = professor
“关联”和“教授”之间有一个空格,因此CurrentFile >> P1[n].Level
只会选择“关联”,因为CurrentFile >>
会看到空格并停止。然后你的下一个CurrentFile >>
选择“教授”而不是你想要阅读的日期。将此复合到整个文件中,您将获得大量无意义的输入。
您可以使用getline(CurrentFile, *string you want to read in*)
解决此问题,如下例所示。 请注意,这不是生产质量的代码,只是一个简单的玩具示例,说明getline
旨在让您继续推进项目。
void FileToStructProfessors()
{
int n;
fstream CurrentFile("professor.txt");
if (CurrentFile.fail())
{
cout <<"can't open file";
// Put a return here so you don't
// try to `.close()` the file later.
return;
}
else
{
while(CurrentFile.good())
{
getline(CurrentFile, P1[n].FirstName);
getline(CurrentFile, P1[n].LastName);
// PID is an integer, and getline doesn't work with integers,
// only strings. You can try simply 'CurrentFile >> P1[n].PID;',
// but this will mess up the next getline (try it yourself if you
// want). We can get around this using a stringstream, so put
// '#include <sstream>' at the top of your file.
string s;
getline(CurrentFile, s);
istringstream iss(s);
iss >> P1[n].PID;
getline(CurrentFile, P1[n].Major);
getline(CurrentFile, P1[n].Level);
getline(CurrentFile, P1[n].Date);
// This is to read past the blank line that
// separates your entries.
getline(CurrentFile, s);
// Print stuff.
n++;
}
}
CurrentFile.close();
}
顺便说一下,我删除了
CurrentFile.seekp(5)
。CurrentFile.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
注意强>
我的示例不安全/正确,因为这些getline
调用或CurrentFile >>
语句中的任何一个都可能遇到错误,并且都没有检查错误。我写这个是一个相对简单易懂的解释,为什么你的代码有问题。对于安全/正确的代码,请查看0x499602D2's answer。
答案 1 :(得分:2)
执行输入的条件不应以获取文件末尾为基础。仅在输入失败之前执行输入是执行提取的常规条件。否则会导致不必要的行为。
因此,您应检查流是否未设置std::ios_base::failbit
或std::ios_base::badbit
。检查这两个位是必要的,因为failbit
表示解析流无法成功提取的位置时出错,而badbit
表示外部设备出现问题(可能是不可恢复的错误)。两者都导致无法提取。但是这一切都需要在尝试输入之后完成。
以上是使用您的代码表示的上述任务:
while ( CurrentFile >> P1[n].FirstName && CurrentFile >> P1[n].LastName && CurrentFile >> P1[n].PID && CurrentFile >> P1[n].Major && std::getline(CurrentFile >> std::ws, P1[n].Level) && std::getline(CurrentFile >> std::ws, P1[n].Date) )
注意我如何使用std::getline()
更改了最后两次提取。这是因为文件中的相应数据包含格式化运算符用作分隔符的空格。日期使用斜杠,当使用格式化输入时,斜杠也会分隔。在此之后,Level
和Date
数据成员必须为std::string
,您需要使用无格式输入。
如果任何一个提取失败,则流将设置适当的位。然后,流将使用operator bool()
(或operator void*()
pre-C ++ 11隐式转换为布尔值,然后将其升级为布尔值)。布尔操作将使用!this->fail()
检查流状态(检查badbit
和failbit
),如果函数返回true,则整个while
循环表达式为false,不会进行剩余的提取(因为短路评估)。
如果流仍处于良好状态(!this->fail()
返回true
),则将执行循环体。如果流在执行提取时遇到EOF字符,则将设置eofbit
,但循环不会停止,直到稍后尝试的输入操作无法成功提取。这是理想的行为。
答案 2 :(得分:0)
这是你问题的症结所在。您不了解如何执行流输入。我不知道是谁教过你while (!file.eof())
,但几乎总是走错路。
你的节目很好,但并不完美。如果您想知道如何改进您的课程,请继续阅读,否则我希望我的其他答案有所帮助。
您没有向我们展示相关代码,但我假设P1
是一个默认大小的静态数组(大小为4)。由于索引n
随文件内容的增加而增加,因此您的代码可能存在危险。如果要将多于4个“组”数据提取到数组中,则程序将调用未定义的行为。
要避免此潜在问题,您可以使用std::vector<T>
之类的动态容器。它将分配适当大小的内部数组,并在需要时动态调整大小。您可以在构造中指定大小,如下所示:
CurrentFile >> size; std::vector<Professor> P1(size);
向此容器添加数据就像P1.push_back(p)
一样简单。你不再需要一个柜台了。
CurrentFile.seekp(5)
没有意义,因为流用于输入,只会影响输出指针。如果要忽略第一个整数,可以简单地提取为虚拟变量。但由于我已经完成了上述工作,因此没有必要。如果你试图忽略文件顶部的整数之间的空格,那么第一个Professor
的名称也是如此,因为格式化的提取器会自动从输入序列中丢弃前导空格。您无需担心无意中提取的空格。
这也使得以下代码变得不必要:
CurrentFile.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
上面的代码出现在你的while循环中(我相信)试图丢弃空格和换行符,以便提取的下一个字符是下一个教授的名字。出于同样的原因,这不是必需的。
最后,为了便于提取到Project
类型的对象,建议您使用运算符重载来实现自己的提取器。它使代码的其余部分保持干净和连贯。