Easiest way to load variables from a text file - c++

I have a program where I want to load Variables from a text file to use them as default variables.
The text file should look like this:
Name=No Name
Age=8
Gender=male
etc.
Is there a simpler way and if not how do I do that in the place with the question marks?
My Code look like this:
int Age;
std::string Name;
bool male;
if(f.is_open())
{
while (!f.eof())
{
getline(f, line);
if (line.find("Name=") == std::string::npos)
{
Name=?????;
continue;
}
else if (line.find("Gender=") == std::string::npos)
{
if(????? == "true"); then
male=true;
else
male=false;
continue;
}
else if (line.find("Age=") == std::string::npos)
{
Age=?????;
continue;
}
//etc. ...
}
f.close();

Is there a simpler way?
You could use a serialization library, like cereal or Boost, as #JesperJuhl suggested.
However, I would strongly suggest to take a step back, and review your approach. You are asking for an improvement, but you don't have a good solution at this point, because Why is iostream::eof inside a loop condition considered wrong?
As I had written here, I will use std::getline() as the loop condition instead of ios::eof(), in order to parse the file, line by line.
How do I do that in the place with the question marks?
Then, for every line, I will tokenize it, based on a delimiter (equal sign in your case), in order to extract two tokens, the name of the variable and its default value. Read more about it in Parse (split) a string in C++ using string delimiter (standard C++)
Afterwards, I would use an if-else approach (You could use a switch statement instead) to check the name of the variable, and assign its default value to the actual variables of the program.
Full code example:
#include <iostream>
#include <string>
#include <fstream>
int main(void) {
std::string defaultName, gender;
int age;
std::ifstream infile("mytextfile.txt");
std::string line, varName, defaultValue;
std::string delimiter = "=";
while (std::getline(infile, line)) {
varName = line.substr(0, line.find(delimiter));
defaultValue = line.substr(line.find(delimiter) + 1);
if(varName == "Name") {
defaultName = defaultValue;
continue;
} else if(varName == "Age") {
age = std::stoi(defaultValue);
continue;
} else if(varName == "Gender") {
gender = defaultValue;
continue;
} else {
std::cout << "Unknown entry: " << line << std::endl;
}
}
std::cout << defaultName << ", " << age << ", " << gender << std::endl;
return 0;
}
Output:
No Name, 8, male

If you feel a need to write it yourself instead of using a ready library, you could use a std::unordered_map<> and add some streaming and extraction support around it. Here's an example with comments in the code:
#include <string>
#include <unordered_map>
class KeyValue { // Key Value
std::unordered_map<std::string, std::string> m_kv{};
public:
// at() is used to get a reference to a Value given the supplied Key. It uses
// the function with the same name in the unordered_map.
inline std::string& at(const std::string& Key) { return m_kv.at(Key); }
inline const std::string& at(const std::string& Key) const { return m_kv.at(Key); }
// The "as<T>" function below is used to extract values from the map.
// The exact version of the function that will be used depends on the type
// you want to extract from the string. Explicit specializations of the function
// are declared outside the class.
// A generic conversion function to anything that can be constructed from a std::string
template<typename T>
T as(const std::string& Key) const {
return at(Key);
}
// A function to extract directly into a variable using the proper as<T>
template<typename T>
void extract_to(T& var, const std::string& Key) const {
var = as<T>(Key);
}
// A friend function to read from an input stream (like an open file) and
// populate the unordered_map.
friend std::istream& operator>>(std::istream&, KeyValue&);
};
// Explicit specializations of KeyValue::as<T>()
// floats
template<>
float KeyValue::as(const std::string& Key) const {
return std::stof(at(Key));
}
template<>
double KeyValue::as(const std::string& Key) const {
return std::stod(at(Key));
}
template<>
long double KeyValue::as(const std::string& Key) const {
return std::stold(at(Key));
}
// signed integers
template<>
int KeyValue::as(const std::string& Key) const {
return std::stoi(at(Key));
}
template<>
long KeyValue::as(const std::string& Key) const {
return std::stol(at(Key));
}
template<>
long long KeyValue::as(const std::string& Key) const {
return std::stoll(at(Key));
}
// unsigned integers
template<>
unsigned KeyValue::as(const std::string& Key) const {
return std::stoul(at(Key));
}
template<>
unsigned long KeyValue::as(const std::string& Key) const {
return std::stoul(at(Key));
}
template<>
unsigned long long KeyValue::as(const std::string& Key) const {
return std::stoull(at(Key));
}
// bool
template<>
bool KeyValue::as(const std::string& Key) const {
const std::string& val = at(Key);
if(val=="true" || val=="1") return true;
else if(val=="false" || val=="0") return false;
throw std::range_error("\"" + Key + "\" is neither true nor false");
}
// the friend function that extracts key value strings from a stream
std::istream& operator>>(std::istream& is, KeyValue& kv) {
std::string line;
// read one line at a time
while(std::getline(is, line)) {
auto pos = line.find('=');
if(pos == std::string::npos || pos == 0) {
// if '=' was not found (or found at pos 0), set the failbit on the stream
is.setstate(std::ios::failbit);
} else {
// if '=' was found, put the Key and Value in the map by
// using substr() to split the line where the '=' was found
kv.m_kv.emplace(line.substr(0, pos), line.substr(pos + 1));
}
}
return is;
}
With that in place, you can read a file and populate the variables that you've preferably put in a class / struct. Example:
#include <fstream>
struct Variables {
std::string Name{};
unsigned int Age{};
std::string Gender{};
double PI{};
bool Hungry{};
bool Sad{};
Variables(const std::string& filename) {
std::ifstream is(filename);
if(is) {
KeyValue tmp;
is >> tmp; // stream the whole file into tmp
// extract values
tmp.extract_to(Name, "Name");
tmp.extract_to(Age, "Age");
tmp.extract_to(Gender, "Gender");
tmp.extract_to(PI, "PI");
tmp.extract_to(Hungry, "Hungry");
tmp.extract_to(Sad, "Sad");
} else throw std::runtime_error("Could not read \""+filename+"\".");
}
};
Example data file (vars.dat):
Name=No name
Age=8
Gender=male
PI=3.14159
Hungry=true
Sad=false
...and a main example::
#include <iostream>
int main() {
try {
Variables var("vars.dat"); // open file and populate variables
std::cout << std::boolalpha
<< "Name: " << var.Name << "\n"
<< "Age: " << var.Age << "\n"
<< "Gender: " << var.Gender << "\n"
<< "PI: " << var.PI << "\n"
<< "Hungry: " << var.Hungry << "\n"
<< "Sad: " << var.Sad << "\n";
} catch(const std::exception& ex) {
std::cerr << ex.what() << "\n";
}
}

I tried to simplify the solution of #Ted Lyngmo:
... I think it is not the fastest way and not the best, but it is more simple and more short:
#include <sstream>
class loadVars
{
public:
std::string file;
loadVars() { }
//Input ->
loadVars(std::string Text) {
this->setFile(Text);
}
loadVars(std::istream& is) {
this->setFile(is);
}
friend void operator>>(std::istream& is, loadVars& lv) {
lv.file = std::string((std::istreambuf_iterator<char>(is)), std::istreambuf_iterator<char>());
}
void setFile(std::string Text) {
this->file = Text;
}
void setFile(std::istream& is) {
this->file = std::string((std::istreambuf_iterator<char>(is)), std::istreambuf_iterator<char>());
}
//<-
std::string extract_to_first(std::string to) {
std::string line;
std::stringstream s_string = std::stringstream(this->file);
while (std::getline(s_string, line)) {
if(line.find("=") != std::string::npos) {
if(line.substr(0,line.find("=")) == to) {
return line.substr(line.find("=")+1);
}
}
}
return "-1";
}
};

I would not reinvent this. As suggested, libraries for serialization exist. Consider Boost.PropertyTree as an example and Boost can be helpful to learn in general.

Related

How to swap strings?

I am trying to swap strings. For one of my functions of my class, I am passing in two strings and I also created a temp variable. I have been trying to compile my code, but it says "no suitable function for conversion from std::string to const char* exists.
void CdLib::swap(string *s1, string *s2)
{
string temp;
strcpy(temp, *s1);
strcpy(*s1, *s2);
strcpy(*s1, temp);
}
class CdLib
{
public:
int n;
char Cd[N_MAX];
string artist;
string title;
int year;
string genre;
string fan;
string imageURL;
CdLib();
void setFromFile(string fileName);
void print(string label);
void sortByYear();
void sortByArtist();
void sortByTitle(string genres[]);
private:
void swap(int *a, int *b);
void swapStrings(string *s1, string *s2);
};
I'm confused why it is trying to convert between string and char when they should all be string. Thank you.
strcpy() takes char* pointers, not string* pointers. It you are not allocating any memory for strcpy() to copy into.
Rather than using strcpy() at all, a better solution is to use std::string::operator= instead:
void CdLib::swap(string *s1, string *s2)
{
string temp = *s1;
*s1 = *s2;
*s1 = temp;
}
Or better, std::swap():
void CdLib::swap(string *s1, string *s2)
{
std::swap(*s1, *s2);
}
I am trying to swap strings
Why would you need to? Sorting can be accomplished via std::sort and you don't have to worry about how the strings get swapped - that's the beauty of C++, such basic operations are all implemented in the standard library.
std::swap supports pretty much everything, so use that.
Don't pass strings as arguments by value. Pass them by const reference. Return them by value. If a function is intended to modify a string in place, then it should take it by non-const reference (i.e. "just" a reference).
Don't write using namespace std - it's bad practice.
I guess that the CdLib class is some sort of a CD library, but you haven't told us what else your program should do.
If I were to write a sketch of this, I'd start with a structure representing the CD information, comparison functions for the CD that can be used in sorting, a function to print out the CD information, and a way to stream the CD information to/from an ostream/istream:
#include <algorithm>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
struct CDInfo
{
std::string artist;
std::string title;
std::string genre;
std::string fan;
std::string imageUrl;
int year;
friend void swap(CDInfo& a, CDInfo& b)
{
// see https://stackoverflow.com/a/2684544/1329652 for rationale
using std::swap; // bring in swap for built-in types
swap(a.artist, b.artist);
swap(a.title, b.title);
swap(a.genre, b.genre);
swap(a.fan, b.fan);
swap(a.imageUrl, b.imageUrl);
swap(a.year, b.year);
}
};
bool lessByYear(const CDInfo &l, const CDInfo &r) {
return l.year < r.year;
}
bool lessByArtist(const CDInfo &l, const CDInfo &r) {
return l.artist < r.artist;
}
void print(std::ostream &os, const CDInfo &cd) {
os << "Artist: " << cd.artist
<< "\n Title: " << cd.title
<< "\n Genre: " << cd.genre
<< "\n Fan: " << cd.fan
<< "\n Image: " << cd.imageUrl
<< "\n Year: " << cd.year << "\n";
}
std::istream &operator>>(std::istream &is, CDInfo &cd)
{
std::string year;
std::getline(is, cd.artist);
std::getline(is, cd.title);
std::getline(is, cd.genre);
std::getline(is, cd.fan);
std::getline(is, cd.imageUrl);
if (std::getline(is, year)) cd.year = std::stoi(year);
return is;
}
std::ostream &operator<<(std::ostream &os, const CDInfo &cd)
{
os << cd.artist << '\n' << cd.title << '\n'
<< cd.genre << '\n' << cd.fan << '\n'
<< cd.imageUrl << '\n' << cd.year << '\n';
return os;
}
Then I'd write a class representing the CD library, with methods to access the individual CDs, iterators to access the entire collection, methods using the std::sort algorithm and the comparison functions to sort the library, and methods to load/save it from/to file, and to print the entire library (by default to stdout):
class CDLibrary
{
std::vector<CDInfo> m_CDs;
public:
CDLibrary() = default;
int count() const { return m_CDs.size(); }
void resize(int newCount) { m_CDs.resize(newCount); }
CDInfo &getCD(int index) { return m_CDs[index]; }
const CDInfo &getCD(int index) const { return m_CDs[index]; }
auto begin() { return m_CDs.begin(); }
auto end() { return m_CDs.end(); }
auto begin() const { return m_CDs.begin(); }
auto end() const { return m_CDs.end(); }
auto cbegin() const { return m_CDs.begin(); }
auto cend() const { return m_CDs.end(); }
void sortByYear() {
std::sort(begin(), end(), lessByYear);
}
void sortByArtist() {
std::sort(begin(), end(), lessByArtist);
}
void addCD(const CDInfo &cd) {
m_CDs.push_back(cd);
}
void removeCD(int index) {
m_CDs.erase(m_CDs.begin() + index);
}
bool load(const std::string &filename);
bool save(const std::string &filename) const;
void printAll(std::ostream &os = std::cout) const {
int n = 1;
for (auto &cd : *this) {
os << "--- CD #" << n << '\n';
print(os, cd);
}
}
};
Of course I'd also implement the streaming operators for the entire library, just as we did for the individual CDInfo:
std::istream &operator>>(std::istream &is, CDLibrary &lib) {
std::string count;
if (std::getline(is, count)) {
lib.resize(std::stoi(count));
for (auto &cd : lib)
if (!(is >> cd)) break;
}
return is;
}
std::ostream &operator<<(std::ostream &os, const CDLibrary &lib) {
if (!(os << lib.count() << '\n')) return os;
for (auto &cd : lib)
if (!(os << cd)) break;
return os;
}
Then, the load and save convenience methods can be expressed n terms of those streaming operators:
bool CDLibrary::load(const std::string &filename) {
std::ifstream ifs(filename);
try {
return ifs.good() && ifs >> *this;
} catch (...) {}
return false;
}
bool CDLibrary::save(const std::string &filename) const {
std::ofstream ofs(filename);
return ofs.good() && ofs << *this;
}
Hopefully this gives you some idea how such code might look. I'm not quite sure what you expected to achieve with void sortByTitle(string genres[]), so I didn't implement it. Feel free to comment under this answer to explain, as well as edit the question to make it clear what is the functionality you need.
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
int main() {
string str1 = "Hello";
string str2 = "World";
swap(str1,str2);
cout<<str1<<" ";
cout<<str2;
}
o/p:
Success #stdin #stdout 0s 4492KB
World Hello

How to replace a string with another string in csv file using C++

I am opening a csv file which contains entries in the form "key,value".
Task is to find a particular key and modify its corresponding value with another string. (key & value : both are strings).
int main()
{
std::fstream m_file;
m_file.open("input.csv", std::ios::in | std::ios::out);
m_file << "Star,Treks\n";
m_file << "Captain,America\n";
m_file << "Black,Cats\n";
m_file << "Ninja,Fighters\n";
std::string row;
std::string key = "Black";
std::string value = "Dreamers";
while (std::getline(m_file, row)) {
std::size_t pos = row.find(',');
std::string retrieved_key = row.substr(0, pos);
std::string retrieved_value = row.substr(pos + 1);
if (retrieved_key == key) {
std::size_t currentPos = m_file.tellg();
std::size_t row_length = row.length();
m_file.seekp(currentPos - row_length);
std::string newEntry = key + "," + value + "\n";
m_file << newEntry;
return 0;
}
}
getchar();
return 0;
}
This is the code I have written. But it does not work. It modifies the contents of the file as below :
Star,Treks
Captain,America
BlBlack,Dreamers
Fighters
You cannot modify files directly using seekg() and tellg() functions unless these have a fixed size format of the parts you want to change.
The usual way to do such things is to create an intermediary temp file to write your changes, and replace the original one with it after you're done.
Recipe:
#include <cstdlib>
#include <iostream>
#include <utility>
#include <string>
#include <fstream>
#include <vector>
#include <iterator>
#include <algorithm>
class pair : public std::pair<std::string, std::string>
{
public:
using std::pair<std::string, std::string>::pair;
bool operator==(pair const & rhs) const { return first == rhs.first; }
friend std::istream& operator>>(std::istream &is, pair &p)
{
std::getline(is, p.first, ',');
std::getline(is, p.second, '\n');
return is;
}
friend std::ostream& operator<<(std::ostream &os, pair const &p)
{
os << p.first << ',' << p.second << '\n';
return os;
}
};
int main()
{
char const *filename = "input.csv";
std::fstream file{ filename, std::ios::in };
if (!file) {
std::cerr << "Failed to open file \"" << filename << "\"!\n\n";
return EXIT_FAILURE;
}
std::vector<pair> data{
std::istream_iterator<pair>{ file },
std::istream_iterator<pair>{}
};
file.close();
auto it{ std::find(data.begin(), data.end(), pair{ "Black", "" }) };
if (it == data.end()) {
std::cerr << "Key not found!\n\n";
return EXIT_FAILURE;
}
it->second = "Dreamers";
file.open(filename, std::ios::out | std::ios::trunc);
if (!file) {
std::cerr << "Failed to open file \"" << filename << "\"!\n\n";
return EXIT_FAILURE;
}
for (auto const & entry : data)
file << entry;
}

C++ How to load a textfile and adding the content to a class?

I am trying to learn how to use classes and I figured I'd create some sort of supermarket system to aid me with learning. After I have saved all the values from my text file into the temp variables, how do I then use them to create an object? I assume I want one object per item you can "buy"?
If you have any other tips on how to improve my code, please mention them as I just started with C++ a few days ago.
My text file looks like:
42 68 Apples
35 1 Oranges
70 25 Bananas
And my code is below:
// Classes.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <vector>
class Products {
private:
int price;
int ID;
int quantity;
public:
void setPrice(int newPrice) {
price = newPrice;
}
void setID(int newID) {
ID = newID;
}
void setQuantity(int newQuantity) {
quantity = newQuantity;
}
int getPrice() {
return price;
}
int getID() {
return ID;
}
int getQuantity() {
return quantity;
}
};
int main()
{
std::string line;
std::string input;
std::string temp;
std::string temp2;
std::string temp3;
int counter = 0;
while (input != "e" && input != "r") {
std::cout << "Do you want to (r)ead the inventory or (e)dit it? (input 'r' or 'e'): ";
getline(std::cin, input);
}
if (input == "r") {
std::ifstream pFile ("products.txt");
if (pFile.is_open()) {
while (getline(pFile, line)) {
std::istringstream iss(line);
iss >> temp >> temp2 >> temp3;
counter++;
}
}
}
return 0;
}
Looking at the text file that you are using as an example and trying to populate a set of classes with the information I'd do something like this:
Text File
42 68 Apples
35 1 Oranges
70 25 Bananas
Looking at the text file you have an int followed by space then another int followed by another space finally followed by a varying size of char[]. We can use this information to create your class or struct and this is how I would create the class based off of the content from the file that is being read in or parsed.
Produce.h
#ifndef PRODUCE_H
#define PRODUCE_H
#include <string>
class Produce {
private:
unsigned price_; // Normally would use float but will use unsigned for simplicity
unsigned quantity_;
std::string name_; // You could use the int as an id as you were using
public:
Produce() {} // Default Constructor
~Produce() {} // Default Destructor
// User Constructor
Produce(const std::string& name, const unsigned& price, const unsigned& qty )
: name_(name), price_(price), quantity_( qty ) {}
// Copy By Const Reference Constructor
Produce(const Produce& other) {
name_ = other.name_;
price_ = other.price_;
quantity_ = other.quantity_;
}
// Assignment Operator
Produce& operator=(const Produce& other) {
name_ = other.name_;
price_ = other.price_;
quantity_ = other.quantity_;
return *this;
}
// Setters & Getters
void setOrChangePrice(const unsigned& priceChange) {
price_ = priceChange;
}
unsigned getPrice() const { return price_; }
void setName(const std::string& name) {
// Already Has A Name? Return!
if (!name_.empty())
return;
}
std::string getName() const { return name_; }
void setOrChangeQuantity(const unsigned& qty) {
quantity_ = qty;
}
unsigned getQuantity() const {
return quantity_;
}
};
#endif // PRODUCE_H
Produce.cpp
#include "Produce.h"
// Normally Constructor(s) & Function Definitions Would Go Here
// Since this is a simple class; I declared & defined them in the header.
Inventory.h
#ifndef INVENTORY_H
#define INVENTORY_H
#include <vector>
#include "Produce.h"
class Inventory {
private:
std::vector<Produce> produce_;
public:
Inventory() {}
~Inventory() {
if (!produce_.empty()) {
produce_.clear();
}
}
std::vector<Produce> getProduceList() const {
return produce_;
}
unsigned getProduceListSize() const {
return static_cast<unsigned>( produce_.size() );
}
void addProduce(const Produce& produce) {
produce_.push_back(produce);
}
Produce getProduce(const std::string& name) const {
for (unsigned n = 0; n < produce_.size(); ++n) {
if (name == produce_[n].getName()) {
return produce_[n];
} else {
return Produce();
}
}
}
unsigned getPrice(const std::string& name) const {
for (unsigned n = 0; n < produce_.size(); ++n) {
if (name == produce_[n].getName()) {
return produce_[n].getPrice();
} else {
return 0;
}
}
}
unsigned getQuantity( const std::string& name ) const {
for (unsigned n = 0; n < produce_.size(); ++n) {
if (name == produce_[n].getName()) {
return produce_[n].getQuantity();
} else {
return 0;
}
}
}
};
#endif // INVENTORY_H
Inventory.cpp
#include "Inventory.h"
Main.cpp
// #include <vector> // Also Included In Inventory.h
// #include <string> // Also Included In Produce.h
#include <iostream>
#include <fstream>
// #include "Produce.h" // Also Included In Inventory.h
#include "Inventory.h"
int main( ) {
// Same As Your Text File
std::string strFilename("ProduceInventory.txt");
std::ifstream fileIn;
// Temps
unsigned price = 0;
unsigned qty = 0;
std::string name;
Inventory inventory;
fileIn.open(strFilename.c_str());
if (!fileIn.is_open()) {
std::cout << "Can not read file\n";
}
while (fileIn >> price >> qty >> name) {
Produce produce(name, price, qty);
inventory.addProduce(produce);
}
if ( fileIn.is_open() ) {
fileIn.close();
}
// Test Our Inventory From File
for (unsigned n = 0; n < inventory.getProduceListSize(); n++) {
std::cout << "Name: " << inventory.getProduceList()[n].getName() << " "
<< "Price: " << inventory.getProduceList()[n].getPrice() << " "
<< "Qty: " << inventory.getProduceList()[n].getQuantity() << "\n";
}
std::cout << std::endl;
// Check To See If Our Search Method Works
std::cout << "Enter a product type by name to get its price and qty on hand.\n";
name.clear(); // reuse
std::cin >> name;
Produce p = inventory.getProduce(name);
if (p.getName().empty()) {
std::cout << "We either do not carry this produce or we are out of stock\n";
} else {
std::cout << "Our price is " << p.getPrice() << " and we have " << p.getQuantity() << " on hand\n";
// Or
std::cout << "Our price is " << inventory.getPrice(name) << " and we have " << inventory.getQuantity(name) << " on hand\n";
}
return 0;
}
Now this is just one of many ways this can be done; but also make sure that your text file is in the appropriate location of the working directory for your IDE or that you specify the correct path along with the filename and extension to be able to open and read from the file in the first place.
Much of this code can be fined even more; but I was showing how to use the std::vector<T> container and some of its functions and how to iterate through them and to retrieve its data. This is a basic way of parsing a simple text file when you know the exact format of the text file and for each and every line you will have the same data types in the same order.
This will not work for every kind of file parsing because it depends on the file's format. For example another file might have different kind of data from one line to the next and might have keywords or tags to give a description of what kind of data is coming next. For that kind of parsing you would have to read the entire line in first into a string stream then you would have to be able to parse that string stream via tokens to extract the data. Some times parsing a file might have multiple lines that belong to a data set and for that you would have to parse by chunks or blobs.
Parsing text files is harder to parse than binary file formats because you have to check each line and character of the text and you also have to be cautious
of character returns, new line characters etc. With binary format you just have to know how many bytes to read in and what type of data it is expected to be. There are many books that are written on just this topic alone on how to parse data from a file. There is no one simple way that fits all.

Read every word in a string C++

I am trying to read every word a string. I want a string to go in and the first word to come out, then I'll process it, then the second, and so on. But the internet isn't helping me, I know it's probably right under my nose but I can't figure it out!
string lex(string filecontent) {
string t = filecontent;
getline(cin, t);
istringstream iss(t);
string word;
while (iss >> word) {
return word;
}
}
int main() {
string data = load_file(); // Returns a string of words
cout << data;
cout << lex(data);
getchar();
}
Right now this works... sort of it prints out a lot of random gibberish and crazy characters, The file I'm reading's output is ok I check this at cout << data and it is what I expect. Any ideas?
Here is the solution I think you are looking for:
int main() {
string data = load_file(); // Returns a string of words
istringstream iss(data);
while(iss)
{
string tok;
iss >> tok;
cout << "token: " << tok << endl;
//you can do what ever you want with the token here
}
}
Have a look at this, it should help you.
main.cpp
#include "stdafx.h"
#include "Utility.h"
int main() {
using namespace util;
std::string fileName( "sample.txt" );
if ( fileName.empty() ) {
std::cout << "Missing or invalid filename." << std::endl;
return RETURN_ERROR;
}
std::string line;
std::vector<std::string> results;
std::fstream fin;
// Try To Open File For Reading
fin.open( fileName.c_str(), std::ios_base::in );
if ( !fin.is_open() ) {
std::cout << "Can not open file(" << fileName << ") for reading." << std::endl;
return RETURN_ERROR;
}
// Read Line By Line To Get Data Contents Store Into String To Be Parsed
while ( !fin.eof() ) {
std::getline( fin, line );
// Parse Each Line Using Space Character As Delimiter
results = Utility::splitString( line, " " );
// Print The Results On Each Iteration Of This While Loop
// This Is Where You Would Parse The Data Or Store Results Into
// Class Objects, Variables Or Structures.
for ( unsigned u = 0; u < results.size(); u++ ) {
std::cout << results[u] << " ";
}
std::cout << std::endl;
}
// Close File Pointer
fin.close();
// Now Print The Full Vector Of Results - This Is To Show You That Each
// New Line Will Be Overwritten And That Only The Last Line Of The File Will
// Be Stored After The While Loop.
std::cout << "\n-------------------------------------\n";
for ( unsigned u = 0; u < results.size(); u++ ) {
std::cout << results[u] << " ";
}
Utility::pressAnyKeyToQuit();
return RETURN_OK;
} // main
sample.txt
Please help me parse this text file
It spans multiple lines of text
I would like to get each individual word
stdafx.h - Some of these include files may not be needed they are here for I have a larger solution that requires them.
#ifndef STDAFX_H
#define STDAFX_H
#include <Windows.h>
#include <stdio.h>
#include <tchar.h>
#include <conio.h>
#include <string>
#include <sstream>
#include <fstream>
#include <iostream>
#include <iomanip>
#include <vector>
#include <array>
#include <memory>
#include <queue>
#include <functional>
#include <algorithm>
// User Application Specific
// #include "ExceptionHandler.h" - One Of My Class Objects Not Used Here
namespace util {
enum ReturnCode {
RETURN_OK = 0,
RETURN_ERROR = 1,
}; // ReturnCode
extern const unsigned INVALID_UNSIGNED;
extern const unsigned INVALID_UNSIGNED_SHORT;
} // namespace util
#endif // STDAFX_H
stdafx.cpp
#include "stdafx.h"
namespace util {
const unsigned INVALID_UNSIGNED = static_cast<const unsigned>( -1 );
const unsigned INVALID_UNSIGNED_SHORT = static_cast<const unsigned short>( -1 );
} // namespace util
Utility.h
#ifndef UTILITY_H
#define UTILITY_H
namespace util {
class Utility {
public:
static void pressAnyKeyToQuit();
static std::string toUpper(const std::string& str);
static std::string toLower(const std::string& str);
static std::string trim(const std::string& str, const std::string elementsToTrim = " \t\n\r");
static unsigned convertToUnsigned(const std::string& str);
static int convertToInt(const std::string& str);
static float convertToFloat(const std::string& str);
static std::vector<std::string> splitString(const std::string& strStringToSplit, const std::string& strDelimiter, const bool keepEmpty = true);
private:
Utility(); // Private - Not A Class Object
Utility(const Utility& c); // Not Implemented
Utility& operator=(const Utility& c); // Not Implemented
template<typename T>
static bool stringToValue(const std::string& str, T* pValue, unsigned uNumValues);
template<typename T>
static T getValue(const std::string& str, std::size_t& remainder);
}; // Utility
#include "Utility.inl"
} // namespace util
#endif // UTILITY_H
Utility.inl
// ----------------------------------------------------------------------------
// stringToValue()
template<typename T>
static bool Utility::stringToValue(const std::string& str, T* pValue, unsigned uNumValues) {
int numCommas = std::count(str.begin(), str.end(), ',');
if (numCommas != uNumValues - 1) {
return false;
}
std::size_t remainder;
pValue[0] = getValue<T>(str, remainder);
if (uNumValues == 1) {
if (str.size() != remainder) {
return false;
}
}
else {
std::size_t offset = remainder;
if (str.at(offset) != ',') {
return false;
}
unsigned uLastIdx = uNumValues - 1;
for (unsigned u = 1; u < uNumValues; ++u) {
pValue[u] = getValue<T>(str.substr(++offset), remainder);
offset += remainder;
if ((u < uLastIdx && str.at(offset) != ',') ||
(u == uLastIdx && offset != str.size()))
{
return false;
}
}
}
return true;
} // stringToValue
Utility.cpp
#include "stdafx.h"
#include "Utility.h"
namespace util {
// ----------------------------------------------------------------------------
// pressAnyKeyToQuit()
void Utility::pressAnyKeyToQuit() {
std::cout << "\nPress any key to quit" << std::endl;
_getch();
} // pressAnyKeyToQuit
// ----------------------------------------------------------------------------
// toUpper()
std::string Utility::toUpper( const std::string& str ) {
std::string result = str;
std::transform( str.begin(), str.end(), result.begin(), ::toupper );
return result;
} // toUpper
// ----------------------------------------------------------------------------
// toLower()
std::string Utility::toLower( const std::string& str ) {
std::string result = str;
std::transform( str.begin(), str.end(), result.begin(), ::tolower );
return result;
} // toLower
// ----------------------------------------------------------------------------
// trim()
// Removes Elements To Trim From Left And Right Side Of The str
std::string Utility::trim( const std::string& str, const std::string elementsToTrim ) {
std::basic_string<char>::size_type firstIndex = str.find_first_not_of( elementsToTrim );
if ( firstIndex == std::string::npos ) {
return std::string(); // Nothing Left
}
std::basic_string<char>::size_type lastIndex = str.find_last_not_of( elementsToTrim );
return str.substr( firstIndex, lastIndex - firstIndex + 1 );
} // trim
// ----------------------------------------------------------------------------
// getValue()
template<>
float Utility::getValue( const std::string& str, std::size_t& remainder ) {
return std::stof( str, &remainder );
} // getValue <float>
// ----------------------------------------------------------------------------
// getValue()
template<>
int Utility::getValue( const std::string& str, std::size_t& remainder ) {
return std::stoi( str, &remainder );
} // getValue <int>
// ----------------------------------------------------------------------------
// getValue()
template<>
unsigned Utility::getValue( const std::string& str, std::size_t& remainder ) {
return std::stoul( str, &remainder );
} // getValue <unsigned>
// ----------------------------------------------------------------------------
// convertToUnsigned()
unsigned Utility::convertToUnsigned( const std::string& str ) {
unsigned u = 0;
if ( !stringToValue( str, &u, 1 ) ) {
std::ostringstream strStream;
strStream << __FUNCTION__ << " Bad conversion of [" << str << "] to unsigned";
throw strStream.str();
}
return u;
} // convertToUnsigned
// ----------------------------------------------------------------------------
// convertToInt()
int Utility::convertToInt( const std::string& str ) {
int i = 0;
if ( !stringToValue( str, &i, 1 ) ) {
std::ostringstream strStream;
strStream << __FUNCTION__ << " Bad conversion of [" << str << "] to int";
throw strStream.str();
}
return i;
} // convertToInt
// ----------------------------------------------------------------------------
// convertToFloat()
float Utility::convertToFloat(const std::string& str) {
float f = 0;
if (!stringToValue(str, &f, 1)) {
std::ostringstream strStream;
strStream << __FUNCTION__ << " Bad conversion of [" << str << "] to float";
throw strStream.str();
}
return f;
} // convertToFloat
// ----------------------------------------------------------------------------
// splitString()
std::vector<std::string> Utility::splitString( const std::string& strStringToSplit, const std::string& strDelimiter, const bool keepEmpty ) {
std::vector<std::string> vResult;
if ( strDelimiter.empty() ) {
vResult.push_back( strStringToSplit );
return vResult;
}
std::string::const_iterator itSubStrStart = strStringToSplit.begin(), itSubStrEnd;
while ( true ) {
itSubStrEnd = search( itSubStrStart, strStringToSplit.end(), strDelimiter.begin(), strDelimiter.end() );
std::string strTemp( itSubStrStart, itSubStrEnd );
if ( keepEmpty || !strTemp.empty() ) {
vResult.push_back( strTemp );
}
if ( itSubStrEnd == strStringToSplit.end() ) {
break;
}
itSubStrStart = itSubStrEnd + strDelimiter.size();
}
return vResult;
} // splitString
} // namspace util
In my small utility library I have a function that will split a string that can use any delimiter that the user defines. It will search for the first occurrence of that character delimiter and it will save everything before it into a string and it will push that string into a vector of strings, and it will continue this for every occurrence of that character until it is finished parsing the full string that is passed to it. It will then return a vector of strings back to the user. This is very helpful when engaged in parsing text files or even just data types with long strings that need to be broken down. Now if there is a case where you are parsing a text file and lets say you need to have more than one word as a single string, this can be done but requires more work on your part. For example a text file might have personal record on a single line.
LastName, FirstName MiddleInitial Age Phone# Address
Cook, John S 33 1-888-323-4545 324 Complex Avenue
And you would want the 324 Complex Avenue to be in a single string also you don't want the comma stored after the last name. Your structure in code to store this info might look like this:
struct PersonalRecord {
std::string firstName;
std::string lastName;
char middleInitial;
unsigned age;
std::string phoneNumber;
std:string address;
};
What you would have to do is after you read this line in from your file on that same iteration of the while loop is you would have to do multiple parsing.
You would first start by using a temporary string and vector of strings and use the utility function splitString with the delimeter being the comma. So this would save 2 strings in the temp vector of strings the first being: Cook and the second being the rest of the line after the comma including the leading space. The reason you have the temp string and temp vector of strings is that you will need to pop values at when needed. So in this case we would have to do the following, but first how do we resolve the case with multiple words to one string? We can change the line of text in the text file to be enclosed with double quotes as such:
textfile
Cook, John S 33 1-888-323-4545 "324 Complex Avenue"
Evens, Sue A 24 1-888-323-6996 "128 Mission Rd"
Adams, Chris B 49 1-777-293-8234 "2304 Helms Drive"
Then parse it with this logic flow or algorithm.
main.cpp
#including "stdafx.h"
#including "Utility.h"
int main() {
using namespace util;
std::string strFilename( "personalRecord.txt" );
std::ifstream file;
std::string strLine;
std::vector<std::string> vTemp;
std::vector<std::string> vResult;
std::vector<PersonalRecord> vData;
// Open File For Reading
file.open( strFilename.c_str() );
// Check For Error Of Opening File
if ( !file.is_open() ) {
std::cout << "Error opening file (" << strFilename << ")" << std::endl;
return RETURN_ERROR;
}
// Continue Until End Of File
while( !file.eof() ) {
// Get Single Full Line Save To String
std::getline( file, strLine );
// Check For Comma
vTemp = Utility::splitString( strLine, ",");
// Save First String For Laster
std::string lastName = vTemp[0];
// Split String Using A Double Quote Delimiter Delimiter
vTemp = Utility::splitString( vTemp[1], "\"" );
// Check To See If vTemp Has More Than One String
if ( vTemp.size() > 1 ) {
// We Need To Use Pop Back To Account For Last Double Quote
vTemp.pop_back(); // Remove Last Double Quote
std::string temp = vTemp.back();
vTemp.pop_back(); // Remove Wanted String From vTemp.
// At This Point We Need To Parse vTemp Again Using Space Delimiter
vResult = Utility::splitString( vTemp[0], " " );
// Need To Account For Leading Space In Vector
vResult[0].erase();
// Need To Account For Last Space In Vector
vResult.pop_back();
// Now We Can Push Our Last String Back Into vResult
vResult.push_back( temp );
// Replace The First String " " With Our LastName
vResult[0] = lastName;
} else if ( vTemp.size() == 1 ) {
// Just Parse vTemp Using Space Delimiter
vResult = Utility::splitString( vTemp[0], " " );
}
// Print Out Results For Validity
for ( unsigned u = 0; u < vResult.size(); u++) {
std::cout << vResult.at(u) << " ";
}
std::cout << std::endl;
// Here Is Where You Would Populate Your Variables, Structures Or Classes On Each Pass Of The While Loop.
// With This Structure There Should Only Be 8 Entries Into Our vResult
PersonalRecord temp;
temp.lastName = vResult[0];
temp.firstName = vResult[1];
temp.middleInitial = vResult[2][0];
temp.age = Utility::convertToUnsigned( vResult[3] );
temp.phoneNumber = vResult[4];
temp.address = vResult[5];
vData.push_back( temp );
} // while
// Close File
file.close();
std::cout << std::endl << std::endl;
// Print Using Structure For Validity
std::cout << "---------------------------------------\n";
for ( unsigned u = 0; u < vData.size(); u++ ) {
std::cout << vData[u].lastName << " "
<< vData[u].firstName << " "
<< vData[u].middleInitial << " "
<< vData[u].age << " "
<< vData[u].phoneNumber << " "
<< vData[u].address << std::endl;
}
Utility::pressAnyKeyToQuit();
return RETURN_OK;
} // main
So both consideration and are has to be taken when parsing text or strings. You have to account for every single character including your carriage returns, spaces etc. So the format that the text file is written in has to be considered.
Yes the splitString() will also parse tabs, you would just have to use "\t" for tabs, etc. Just remember that it will make a split at every occurrence. So if you have a sentence that has a colon ":" in it, but then you decide to use the colon as your delimiter between values, it will split that sentence as well. Now you could have different rules for each line of text from the file and if you know what line you are on you can parse each line accordingly. This is why most people prefer to write their code to read and parse binary, because it is much easier to program, then writing a text parser.
I chose to use the PersonalRecord structure to show you how you can extract strings from a line of text and to convert them to basic types such as int, float or double by using some of my other functions in my Utility class. All methods in this class are declared as static and the constructor is private, so the class name acts as a wrapper or a namespace so to speak. You can not create an instance of a Utility util; // invalid object. Just include the header file and use the class name with the scope resolution operator :: to access any of the functions and make sure you are using the namespace util.

Using push_back for vector in c++

I am having trouble using push_back for vectors in c++.
My vector is named data.
In my loop I want to add 50 to data[i].getQuantity then push_back to data
These are things that I have tried.
data.push_back(data[i].getQuantity());
and
float myFloat = data[i].getQuantity() + 50;
data.push_back(data[i].getQuantity(myFloat));
data.push_back(myFloat);
The error is saying
No function to call to push_back
Here is my code:
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <iterator>
struct Input
{
friend std::istream& operator >>(std::istream& inp, Input& item);
friend std::ostream& operator <<(std::ostream& outp, Input const& item);
std::string group;
std::string total_pay;
float quantity;
// default constructor. sets up zero-elements
Input() : group(), total_pay(), quantity()
{
}
Input(std::string groupIn, std::string total_payIn, float quantityIn) :
group(std::move(groupIn)),
total_pay(total_payIn),
quantity(quantityIn)
{
}
// Accessors
std::string const& getGroup() const { return group; }
std::string getTotalPay() const { return total_pay; }
float 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;
}
struct ctype : std::ctype<char>
{
static mask* make_table()
{
static std::vector<mask> table(classic_table(),
classic_table() + table_size);
table[','] |= space;
return &table[0];
}
ctype() : std::ctype<char>(make_table()) { }
};
int main() {
std::fstream infile("employee.dat");
std::vector<Input> data;
std::string line;
try {
while (std::getline(infile, line))
{
std::istringstream iss(line);
Input inp;
iss.imbue(std::locale(iss.getloc(), new ctype));
while (iss >> inp) // calls our extraction operator >>
data.push_back(inp);
if (iss.fail() && !iss.eof())
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"));
std::ofstream outp("output.dat");
for(int i = 0; i < data[i].getQuantity(); i++)
{
float myFloat = data[i].getQuantity() + 50;
data.push_back(myFloat);
outp << data[i].getGroup() << ',' << data[i].getTotalPay() << ',' << data[i].getQuantity() + 50 << '\n';
}
} catch (std::exception& e) {
std::cout << "There was an error: " << '\n';
return 1;
}
return 0;
}
Your vector is of type std::vector<Input>. That means you can only put objects of type Input into it. You can't push_back a float into such a vector.
If your intention is to create a new Input object and push that back into your vector, you could do something like
data.push_back(Input(data[i].getGroup(), data[i].getTotalPay(), data[i].getQuantity() + 50))
On the other hand, if you are simply trying to modify an element in data without adding a new element to data, you could just do
data[i].quantity += 50;
This works because you use a struct rather than a class. In a struct, variables default privacy level is public. If you wanted to use a class, or you just don't want to directly access the struct members, you would have to create a setter function for quantity.