C++ Read from file to vector - c++

I'm trying to convert a string streamed phone lookup program into file streamed.. I'm missing something, but I'm stuck.. what members can I use in the ofstream process to get this working?
ofstream& process (ofstream &os, vector<PersonInfo> people)
{
// for each entry in people
for (vector<PersonInfo>::const_iterator entry = people.begin();
entry != people.end(); ++entry) {
ofstream formatted, badNums; // objects created on each loop
// for each number
for (vector<string>::const_iterator nums = entry->phones.begin();
nums != entry->phones.end(); ++nums) {
if (!valid(*nums)) {
badNums << " " << *nums; // string in badNums
} else
// ``writes'' to formatted's string
formatted << " " << format(*nums);
}
if (badNums.empty()) // there were no bad numbers
os << entry->name << " " // print the name
<< formatted.str() << endl; // and reformatted numbers
else // otherwise, print the name and bad numbers
cerr << "input error: " << entry->name
<< " invalid number(s) " << badNums.str() << endl;
}
return os;
}

First, you don't want an ofstream, except at the point you're opening
the file (creating the instance). The output stream interface is
defined by std::ostream; std::ofstream derives from this, as does
std::ostringstream (output can become an std::string), and in most
applications, a couple of others written by the local programmers. In
your case (if I've understood the problem correctly), what you want is:
std::ostream& process( std::ostream& os,
std::vector<PersonInfo> const& people )
// Note the use of a const reference above. No point
// in copying the entire vector if you're not going to
// modify it.
{
for ( std::vector<PersonInfo>::const_iterator entry = people.begin();
entry != people.end();
++ entry ) {
std::ostringstream formatted;
std::ostringstream badNums;
// ...
if ( badNums.str().empty() ) {
os << ... << formatted.str() << std::endl;
} else {
os << ... << badNums.str() << std::endl;
}
}
return os;
}
Note the different types: std::ostream formats output, independently
of the destination type. std::ofstream derives from it, and provides
a file as destination. std::ostringstream derives from it, and
provides a std::string as destination type. And the std::ostream
takes a std::streambuf* as argument, and you provide the destination
type.

You never associate a file with ostream, so the compiler doesn't know what to do with the data you write into it.
ofstream& process (ofstream &os, vector<PersonInfo> people)
{
os.open("Data.txt"); //open file to be used
if(!os.is_open())
std::cerr << "Error opening file!\n";
//rest of code goes here
}
EDIT: after reading through your program again, i noticed you're using ofstream wrong. Ofstream is for opening and writing FILES.The program has a lot of syntax and logical errors i would read up on it more here.

It looks like you don't need to use ofstreams for the internal parts of this function. In fact you don't need to use streams at all, a std::string would do:
ofstream& process (ofstream &os, vector<PersonInfo> people)
{
// for each entry in people
for (vector<PersonInfo>::const_iterator entry = people.begin();
entry != people.end(); ++entry) {
string formatted, badNums; // objects created on each loop
// for each number
for (vector<string>::const_iterator nums = entry->phones.begin();
nums != entry->phones.end(); ++nums) {
if (!valid(*nums)) {
badNums += " " + *nums; // string in badNums
} else
// ``writes'' to formatted's string
formatted += " " + format(*nums);
}
if (badNums.empty()) // there were no bad numbers
os << entry->name << " " // print the name
<< formatted << endl; // and reformatted numbers
else // otherwise, print the name and bad numbers
cerr << "input error: " << entry->name
<< " invalid number(s) " << badNums << endl;
}
return os;
}

Related

How replace specific value from textfile using ofstream c++?

Hi I am working an a vending machine and I want to update the quantity of the item by updating the text file. I been trying using ofstream and ifstream but is not working.
This is my text file.
Water:1:1.99:D1
Coke:4:2.79:D2
Milk:6:3.15:D3
Ham Sandwitch:9:4.50:L1
Lunchables:3:6.00:L2
Cereal:2:3.59:L3
M&M:8:1.75:C1
SourPatch:0:2.10:C2
Twix:6:2.99:C3
This is where vending machine checks the user input and where i want to update the file
void vendingWorking(Item &item) {
if(item.quantity == 0) {
cout << endl;
cout << "------------------------------------" << "\n";
cout << "" << item.name << " (OutStock)" << endl;
cout << "------------------------------------" << "\n";
}
else {
//Check if itemCode is same as product ID
if(itemCode == item.productId) {
//HERE I WANT TO UPDATE THE QUANTITY OF THE ITEM IF USER HAS PICKED ONE
//EXAMPLE: (Old) Water:2:2.50:D1 -> (New) Water:1:2.50:D1
//Message for user
cout << endl;
cout << "------------------------------------" << "\n";
cout << "" << item.name << ", $" << fixed << setprecision(2)<< item.price << " (InStock)" << "\n" ;
//Pass value to vector
tempBasket.push_back({item.name, item.price});
}
}
}
What you would like to do is:
Read the product content of the vending machine from a file
Modify data somehow
Write the product content of the vending machine to a file
How does modify somehow work? As you cannot change files online with arbitrary new data, you need to do like this:
Read file into memory --> operate on data in memory --> Save modified data in file
For the above there are 2 approaches.
Open file --> Read data --> Close file --> Modfiy data in memory --> Open file for output by overwrite original file --> Save Data --> Close file
Or, a little bit safer:
Open file --> Read data --> Close file --> Modfiy data in memory --> Open temporary file for output --> Save Data in temporary file --> Close temporary file --> If everything OK, delete original file --> rename temporary file to orignial file name
But the key is, to work on the data in memory.
You can also create "load" and "save" fucntions. So, at any time, after changing data in memory, you could "save" the modified data. With one of the above described methods.
Or, you could "load" your data in a constructor and "save" it in a destructor. Everything would then work automatically.
Regarding the "load" function. You need to read the source file line by line and then split the line into your needed data members. I have answered a question here, which describes 4 different methods on how to split a line. In the below given example, I use a std::regex based solution using std::regex_match. This will ensure that the data is in the expected format.
Please note that you should also overwrite the extractor and inserter operators >> and << for easier working with streams.
And last but not least, everything should be encapsulated in classes.
Please see a working and tested example code for a partial implemented vending machine functionality. In this code I am using C++17 features, like if with initializer. So, If you want to compile, then please enable C++17 for your compiler.
Additionally, this is just some code to illustrate the explanations above. There are 1 million solutions. In the end you need to come up with sometthing fitting the requirements.
#include <iostream>
#include <fstream>
#include <string>
#include <iterator>
#include <vector>
#include <regex>
#include <algorithm>
#include <numeric>
const std::regex re{ R"(^([^:]+):(\d+):(\d+\.\d+):([A-Z]+\d+))" };
class VendingMachine {
// Local definition of item struct
struct Item {
// Item attributes
std::string name{};
unsigned long quantity{};
double price{};
std::string productID{};
// Simple overwrite of extractor operator
friend std::istream& operator >> (std::istream& is, Item& it) {
// Read a complete line and check, if that worked
if (std::string line{}; std::getline(is, line)) {
// Check, if the input line, is in the expected format
if (std::smatch sm{}; std::regex_match(line, sm, re)) {
it.name = sm[1];
it.quantity = std::stoul(sm[2]);
it.price = std::stod(sm[3]);
it.productID = sm[4];
}
else std::cerr << "\n***Error while reading: '" << line << "'\n'";
}
return is;
}
// Simple overwrite of inserter operator
friend std::ostream& operator << (std::ostream& os, const Item& it) {
return os << it.name << ':' << it.quantity << ':' << it.price << ':' << it.productID;
}
};
// All products in vending machine
std::vector<Item> products{};
// Filename for saving and loading
std::string fileName{ "products.txt" };
public:
// Constructor and Destructor
// Constructor will load the data from a file
VendingMachine() { load(); }; // Default constructor
VendingMachine(const std::string& fn) : fileName(fn) { load(); }; // Constructor + file name
// Destructor will automatically save product file
~VendingMachine() { save(); };
// Simple overwrite of extractor operator
friend std::istream& operator >> (std::istream& is, VendingMachine& vm) {
// Delete all existing products
vm.products.clear();
// Copy all data from stream into internal structure
std::copy(std::istream_iterator<Item>(is), {}, std::back_inserter(vm.products));
return is;
}
// Simple overwrite of extractor operator
friend std::ostream& operator << (std::ostream& os, const VendingMachine& vm) {
// Copy all data to stream
std::copy(vm.products.begin(), vm.products.end(), std::ostream_iterator<Item>(os, "\n"));
return os;
}
// Load file from file
void load() {
// Open file and check, if it could be opened
if (std::ifstream ifs(fileName); ifs) {
// Use existing extractor operator
ifs >> *this;
}
else std::cerr << "\n***Error: Could not open file '" << fileName << "' for reading\n";
}
// Save products to file
void save() {
// Open file and check, if it could be opened
if (std::ofstream ofs(fileName); ofs) {
// Use existing inserter operator
ofs << *this;
}
else std::cerr << "\n***Error: Could not open file '" << fileName << "' for writing\n";
}
// Show the complete content of the vending machine. Even if one product category quantity is 0
void displayContent() {
// Some header line
std::cout << "\nNumber of selections in vending machine: " << products.size() << "\n\nProducts:\n\n";
// All Items wit their attributes
for (const Item& item : products)
std::cout << item.productID << "\t Quantity: " << item.quantity << "\t Price: " << item.price << "\t --> " << item.name << '\n';
}
// Select an item and the decrease quatnity
void getItem() {
// COunt the number of overall items in the vending maschine
const unsigned long overallItemQuantity = std::accumulate(products.begin(), products.end(), 0UL, [](size_t sum, const Item& it) {return sum + it.quantity; });
// If there are at all products in the machine and not all item quantity is 0
if (products.size() && overallItemQuantity > 0UL ) {
// Instruction from user
std::cout << "\n\nGet item\nPlease select from below list:\n\n";
// Show list of possible selections
for (const Item& item : products) {
if (item.quantity > 0UL) std::cout << item.productID << " \tPrice " << item.price << " \t--> " << item.name << '\n';
}
// Get user input. What item does the user want to have
std::cout << "\n\nPlease select product by typing the ID: ";
if (std::string id{}; std::getline(std::cin, id)) {
// FInd the selected item in the product list
if (std::vector<Item>::iterator iter{ std::find_if(products.begin(), products.end(),[&id](const Item& i) {return i.productID == id && i.quantity > 0UL; }) };iter != products.end())
// In my example I do not handle payment. Simply decrease quantity
--iter->quantity;
else
std::cerr << "\n\n***Error: Unknown product ID\n"; // Wrong input
}
}
else std::cerr << "\n\n***Error: Vending machine empty\n";
}
// Run the machine. Main menu and actions. At the moment kust get items without payment
// Needs to be extended for real application
void run() {
// We run the main menu in a loop as long as the machine is active
bool active{ true };
while (active) {
// Show main menu
std::cout << "\n\n\nMain menu. Please select:\n 1 --> Get Item\n 0 --> Exit\n\nOption: ";
// Get user selection
unsigned int option; std::cin >> option;
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
// Depending on the user selected action
switch (option) {
case 0:
// Leave function.
active = false;
std::cout << "\n\nExiting . . .\n";
break;
case 1:
// Get an item
std::cout << "\n";
getItem();
break;
default:
std::cout << "\n\n\nError: Wrong selection. Please try again\n";
break;
}
}
}
};
int main() {
// Define a Vending Machine. Read data from disk
VendingMachine vendingMachine;
// SHow what is in initially
vendingMachine.displayContent();
// Run the machine
vendingMachine.run();
// Show, what is now in the machine
vendingMachine.displayContent();
// Destructor of vendingMachine will be called and file automatically saved
return 0;
}

Overload << operator to change " " to "\n"

I am trying to overload
<<
operator. For instance
cout << a << " " << b << " "; // I am not allowed to change this line
is given I have to print it in format
<literal_valueof_a><"\n>
<literal_valueof_b><"\n">
<"\n">
I tried to overload << operator giving string as argument but it is not working. So I guess literal
" "
is not a string. If it is not then what is it. And how to overload it?
Kindly help;
Full code
//Begin Program
// Begin -> Non - Editable
#include <iostream>
#include <string>
using namespace std;
// End -> Non -Editable
//---------------------------------------------------------------------
// Begin -> Editable (I have written )
ostream& operator << (ostream& os, const string& str) {
string s = " ";
if(str == " ") {
os << '\n';
}
else {
for(int i = 0; i < str.length(); ++i)
os << str[i];
}
return os;
}
// End -> Editable
//--------------------------------------------------------------------------
// Begin -> No-Editable
int main() {
int a, b;
double s, t;
string mr, ms;
cin >> a >> b >> s >> t ;
cin >> mr >> ms ;
cout << a << " " << b << " " ;
cout << s << " " << t << " " ;
cout << mr << " " << ms ;
return 0;
}
// End -> Non-Editable
//End Program
Inputs and outputs
Input
30 20 5.6 2.3 hello world
Output
30
20
5.6
2.3
hello
world
" " is a string-literal of length one, and thus has type const char[2]. std::string is not related.
Theoretically, you could thus overload it as:
auto& operator<<(std::ostream& os, const char (&s)[2]) {
return os << (*s == ' ' && !s[1] ? +"\n" : +s);
}
While that trumps all the other overloads, now things get really hairy. The problem is that some_ostream << " " is likely not uncommon, even in templates, and now no longer resolves to calling the standard function. Those templates now have a different definition in the affected translation-units than in non-affected ones, thus violating the one-definition-rule.
What you should do, is not try to apply a global solution to a very local problem:
Preferably, modify your code currently streaming the space-character.
Alternatively, write your own stream-buffer which translates it as you wish, into newline.
Sure this is possible, as I have tested. It should be portable since you are specifying an override of a templated function operator<<() included from <iostream>. The " " string in your code is not a std::string, but rather a C-style string (i.e. a const char *). The following definition works correctly:
ostream& operator << (ostream& os, const char *str) {
if(strcmp(str, " ") == 0) {
os << '\n';
} else {
// Call the standard library implementation
operator<< < std::char_traits<char> > (os, str);
}
return os;
}
Note that the space after std::char_traits<char> is necessary only if you are pre-c++11.
Edit 1
I agree with Deduplicator that this is a potentially dangerous solution as it may cause undesirable consequences elsewhere in the code base. If it is needed only in the current file, you could make it a static function (by putting it within an unnamed namespace). Perhaps if you shared more about the specifics of your problem, we could come up with a cleaner solution for you.
You might want to go with a user defined literal, e.g.
struct NewLine {};
std::ostream& operator << (std::ostream& os, NewLine)
{
return os << "\n";
}
NewLine operator ""_nl(const char*, std::size_t) // "nl" for newline
{
return {};
}
This can be used as follows.
int main(int, char **)
{
std::cout << 42 << ""_nl << "43" << ""_nl;
return 0;
}
Note three things here:
You can pass any string literal followed by the literal identifier, ""_nl does the same thing as " "_nl or "hello, world"_nl. You can change this by adjusting the function returning the NewLine object.
This solution is more of an awkward and confusing hack. The only real use case I can imagine is pertaining the option to easily change the behavior at a later point in time.
When doing something non-standard, it's best to make that obvious and explicit - here, the user defined literal indeed shines, because << ""_nl is more likely to catch readers' attention than << " ".

Print to file and console c++

I am trying to logg my events so i thought of using ostringstream to save the output and then send it to a function where i print the output on screen and on file fstream fileOut. It wont work, it just gives me random numbers and seem not to output all new outputs on the same file but just creates a new file everytime and deletes what was on it before. How do i do this?
where the print happens:
void Event::output(ostringstream* info) {
std::cout << info << std::endl;
fileOut << info << std::endl;
}
where the output happens:
ostringstream o;
if (time < SIM_TIME) {
if (status->tryAssemble(train)) {
Time ct;
ct.fromMinutes(time);
o << ct << " Train [" << train->getTrainNumber() << "] ";
Time t(0, DELAY_TIME);
o << "(ASSEMBLED) from " << train->getStart() << " " << train->getScheduledStartTime() <<
" (" << train->getStartTime() << ") to " << train->getDest() << " " << train->getScheduledDestTime() <<
" (" << train->getDestTime() << ") delay (" << train->getDelay() << ") speed=" << train->getScheduledSpeed() <<
" km/h is now assembled, arriving at the plateform at " << train->getStartTime() - t << endl << endl;
fileOut.open("testfile.txt", std::ios::out);
if (!fileOut.is_open())
exit(1); //could not open file
output(&o);
train->setStatus(ASSEMBLED);
time += ASSEMBLE_TIME;
Event *event = new ReadyEvent(simulation, status, time, train);
simulation->addEvent(event);
It wont work, it just gives me random numbers
You are passing the ostringstream to your function by pointer. There is no operator<< that takes an ostringstream* pointer as input and prints its string content. But there is an operator<< that takes a void* as input and prints the memory address that the pointer is pointing at. That is the "random numbers" you are seeing. ANY type of pointer can be assigned to a void* pointer.
You need to dereference the ostringstream* pointer to access the actual ostringstream object. Even so, there is still no operator<< that takes an ostringstream as input. However, ostringstream has a str() method that returns a std::string, and there is an operator<< for printing a std::string:
void Event::output(ostringstream* info) {
std::string s = info->str();
std::cout << s << std::endl;
fileOut << s << std::endl;
}
That being said, you should pass the ostringstream by const reference instead of by pointer, since the function does not allow a null ostringstream to be passed in, and it does not modify the ostringstream in any way:
void Event::output(const ostringstream &info) {
std::string s = info.str();
std::cout << s << std::endl;
fileOut << s << std::endl;
}
...
output(o);
seem not to output all new outputs on the same file but just creates a new file everytime and deletes what was on it before.
That is because you are not opening the file with either the app or ate flag 1, so it creates a new file each time, discarding the contents of any existing file. If you want to append to an existing file instead, you need to either:
use the ate flag to "seek to the end of stream immediately after open":
fileOut.open("testfile.txt", std::ios::out | std::ios::ate);
use the app flag to "seek to the end of stream before each write":
fileOut.open("testfile.txt", std::ios::out | std::ios::app);
1: if fileOut is a std::ofstream, you do not need to specify std::ios::out explicitly.

Create loop to write multiple files in C++?

Let's say I have a program that does the follow:
for (i=1; i<10; i++)
{
computeB(i);
}
where the computeB just outputs a list of values
computeB(int i)
{
char[6] out_fname="output";
//lines that compute `var` using say, Monte Carlo
string fname = out_fname + (string)".values";
ofstream fout(fname.c_str());
PrintValue(fout,"Total Values", var);
}
From another file:
template <class T>
void PrintValue(ofstream & fout, string s, T v) {
fout << s;
for(int i=0; i<48-s.size(); i++) {
fout << '.';
}
fout << " " << v << endl;
}
Before implementing that loop, computeB just outputted one file of values. I now want it to create multiple values. So if it originally created a file called "output.values", how can I write a loop so that it creates "output1.values", "output2.values", ..., "output9.values"?
EDIT: I forgot to mention that the original code used the PrintValue function to output the values. I originally tried to save space and exclude this, but I just caused confusion
Disregarding all the syntax errors in your code ...
Use the input value i to compute the output file name.
Use the file name to construct an ofstream.
Use the ofstream to write var to.
Here's what the function will look like:
void combuteB(int i)
{
char filename[100];
sprintf(filename, "output%d.values", i);
ofstream fout(filename);
fout << "total values";
fout << " " << var << endl; // Not sure where you get
// var from. But then, your
// posted code is not
// exactly clean.
}
You can use std::to_string() to convert from an int to a string:
void computeB(int i)
{
if (std::ofstream fout("output" + std::to_string(i) + ".values"))
fout << "total values" << " " << var << '\n';
else
throw std::runtime_error("unable to create output file");
}

Stringstream read values into double fails?

New Problem
boost::tokenizer<> token(line); tokenizes decimal points! How can I stop this happening?
Previous problem below is now resolved.
I am trying to grab values from a stringstream into a vector of doubles.
std::ifstream filestream;
filestream.open("data.data");
if(filestream.is_open()){
filestream.seekg(0, std::ios::beg);
std::string line;
std::vector<double> particle_state;
particle_state.resize(6);
while(filestream >> line){
boost::tokenizer<> token(line);
int i = -1;
for(boost::tokenizer<>::iterator it=token.begin(); it!=token.end(); ++it){
std::cout << *it << std::endl; // This prints the correct values from the file.
if(i == -1){
// Ommitted code
}
else{
std::stringstream ss(*it);
ss >> particle_state.at(i); // Offending code here?
}
i ++;
}
turbovector3 iPos(particle_state.at(0), particle_state.at(1), particle_state.at(2));
turbovector3 iVel(particle_state.at(3), particle_state.at(4), particle_state.at(5));
// AT THIS POINT: cout produces "(0,0,0)"
std::cout << "ADDING: P=" << iPos << " V=" << iVel << std::endl;
}
filestream.close();
}
Contents of input file:
electron(0,0,0,0,0,0);
proton(1,0,0,0,0,0);
proton(0,1,0,0,0,0);
More on turbovector3:
turbovector3 is a mathematical vector class. (The important thing is that it works - essentially it is a vector with 3 items. It is initialised using the constructor with three doubles.)
Thanks in advance for help!
EDIT Modification of code:
std::stringstream ss(*it);
if(ss.fail()){
std::cout << "FAIL!!!" << std::endl; // never happens
}
else{
std::cout << ss.str() << std::endl; // correct value pops out
}
double me;
ss >> me;
std::cout << "double:" << me << std::endl; // correct value pops out again
particle_state.at(i) = me; // This doesn't work - why?
Do you increment i in the omitted code? If not your else clause never gets called. Try outputting the stringstream buffer contents:
std::cerr << ss.str();
Also check if reading from ss actually fails:
if (ss.fail())
std::cerr << "Error reading from string stream\n";
Solution! I fluked and found this site: Link
The solution is to change the tokenizer to this:
boost::char_delimiters_separator<char> sep(false,"(),;");
boost::tokenizer<> token(line,sep);
Now it works!