Notes regarding the OP
So my question is, would all this compile? Or perhaps is there a simple way of doing it?
There are two questions here. The answers are: (1) Try it, and see. (2) Yes! See class Student below.
A problem with delimiter
When I plugged the code from the OP into a test framework I had created, initially, I got just a few errors. The first was a complaint about the C++23 character literal used to initialize variable delimiter. It is not yet supported by my compiler.
char delimiter = '\x{ 0000 }'; // A new kind of character literal from C++23
Since I was planning on changing this to a simple comma, anyway, I went ahead and did that.
char const delimiter = ',';
Unmatched parentheses
The next complaint was about a couple of unmatched parentheses, so I fixed that.
while (std::getline(stream, check) { // <------ missing )
if (!getline(iss, check, delimiter) { // <------ missing )
stream.setstate(std::ios::fail) abort();
}
//...
}
A bevy of errors
With those preliminaries out of the way, I was finally able to get the compiler to tell me what it really thinks, and the results were not good.
It gave me a bevy of errors, more than 130 lines worth, beginning with these.
1>C:\Users\blotc\OneDrive\Documents\Programming\StackOverflow\Answers\StackOverflow_78980542_ExtractGroceryItem_Question.cpp(57,47): error C3867: 'std::ios_base::fail': non-standard syntax; use '&' to create a pointer to member
1>C:\Users\blotc\OneDrive\Documents\Programming\StackOverflow\Answers\StackOverflow_78980542_ExtractGroceryItem_Question.cpp(57,53): error C2146: syntax error: missing ';' before identifier 'abort'
1>C:\Users\blotc\OneDrive\Documents\Programming\StackOverflow\Answers\StackOverflow_78980542_ExtractGroceryItem_Question.cpp(64,36): error C2440: '=': cannot convert from 'std::string' to 'double'
1> C:\Users\blotc\OneDrive\Documents\Programming\StackOverflow\Answers\StackOverflow_78980542_ExtractGroceryItem_Question.cpp(64,36):
There was enough wrong that I decided it would better to demonstrate how to do it right, rather than to address each error, one-by-one.
A similar operator>>, for class Student
This appears to be some sort of homework assignment, so let's go with a similar student/GPA example, instead grocery items.
In this example, and also in the OP, the sample data are structured into records.
- Each record has 4 fields.
- Fields are comma-separated, but there is no comma after the 4th field.
- Whitespace can appear both before and after a delimiting comma.
- The first 3 fields are quoted strings.
- The 4th field is a floating-point number.
- Any amount of whitespace can appear between records.
You can see the data I used for testing in function sample_data below. It includes one incomplete record at the end.
Function operator>> is a hidden friend
operator>> is defined within class Student, as a hidden friend. That way, it has access to the private data members of the class.
friend std::istream& operator>>(std::istream& stream, Student& student)
{
Student local;
char const delimiter{ ',' }; // Use comma, NOT '\x{0000}'
char a, b, c;
stream >> std::skipws
>> std::quoted(local._studentID)
>> a >> std::quoted(local._studentName)
>> b >> std::quoted(local._major)
>> c >> local._gpa;
if (stream.fail() ||
a != delimiter ||
b != delimiter ||
c != delimiter) {
stream.setstate(std::ios_base::failbit);
}
else {
student = std::move(local);
}
return stream;
}
Fail when a field cannot be extracted
The stream extraction is very similar to one of the operations from the OP. It chains together several extractions. If any one of them fails, stream will be placed into a failed state, and subsequent input operations will fail.
stream >> std::skipws
>> std::quoted(local._studentID)
>> a >> std::quoted(local._studentName)
>> b >> std::quoted(local._major)
>> c >> local._gpa;
The 4th field must be a well-formed, floating-point number. This helps to trap errors.
You can experiment below, by changing the sample data, to invalidate one or more of the fields.
If a number cannot be extracted from the 4th field, input fails.
If a string field contains embedded whitespace, and the quote mark is missing at its beginning, then the field will be interpreted to be two (or more) fields. This causes the data to become unsynchronized. Subsequent string extractions may appear to succeed, but when a string is encountered at what is supposed to be the 4th field, extraction will fail.
When the quote mark is missing at the end of a string, the following delimiter will be merged with the field. As before, this will cause the data to become unsynchronized, and extraction will fail when the 4th field turns out not to be a number.
Due to the way std::quoted works, if you remove both of the quote marks from a string field, extraction may succeed. This happens if the following conditions are true.
- The field does not contain embedded whitespace, i.e., it is a "one-word" field.
- The following delimiter is preceeded by whitespace, so that it will not be merged into the string.
Fail when the delimiter is invalid
Note that the delimiter is ',', rather than '\x{0000}'.
As delimiter characters are read in, each is stored in a separate character variable, a, b, or c. That makes it possible to verify that each one is a comma.
If any of them is not a comma, the following if-statement will place the stream into a failed state.
One thing worth pointing out: If the stream has already failed, it is not necessary to set its failbit a second time. We do it here, just to avoid a tangle of if-statements.
if (stream.fail() ||
a != delimiter ||
b != delimiter ||
c != delimiter) {
stream.setstate(std::ios_base::failbit);
}
Save and restore stream state
In operator>> (above), and also in operator<<, from the testing framework below, I/O manipulators are used to change the state of a stream.
- Prior to input,
std::skipws is "extracted" from the stream.
- Prior to ouput,
std::fixed and std::setprecision are "inserted" into the stream.
In production code, you might need to save the current settings of a stream, before they are modified by I/O manipulators such as the ones above. Then, after I/O is complete, you can restore the original stream settings.
For a homework assignment, such as this, that has been omitted.
Testing framework
Source code for the testing framework I used is given below.
Here are a few notes.
In the OP, operator>> contains a loop that inputs multiple records. That loop has been moved into function main below.
In the OP, operator>> contains what appears to be a check to see whether an input file was successfully opened. That check belongs in function main, as does the file-open operation itself.
The program below avoids using a file, by placing the sample data into a std::istringstream object, and reading from that.
// main.cpp
#include <iomanip> // quoted, setprecision
#include <iostream> // cout, fixed, ios_base, istream, ostream
#include <sstream> // istringstream
#include <string> // getline, string
#include <utility> // move
//======================================================================
// Student
//======================================================================
class Student
{
std::string _studentID;
std::string _studentName;
std::string _major;
double _gpa{};
public:
Student()
= default;
Student(
std::string studentID,
std::string studentName,
std::string major,
double gpa)
: _studentID{ studentID }
, _studentName{ studentName }
, _major{ major }
, _gpa{ gpa }
{}
// More member functions
// ...
friend std::ostream& operator<<(std::ostream& stream, Student const& student)
{
stream << std::fixed << std::setprecision(1)
<< "Student ID : " << student._studentID
<< "\nStudent name : " << student._studentName
<< "\nMajor : " << student._major
<< "\nGPA : " << student._gpa
<< '\n';
return stream;
}
friend std::istream& operator>>(std::istream& stream, Student& student)
{
Student local;
char const delimiter{ ',' }; // Use comma, NOT '\x{0000}'
char a, b, c;
stream >> std::skipws
>> std::quoted(local._studentID)
>> a >> std::quoted(local._studentName)
>> b >> std::quoted(local._major)
>> c >> local._gpa;
if (stream.fail() ||
a != delimiter ||
b != delimiter ||
c != delimiter) {
stream.setstate(std::ios_base::failbit);
}
else {
student = std::move(local);
}
return stream;
}
};
//======================================================================
// sample_data
//======================================================================
std::string sample_data() {
return
R"sample("00072250018548","Dwayne \"The Rock\" Johnson","Wrestling",2.8
"00028000517205", "Beyonce" ,
"Music" ,
4.0
"00034000020706" ,
"Cristiano Ronaldo",
"Futebol",
4.0 "00038000570742",
"Kim Kardashian", "Undeclared",
1.9
"00014100072331" , "Oprah Winfrey", "Broadcast Journalism", 3.2
"00000000000000", "incomplete / invalid item"
)sample";
}
//======================================================================
// main
//======================================================================
int main()
{
std::istringstream iss{ sample_data() };
Student student;
while (iss >> student) {
std::cout << student << '\n';
}
return 0;
}
// end file: main.cpp
Output
Student ID : 00072250018548
Student name : Dwayne "The Rock" Johnson
Major : Wrestling
GPA : 2.8
Student ID : 00028000517205
Student name : Beyonce
Major : Music
GPA : 4.0
Student ID : 00034000020706
Student name : Cristiano Ronaldo
Major : Futebol
GPA : 4.0
Student ID : 00038000570742
Student name : Kim Kardashian
Major : Undeclared
GPA : 1.9
Student ID : 00014100072331
Student name : Oprah Winfrey
Major : Broadcast Journalism
GPA : 3.2