如何从文件中一次读取一个输入,类似于在c++中使用cin/scanf从控制台读取输入



我正在尝试从文件中逐个读取输入,而不是从标准输入中给出。

我所拥有的,目前正在工作!

for(int i = 0; i <CLASS_SIZE; i++)
{
for(int j = 0; j <10 ; j++)
{
scanf("%d", &grade);
studentsInClass[i].setGrade(j,grade);
}  
}

控制台的当前输入是:

91 92 85 58 87 75 89 97 79 65
88 72 81 94 90 61 72 75 68 77
75 49 87 79 65 64 62 51 44 70

我希望直接从文件中读取此输入!因此,我尝试将其作为文件流逐行读取,然后尝试将该行保存到字符串向量中,这样我就可以在内部循环中提取每行的单个数字。

std::ifstream inputfile("input.txt");
std::string line;
std::vector<std::string> v;
for(int i = 0; i <CLASS_SIZE; i++)
{
for(int j = 0; j <10 ; j++)
{
if(inputfile.is_open()){
while( std::getline(inputfile, line) ){
std::cout << line << 'n';
std::istringstream iss(line);
iss>>v;
for(std::vector<std::string>::iterator it = v.begin(); it != v.end(); ++it) {
studentsInClass[i].setGrade(j, std::stoi(it, nullptr, 2));
}
}
inputfile.close();
}
}
}

但出现以下错误!因为我不知道该怎么做!

error: cannot bind ‘std::basic_istream<char>’ lvalue to ‘std::basic_istream<char>&&’
iss>>v;

以及下面的错误,因为我正试图将字符串转换为整数,因为我的程序需要将其作为整数使用,而且我的语法再次出错。

error: no matching function for call to ‘stoi(std::vector<std::__cxx11::basic_string<char> >::iterator&, std::nullptr_t
, int)’
studentsInClass[i].setGrade(j, std::stoi(it, nullptr, 2));

有人能帮我解决这个问题吗?

作为第一个近似值,我会对让您满意的代码进行最微小的更改:

std::ifstream infile("filename");
for(int i = 0; i <CLASS_SIZE; i++)
{
for(int j = 0; j <10 ; j++)
{          
// scanf("%d", &grade);
infile >> grade;
studentsInClass[i].setGrade(j,grade);
}  
}

假设你知道每个学生的确切成绩,那么使用getline几乎没有什么好处。如果输入是面向行的,那么这将是有用的。例如,考虑这样一个输入文件:

91 92 85 58 87 75 89 97 79 65 88 
72 81 94 90 61 72 75 68 77
75 49 87 79 65 64 62 51 44 70

按照目前的情况,你显然仍然希望将其解读为3个学生,每个学生有10个年级,所以第一行的最后一个年级(88)属于第二个学生,而不是第一个。

如果您希望将输入数据解释为第一个学生有11个年级,第二个学生有9个年级(第三个学生有10个年级),那么使用getline并单独解析每一行是有意义的。


这涵盖了您提出的问题,但我仍然对代码不太满意。就我个人而言,我更喜欢编写一个知道如何从流中读取自己的数据的学生类:

class student { 
std::vector<int> grades;
public:
// other stuff, of course.
friend std::istream &operator>>(std::istream &is, student &s) {
int grade;
for (int i=0; i<10; i++) {
is >> grade;
s.grades.push_back(grade);
}
return is;
}
};

然后,当我们想读取多个学生的数据时,我们会这样做:

std::vector<students> the_class;
std::ifstream infile("grades.txt");
for (int i=0; i<CLASS_SIZE; i++) {
student s;
// use `operator>>` above to read all 10 grades for one student.
infile >> s;
the_class.push_back(s);
}

1)在不检查返回代码的情况下使用scanf()是一个主要问题。如果输入与您的期望不匹配,那么您将遇到未定义的行为。

2) 任何从控制台读取的代码都可以通过简单地使用输入重定向从文件中读取。(./myprog < file.txt)

3) 为什么不先从scanf()转换到std::cin,然后从控制台转换到文件输入?一步一个脚印。(我仍然认为你应该让用户来决定是从控制台输入还是从文件重定向。更灵活。)

4)scanf( "%d", &grade )的等价物是std::cin >> grade。不需要getline()/istringstream之类的东西,只需像在当前工作代码中那样一次读取一个数字。


至于您的错误:

error: cannot bind ‘std::basic_istream<char>’ lvalue to ‘std::basic_istream<char>&&’
iss>>v;

您正在尝试从流读取到向量(iss >> v)。这不是一个已定义的操作。您需要一次读取一个值。

error: no matching function for call to ‘stoi(std::vector<std::__cxx11::basic_string<char> >::iterator&, std::nullptr_t
, int)’
studentsInClass[i].setGrade(j, std::stoi(it, nullptr, 2));

您的it是一个迭代器,需要取消对它的引用以给出它所引用的值(*it)。不过,请注意我之前说过的内容——您的v没有按照您的意愿填充,请参阅前面的错误。


代替:

for(std::vector<std::string>::iterator it = v.begin(); it != v.end(); ++it)

使用类型推导(编译器不知道v.begin()的返回类型):

for ( auto it = v.begin(); it != v.end(); ++it )

更好的是,使用一个范围:

for ( auto & value : v )

for(int i = 0; i <CLASS_SIZE; i++)
{
for(int j = 0; j <10 ; j++)
{
if(inputfile.is_open()){

如果inputfile未打开,则可以跳过整个循环。因此,在循环之前测试,而不是在循环内部。

最新更新