I'm not sure if operator overloading is what I'm looking for, but I need to know the best way to achieve the following in C++;
I have a class Employee (for simplicity) with just an ID number atm. Please assume the input file has an int number and some characters after (1 line shown only), such as:
1234 Charles Hammond
Here is the code so far. I am trying to use the extraction operator to get the integer and other data from input file to my class function (SetID);
class Employee
{
int employeeID;
public:
void SetID(int);
}
void Employee::SetID(int empID)
{
employeeID = empID;
}
int main(void)
{
int lineCounter = 4;
Employee emp;
//Create filestream objects and open
ifstream input;
ofstream output;
input.open("input.txt");
output.open("output.txt");
for (int i = 0; i < lineCounter; i++)
{
input >> emp.SetID(input.get()); //illegal? Best way to do this
}
//Close program
output.close();
input.close();
system("pause");
return 0;
}
I am simply trying to get the ID from the input file and store it in the class member "employeeID" to be used for calculations later.
One option is to overload the >> operator and make it a friend function in your Employee class.
Something like:
istream& operator>>( istream& in, Employee& emp )
{
in >> emp.employeeID;
return in;
}
And in your Employee class:
friend istream& operator>> (istream& in, Employee& emp);
There are a ton of ways to do this, each with pluses and minuses. The format of the data you're reading indicates you have one "record" per line, in which case that should be enforced somehow. The following does that by reading a line of data from the input file, then sending that line through a string stream for further processing:
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
class Employee
{
// friend this operator, as we'll give it access to our
// private data members of our class.
friend std::istream& operator >>(std::istream& inp, Employee& obj);
int employeeID;
public:
void setID(int id) { employeeID = id; }
int getID() const { return employeeID; }
};
// extracts a single employee from a single input line taken from
// the passed input stream. the code below mandates one, and only
// one employee per line.
std::istream& operator >>(std::istream& inp, Employee& obj)
{
// used as a full-line-buffer to enforce single-record-per-line
std::string line;
if (std::getline(inp, line))
{
// think of it as an in-memory stream primed with our line
// (because that's exactly what it is).
std::istringstream iss(line);
// TODO: eventually you'll want this to parse *all* values from
// the input line, not just the id, storing each in a separate
// member of the Employee object being loaded. for now we get
// only the id and discard the rest of the line.
if (!(iss >> obj.employeeID))
{
// a failure to read from the line string stream should flag
// the input stream we read the line from as failed. we also
// output the invalid line to std::cerr.
inp.setstate(std::ios::failbit);
std::cerr << "Invalid format: " << line << std::endl;
}
}
return inp;
}
int main()
{
// open input and output files
std::ifstream input("input.txt");
// read at-most four employee lines from our file. If there are
// less than that or a read-error is encountered, we will break
// early.
Employee emp;
for (int i=0; i<4 && input >> emp; ++i)
{
// do something with this thing
std::cout << "Read Employee: " << emp.getID() << '\n';
}
system("pause");
return 0;
}
Related
I am doing a project with I/O and structs. My text file is below. I need to make my program store each string in a different part of the array of structs I have created. I am having a problem making it separate them in the array when it senses a blank line.
Steps for the program: 1. Read each line with data and store it in the struct array until it reaches a blank line. 2. Output each string in a different group or on a different line.
Text file:
ecl:gry pid:860033327 hcl:#fffffd
byr:1937 iyr:2017 cid:147 hgt:183cm
iyr:2013 ecl:amb cid:350 pid:028048884
hcl:#cfa07d byr:1929
hcl:#ae17e1 iyr:2013 cid:150
eyr:2024
ecl:brn pid:760753108 byr:1931
hgt:179cm
hcl:#cfa07d eyr:2025 pid:166559648
iyr:2011 ecl:brn hgt:59in cid:230
My code:
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
const int SIZE = 4;
struct Passports {
std::string singlePass;
};
int main()
{
Passports records[SIZE];
std::string fileName = "some_file.txt";
std::ifstream inFile(fileName);
std::string line, data;
if (inFile.is_open()){
while (!inFile.eof()) {
getline(inFile, line);
std::istringstream ss(line);
for (int i = 0; i < SIZE; i++) {
while (ss >> records[i].singlePass) {
std::cout << records[i].singlePass;
}
}
}
}
else {
std::cout << "Error opening file! " << std::endl;
}
}
You should model the data using a struct.
struct Record
{
std::string input_line1;
std::string input_line2;
friend std::istream& operator>>(std::istream& input, Record& r);
};
std::istream& operator>>(std::istream& input, Record& r)
{
std::getline(input, r.input_line1);
std::getline(input, r.input_line2);
input.ignore(1000000, '\n'); // ignore the blank line.
return input;
}
Your input code would look like:
std::vector<Record> database;
Record r;
while (inFile >> r)
{
database.push_back(r);
}
By placing the detailed input inside of the struct, you can modify the input method later without having to change the input code in main().
Detailed Parsing
You could add in a field or two to advance your program (no need to add all the fields at this point, then can be added later).
struct Passport
{
std::string ecl;
friend std::istream& operator>>(std::istream& input, Passport& p);
};
std::istream& operator>>(std::istream& input, Passport& p)
{
std::string text_line1;
std::string text_line2;
std::getline(input, text_line1);
std::getline(input, text_line2);
size_t position = text_line1.find("ecl:");
if (position != std::npos)
{
// extract the value for ecl and assign to p.ecl
}
return input;
}
There are many different methods for parsing the string, the above is alluding to one of them.
Suppose I have the following class with some private string:
class data{
public:
friend istream &operator>>(istream&, data&);
private:
string firstname;
string lastname;
string phonenum;
}
So, what is the right way to use the overload to read in my string from file. Here is my code. I had researched and seen people using getline. But when I use getline for my private data, it print out the whole different string.
istream &operator>>(istream &in, data &r)
{
// write this to read data object data
in.ignore();
in >> r.firstname;
in >> r.lastname;
in >> r.phonenum;
return in;
}
int main(){
ifstream inFile;
inFile.open("list1.txt"); // open file.txt
string line;
vector<data> A;
data din;
while (inFile >> din){
A.push_back(din);
cout << din << endl; // to print out if this read
}
inFile.close();
}
The file. First name, last name and phone number
CANDACE WITT 250-939-5404
THEODORE PERKINS 723-668-3397
file_name = file_name_out_of_class;
ifstream file(file_name);
if (file.is_open()) {
string line;
int temp_sap;
int temp_sem;
int temp_cours;
int temp_cred;
while (getline(file, line)) {
cout << sizeof(info) << endl;
//????????
}
}
C++
This pic contains data of the student I want to store in variables or array, but the problem is who to store it. Because 1st line contain large data and 2nd line contain small data.
Since the most important information, I mean, the column header, or field names, or student attribute is missing, I can only give a general answer.
You should abstract the data in one line into a corresponding data structure. I give you an artificial example. A line, as you did show above, could be structured like this:
First an ID, then an item, and then, 0, 1 or more pairs of Evaluators consisting of Descriptors and Numbers. All names are arbitrary chosen. If you give the descripition of the content of one line, then I could use that instead. Anyway.
Then we make an abstraction and create a datatype, a struct/class that can hold the data above.
So, first we have something like:
struct Evaluator {
std::string descriptor{};
unsigned int number{};
};
Next we make a abstraction of complete line data, maybe like that
struct Record {
unsigned int ID{};
unsigned int item{};
std::vector<Evaluator> eval{};
};
the std::vector makes the data dynamic. There can be 0,1 or more Evaluators in there.
And then we will use the C++ method of data inpit and output. The iostream facilities. You know the inserter << operator and the extractor >> operator.
This we will define for our structs.
Then the whole program will look like :
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
struct Evaluator {
std::string descriptor{};
unsigned int number{};
// Extractor
friend std::istream& operator >> (std::istream& is, Evaluator& ev) {
return is >> ev.descriptor >> ev.number;
}
// Inserter
friend std::ostream& operator << (std::ostream& os, const Evaluator& ev) {
return os << '\t' << ev.descriptor << '\t' << ev.number;
}
};
struct Record {
unsigned int ID{};
unsigned int item{};
std::vector<Evaluator> eval{};
// Extractor
friend std::istream& operator >> (std::istream& is, Record& r) {
std::string line{};
if (std::getline(is, line)) {
std::istringstream iss(line);
// Delete old data
r.eval.clear();
// Read attributes
iss >> r.ID >> r.item;
// Read all Evaluators
Evaluator temp{};
while (iss >> temp) {
r.eval.push_back(std::move(temp));
}
}
return is;
}
// Inserter
friend std::ostream& operator << (std::ostream& os, const Record& r) {
// Write main atributes
os << r.ID << '\t' << r.item;
// Write all evaluators
for (const Evaluator& e : r.eval) os << e;
return os;
}
};
std::istringstream testFile{ R"(7001 2 OOP 4 POM 3 CS 4 Englh 3 Isl.st 2
7002 2 OOP 4 CS 4 Isl.St 2)" };
int main() {
// All records (all lines)
std::vector<Record> record;
// Read all data
Record r;
while (testFile >> r)
record.push_back(std::move(r));
// show debug output
for (const Record& rr : record) std::cout << rr << '\n';
return 0;
}
I have a large textfile in which each line represents a city with postalcode and state.
It is written like this:
Brandenburg 35432 Potsdamm
Niedersachsen 35698 Hannover
I've already read the file in a vector and I have written a class and want to assign the classvalues to the vector.
class City
{
private:
float lat; //latitude
float lon; //longitude
public:
City cityclass(std::string state, std::string zipCode, std::string name);
//std::string name;
//std::string state;
//std::string zipCode;
float getLatitude() const
{
return lat;
}
float getLongitude() const
{
return lon;
}
};
So I have a class with std::string zipCode, the state and the cityname. I assume that this would be better to work with, especially when I want to be able to search for city's by zip or name.
How can I realize this? I thought about to simply modify my while-loop as it follows, but I'm realy not sure if this is the way to go.
Here is my full code:
class City
{
private:
/*float lat; //latitude
float lon; //longitude*/
public:
std::string zipCode;
std::string name;
std::string state;
/*float getLatitude() const
{
return lat;
}
float getLongitude() const
{
return lon;
}*/
};
int main ()
{
std::ifstream input("bundesland_plz_ort_de.txt");
//initilazing a vector of type string to store the data
std::vector<City> cityVector;
City city; //creating instance of class
//check if file can be accessed
if(!input)
{
std::cout << "ERROR!\tFile could not be opened!" << std::endl;
}
else
{
while(input >> city.state >> city.zipCode >> city.name)
{
cityVector.push_back(city);
}
input.close(); // close after finishing
}
}
I would recommend a slightly different approach. You defined a class City, but you read the data outside of the class.
In C++ we should put data on operations that are working on that data, all in one class. In this case you would (in your class) overwrite the inserter and extractor operator. The class knows, how to read and write its data. Even if you change the algorithm later, the rest of the code will work without modification.
In the following example code I put an ultra simple extractor and inserter (No error checking). Reading all data from the source file is then just one simple statement.
It is the definition of the variable "cl" as std::vector, using its range constructor. Very short and simple.
Usage of range constructor for std::vector.
We can define the std::vector without template argument. The compiler can deduce the argument from the given function parameters. This feature is called CTAD ("class template argument deduction").
Additionally, you can see that I do not use the "end()"-iterator explicitely.
This iterator will be constructed from the empty brace-enclosed initializer list with the correct type, because it will be deduced to be the same as the type of the first argument due to the std::vector constructor requiring that.
Having modified the class as described, you can then use all algorithm from the std library.
Please see:
#include <iostream>
#include <string>
#include <vector>
#include <iterator>
#include <algorithm>
#include <fstream>
struct City {
// Data
std::string state{};
std::string zipCode{};
std::string name{};
// Member functions
// Extractor
friend std::istream& operator >> (std::istream& is, City& c) {
return is >> c.state >> c.zipCode >> c.name;
}
// Inserter
friend std::ostream& operator << (std::ostream& os, const City& c) {
return os << "\nState: " << c.state << "\nZip Code: " << c.zipCode << "\nName: " << c.name;
}
};
int main() {
// Try to open file and check, if it worked
if (std::ifstream sourceFile("r:\\bundesland_plz_ort_de.txt"); sourceFile) {
// Read complete source file into a city list
std::vector cl(std::istream_iterator<City>(sourceFile), {});
// Give some Debug output
std::copy(cl.begin(), cl.end(), std::ostream_iterator<City>(std::cout, "\n"));
}
else {
std::cerr << "\nError: Source file could not be opened\n";
}
}
Of course there are many other possible solutions . . .
just go on
first obtaining the number of object approximately by a loop precede it to count the total space delimited string then it's divided by the number of member data
// ...
//City city; //creating instance of class
if(!input)
{
std::cout << "ERROR!\tFile could not be opened!" << std::endl;
}
else if (input.is_open()) {
string buf(25);
int i=0;
while( input >> buf) ++i;
std::vector<City> cityVector(i /3 +5); // the number of member data plus some extra for sureness
input.clear();
input.seekg(0, ios::beg); // point back to start
i=0;
while( input >> cityVector[i].state >> cityVector[i].zipCode >> cityVector[i].name) ++i;
input.close(); // close after finishing
}
//..
this way need estimate the array size earlier approximately plus some guarding amount, then can resize, refit it eg. cityVector.resize(i+1)
I am trying to understand reading different txt file formats in c++
I am currently trying to read a file formatted like this,
val1 val2 val3
val1 val2 val3
val1 val2 val3
When I read the file in and then cout its contents I only get the first line then a random 0 0 at the end.
I want to save each value into its own variable in a struct.
I am doing this like this,
struct Input{
std::string group;
float total_pay;
unsigned int quantity;
Input(std::string const& groupIn, float const& total_payIn, unsigned int const& quantityIn):
group(groupIn),
total_pay(total_payIn),
quantity(quantityIn)
{}
};
int main(){
std::ifstream infile("input.txt");
std::vector<Input> data;
std::string group;
std::string total_pay;
std::string quantity;
std::getline(infile,group);
std::getline(infile,total_pay);
std::getline(infile,quantity);
while(infile) {
data.push_back(Input(group,atof(total_pay.c_str()),atoi(quantity.c_str())));
std::getline(infile,group);
std::getline(infile,total_pay);
std::getline(infile,quantity);
}
//output
for(Input values : data) {
std::cout << values.group << " " << values.total_pay << " " << values.quantity << '\n';
}
return 0;
}
What is the proper way to read this file in the the format I have specified? Do I need to specify to go to the next line after the third value?
Or should this be taking each value and putting them in to the right variable?
std::getline(infile,group);
std::getline(infile,total_pay);
std::getline(infile,quantity);
Your input processing has a number of issues. Your prevalent usage of std::getline in places where it is not needed isn't helping.
In short, per-line validation of input is generally done with a model similar to the following. Note that this requires the class provide a default constructor. We use an input-string-stream to process a single item from each line of input from the input file. If it was certain there was at-most one per line, we could forego the per-line processing, but it is a potential place for errors, so better safe than sorry. The mantra presented here is commonly used for per-line input validation when reading a stream of objects from a formatted input file, one item per line.
The following code defines the structure as you have it with a few extra pieces, including providing both an input and output stream insertion operator. The result makes the code in main() much more manageable.
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <iterator>
struct Input
{
// friends not needed if the members are public, but provided here
// in case you ever do make them protected or private (which you should)
friend std::istream& operator >>(std::istream& inp, Input& item);
friend std::ostream& operator <<(std::ostream& outp, Input const& item);
std::string group;
float total_pay;
unsigned int quantity;
// default constructor. sets up zero-elements
Input() : total_pay(), quantity()
{
}
Input(std::string groupIn, float total_payIn, unsigned int quantityIn)
: group(std::move(groupIn))
, total_pay(total_payIn)
, quantity(quantityIn)
{
}
// you really should be using these for accessors
std::string const& getGroup() const { return group; }
float getTotalPay() const { return total_pay; }
unsigned int getQuantity() const { return quantity; }
};
// global free function for extracting an Input item from an input stream
std::istream& operator >>(std::istream& inp, Input& item)
{
return (inp >> item.group >> item.total_pay >> item.quantity);
}
// global operator for inserting to a stream
std::ostream& operator <<(std::ostream& outp, Input const& item)
{
outp << item.getGroup() << ' '
<< item.getTotalPay() << ' '
<< item.getQuantity();
return outp;
}
int main()
{
std::ifstream infile("input.txt");
if (!infile)
{
std::cerr << "Failed to open input file" << '\n';
exit(EXIT_FAILURE);
}
// one line per item enforced.
std::vector<Input> data;
std::string line;
while (std::getline(infile, line))
{
std::istringstream iss(line);
Input inp;
if (iss >> inp) // calls our extaction operator >>
data.emplace_back(inp);
else
std::cerr << "Invalid input line: " << line << '\n';
}
// dump all of them to stdout. calls our insertion operator <<
std::copy(data.begin(), data.end(),
std::ostream_iterator<Input>(std::cout,"\n"));
return 0;
}
Provided the input is properly formatted, values like this:
group total quantity
group total quantity
will parse successfully. Conversely, if this happens:
group total quantity
group quantity
group total quantity
total quantity
the extractions of the second and fourth items will fail, and appropriate warning will be issued on std::cerr. This is the reason for using the std::istringstream intermediate stream object wrapping extraction of a single line per item.
Best of luck, and I hope it helps you out.
Check this solution
It is without error checks but with conversion to types
#include<iostream>
#include<sstream>
using namespace std;
int main()
{
string line="v1 2.2 3";//lets say you read a line to this var...
string group;
float total_pay;
unsigned int quantity;
//we split the line to the 3 fields
istringstream s(line);
s>>group>>total_pay>>quantity;
//print for test
cout<<group<<endl<<total_pay<<endl<<quantity<<endl;
return 0;
}