ОК, даже не видя ваш полный исходный код, я вам помогу.
У вас 2 основные проблемы
- Вы не проверяете, выполняется ли операция извлечения (>> ) не удалось или нет
- У вас есть проблема с символом конечной линии после извлечения без
std::getline
Итак, что произойдет. Предположим, что следующая простая тестовая программа
#include <iostream>
#include <sstream>
std::istringstream testDataStream{ R"(1 2
string1
3 4
string2)" };
int main() {
int i1{}, i2{}, i3{}, i4{};
std::string s1{}, s2{};
testDataStream >> i1 >> i2;
std::getline(testDataStream, s1);
testDataStream >> i3 >> i4;
std::getline(testDataStream, s2);
// more code . . .
return 0;
}
Сначала мы прочитаем i1 и i2 с правильной информацией. Оператор экстрактора читает до следующего пробела, но не использует '\ n' в конце строки. Он прочитал i1 и i2, пропуская пробел. Тогда остановись. Нет больше активности. Ничто не будет использовать '\ n'.
Если вы сейчас позвоните std::getline
, оно начнет читать после значения 2 и найдет '\ n'. Затем он прекращает чтение и возвращает пустую строку. Итак, после вызова std::getline
std::string
s1 будет пустым, будет использоваться '\ n' и следующая позиция для чтения будет "string1".
И теперь мы попробуем testDataStream >> i3 >> i4;
. Но нет 3 и 4. Помните: позиция для чтения все еще в "string1". Таким образом, операция извлечения не удастся и будет установлен failbit
потока.
Но вы никогда не тестируете failbit
, поэтому остальная часть программы молча завершается сбоем.
Вы можете и должны проверять результат каждой операции извлечения.
Это можно просто сделать с помощью
if (testDataStream >> i1 >> i2) {
// Good. Continue standard operation
}
else {
// Bad. Show error message and do error handling
}
Почему это работает? Цепная операция извлечения возвращает ссылку на поток в конце. И у потока есть перегруженный оператор bool. См. здесь и здесь .
Итак, для проверки состояния потока вы всегда можете написать просто if (someStream)
.
ОК, понятно , Но как мы можем избавиться от конечного '\ n' в конце строки. Для этого у нас есть функция ignore . См. Документацию по ссылке.
Чтобы исправить фрагмент кода избежания, нам нужно добавить ignore
2 раза:
#include <limits>
#include <iostream>
#include <sstream>
std::istringstream testDataStream{ R"(1 2
string1
3 4
string2)" };
int main() {
int i1{}, i2{}, i3{}, i4{};
std::string s1{}, s2{};
testDataStream >> i1 >> i2;
testDataStream.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::getline(testDataStream, s1);
testDataStream >> i3 >> i4;
testDataStream.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::getline(testDataStream, s2);
// more code . . .
return 0;
}
И чтобы показать вам, как все это можно реализовать, Я подготовил для вас полный рабочий пример.
Пожалуйста, смотрите:
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <iterator>
#include <limits>
#include <array>
// Function to check, if we have a valid month
constexpr std::array<std::string_view, 12> Months{ "January", "February", "March","April", "May", "June", "July","August", "September", "October","November", "December" };
bool isInValidMonth(const std::string& m) { return std::find(Months.begin(), Months.end(), m) == Months.end(); }
struct Unit {
// Data for one semester/class unit for a student
std::string id{};
std::string name{};
unsigned int credit{};
unsigned int mark{};
unsigned int dateDay{};
std::string dateMonth{};
unsigned int dateYear{};
// This will check, if the data that we have read is ok, and if not, set the fail bit of the stream
std::istream& sanityCheck(std::istream& is) {
// Check for non plausible data. If there is unplausible data anywhere
if (!is || credit > 500 || mark > 100 || dateDay == 0U || dateDay > 31 || dateYear < 1920 || dateYear > 2100 || isInValidMonth(dateMonth)) {
// Set the streams failbit
is.setstate(std::ios::failbit);
std::cerr << "\n*** Error: Problem while reading unit data\n";
}
return is;
}
// Overide extractor. Get Unit data
friend std::istream& operator >> (std::istream& is, Unit& u)
{
if (std::getline(std::getline(is, u.name), u.id) >> u.credit >> u.mark >> u.dateDay >> u.dateMonth >> u.dateYear)
// Eat up the trailing '\n'
is.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
return u.sanityCheck(is);
}
// Overwrite inserter. Write unit data
friend std::ostream& operator << (std::ostream& os, const Unit& u) {
return os << "\tUnit ID:\t" << u.id << "\n\tUnit name:\t" << u.name << "\n\tCredits:\t" << u.credit << "\n\tMarks:\t\t"
<< u.mark << "\n\tDate:\t\t" << u.dateDay << " " << u.dateMonth << " " << u.dateYear << "\n";
}
};
struct Student {
// Student data
unsigned long id{};
unsigned long semester{};
std::vector<Unit> unit{};
// Override extractor: Read Student Data
friend std::istream& operator >> (std::istream& is, Student& s) {
s.unit.clear();
// Here will store the number of units that we need to read
size_t numberOfUnits{};
// Read, id and semester and numberOfUnits and check, if that was successfull
if (is >> s.id >> s.semester >> numberOfUnits) {
// Eat up the trailing '\n'
is.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
// Read all units. Call the extractor operator n times, but only if everything OK
for (size_t i = 0U; i < numberOfUnits && is; ++i) {
// Read a unit and check, if OK
if (Unit temp{}; is >> temp) {
// If OK, append the new unit to the student
s.unit.push_back(std::move(temp));
}
}
}
// Sanity check. We expect to have numberOfUnits
if (!is && s.unit.size() != numberOfUnits) {
// Set the streams failbit
is.setstate(std::ios::failbit);
std::cerr << "\n*** Error: Problem while reading student data\n";
}
return is;
}
// Override inserter: Write student data
friend std::ostream& operator << (std::ostream& os, const Student& s) {
os << "\n\nStudent ID:\t" << s.id << "\nSemester:\t" << s.semester << "\n\n";
std::copy(s.unit.begin(), s.unit.end(), std::ostream_iterator<Unit>(os, "\n"));
return os;
}
};
struct Register {
std::vector<Student> students{};
// Override extractor: Read Student Data
friend std::istream& operator >> (std::istream& is, Register& r) {
for (Student s{}; is && is >> s; ) if (is) r.students.push_back(s);
return is;
}
// Override inserter: Write complete Register
friend std::ostream& operator << (std::ostream& os, const Register& r) {
std::copy(r.students.begin(),r.students.end(), std::ostream_iterator<Student>(os, "\n"));
return os;
}
};
std::istringstream inputStream{ R"(102234 962 3
Data Structures and Abstractions
ICT283
3 90 30 June 2016
Applied ICT Research Skills
BSC250
3 92 24 April 1993
Games Technology
ICT292
3 76 4 August 1998
222222 22 2
Data Structures and Abstractions 2
ICT222
3 90 30 June 2016
Applied ICT Research Skills 2
BSC222
2 2 2 February 1993
)" };
int main() {
Register r;
inputStream >> r;
std::cout << r;
return 0;
}
Веселитесь. , .