I have an std::vector and the function expects an std::istream:
callMe(std::istream& is)
What is the best way to do the conversion? Is there something more clever than?
std::stringstream sstr;
for(int i = 0; i < myVector.size(); ++i) {
sstr << myVector[i] << " ";
}
std::istringstream istr{sstr.str()};
callMe(istr);
EDIT: Thanks for the suggestions so far! Updated code:
std::stringstream sstr;
for(const float& val : myVector) {
sstr << val << " ";
}
callMe(sstr);
The issue is that std::istream is inherently character-based. If you want to keep using callMe(std::istream& is) as an interface, you are bound to convert every element of myVector to characters and back at some point. If you want to stick with this option, I personally find ostream_iterator an elegant solution:
copy(begin(data), end(data), std::ostream_iterator<float>(sstr));
Full example:
void callMeStream(std::istream &is)
{
float f1;
is >> f1;
std::cout << "Stream: " << f1 << std::endl;
}
// ...
std::vector<float> data = {3.5, 1.5, 2.5 /* ... */};
std::stringstream sstr;
copy(begin(data), end(data), std::ostream_iterator<float>(sstr));
callMeStream(sstr); // Output: "Stream: 3.51"
If you are willing to change the signature of callMe, this conversion can be avoided:
template <class T>
void callMeTemplate(T &is)
{
float f1;
is >> f1;
std::cout << "Template: " << f1 << std::endl;
}
#define NO_ELEMENT -1.0;
class data_wrapper
{
std::vector<float>::const_iterator current;
const std::vector<float>::const_iterator last;
public:
data_wrapper(const std::vector<float> &data) : current(begin(data)), last(end(data)) {}
data_wrapper &operator>>(float &value)
{
if (last == current)
{
value = NO_ELEMENT;
}
else
{
value = *current++;
}
return *this;
}
};
// ...
data_wrapper wrapper(data);
callMeTemplate(wrapper); // Output: "Template: 3.5"
In the second example, the float value never gets converted to a character sequence, and could accept both data_wrapper and std::istream types. Of course, if you are willing to change the signature of callMe entirely, you might as well change it to accept a begin/end iterator range or a vector directly.
Related
Below code is the normal way to get the input from a text and store it in an array in a structure.
Wanted to ask how can i use pointer to store all these data into the array of structure ? Like p1->Years (this is without array, but how can i apply this to way of writing in below code)
Any better suggestion to use pointer to take in the input?
int years = 4;
struct maju_company {
int Year;
float quarter1, quarter2, quarter3, quarter4, total_sales, average_sales;
};
int main() {
string line;
maju_company p1[years];
fstream yeecinnfile("MajuSales.txt");
if(yeecinnfile.is_open()) {
//ignoring the first four line of code and store the rest of the code
string line1,line2,line3,line4;
getline(yeecinnfile,line1);
getline(yeecinnfile,line2);
getline(yeecinnfile,line3);
getline(yeecinnfile,line4);
while(!yeecinnfile.eof()) {
for(int i =0; i<years; i++) {
yeecinnfile>>p1[i].Year>>p1[i].quarter1>>p1[i].quarter2>>p1[i].quarter3>>p1[i].quarter4;
}
}
for(int i =0; i<years; i++) {
cout<<p1[i].Year<<setw(10)<<p1[i].quarter1<<setw(10)<<p1[i].quarter2<<setw(10)<<p1[i].quarter3<<setw(10)<<p1[i].quarter4<<endl;
}
cout<<endl;
}
}
I see nothing wrong with the way you do this.
However, you could create a pointer to each record inside the loop
maju_company* p = &p1[i];
and then use p-> instead of p1[i]., but I really don't see this as an improvement.
If the reading loop looks too complicated, I would rather move the code to a separate function, perhaps
void read_record(maju_company& company);
or maybe
maju_company read_record();
and then only have to handle a single company inside the function (so no indexing and no ponters there).
I think you wouldn't need pointers at all for your example.
Use a std::vector to hold all your data and then there are other
things from C++ I think you should learn to use, example here :
(if you have questions let me know)
#include <iostream>
#include <string>
#include <vector>
#include <fstream>
// dont use : using namespace std;
struct maju_company_data
{
int year;
float quarter1, quarter2, quarter3, quarter4, total_sales, average_sales;
};
// describe how to stream data to an output stream (like std::cout)
std::ostream& operator<<(std::ostream& os, const maju_company_data& data)
{
os << "-----------------------------------------------------\n";
os << "Company data for year : " << data.year << "\n";
os << "Quarter 1 : " << data.quarter1 << "\n";
os << "Quarter 2 : " << data.quarter1 << "\n";
os << "Quarter 3 : " << data.quarter1 << "\n";
os << "Quarter 4 : " << data.quarter1 << "\n";
os << "\n";
return os;
}
int main()
{
// no need to manage pointers yourself use a vector
std::vector<maju_company_data> company_yearly_data; // give variables a meaningful name
std::ifstream ifile("MajuSales.txt"); // ifstream your using file as input
std::string line1, line2, line3, line4;
// ignore first line
ifile >> line1;
while (ifile >> line1 >> line2 >> line3 >> line4) // probably you need to read a few more lines here
{
maju_company_data data;
// convert read strings to numbers
data.year = std::stoi(line1);
data.quarter1 = std::stof(line2);
data.quarter2 = std::stof(line3);
data.quarter3 = std::stof(line4);
//..
//data.quarter4 = std::stof(line5);
//data.total_sales = std::stof(line6);
company_yearly_data.push_back(data);
};
// this is a range based for loop
// it is prefered since you cant go out of bounds
// const auto& means that data will be an unmodifiable
// reference to each of the structs stored in the vector
for (const auto& data : company_yearly_data)
{
std::cout << data; // since we overloaded << this loop will be nice and clean
}
return 0;
}
A C++ approach to this to overload the istream operator>> and ostream operator<< for your specific type. E.g.
#include <algorithm>
#include <array>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <string>
static constexpr auto years{4};
struct maju_company {
int Year{};
float quarter1{}, quarter2{}, quarter3{}, quarter4{};
float total_sales{}, average_sales{}; // ALWAYS init your floats.
};
auto& operator>>(std::istream& is, maju_company& mc) {
is >> mc.Year
>> mc.quarter1 >> mc.quarter2 >> mc.quarter3 >> mc.quarter4
>> mc.total_sales >> mc.average_sales;
return is;
}
auto& operator<<(std::ostream& os, maju_company const& mc) {
os << mc.Year
<< std::setw(10) << mc.quarter1
<< std::setw(10) << mc.quarter2
<< std::setw(10) << mc.quarter3
<< std::setw(10) << mc.quarter4;
return os;
}
You can then go on to use the type using the std library, e.g.
int main() {
auto p1{std::array<maju_company, years>{}};
{
auto fs{std::fstream("MajuSales.txt")};
if (!fs.is_open()) return -1;
{
// throw away 4 lines
auto dummy{std::string{}};
for (auto i{0}; i < 4; ++i) getline(fs, dummy);
}
std::copy_n(std::istream_iterator<maju_company>{fs},
years,
begin(p1));
}
std::copy(cbegin(p1), cend(p1),
std::ostream_iterator<maju_company>{std::cout, "\n"});
}
I need to find the fastest way from one city to other using djikstra algorithm (working on directed graph with weights), but i have to use this specific container. I have a problem with saving data properly to this map, here is part of my code.
fstream file;
string fromThisCity, toThisCity;
int distance;
file.open("drogi.txt", ios::in);
if (file.good())
{
while (!file.eof())
{
file >> fromThisCity;
file >> toThisCity;
file >> distance;
map<string, int> inGraph;
inGraph[toThisCity] = distance;
graph[fromThisCity] = inGraph;
for (map<string, map<string, int>>::iterator element = graph.begin(); element != graph.end(); element++)
{
for (map<string, int>::iterator i = inGraph.begin(); i != inGraph.end(); i++)
{
cout << element->first << " " << i -> first << " " << i -> second << endl;
}
}
}
}
else
{
cout << "file couldnt be opened" << endl;
}
file.close();
Every time you read a fromThisCity, you set that key's value in graph to a new single-value map<string,int>, discarding any mapping you already had.
You want to modify the map graph[fromThisCity], not replace it, i.e. graph[fromThisCity][toThisCity] = distance;.
Fixing some other problems, you might end up with
ifstream file("drogi.txt"); // Use ifstream for reading, open on initialization.
if (file.good())
{
// Declare variables as near to use as possible.
string fromThisCity, toThisCity;
int distance;
// This is the "standard" and least surprising read loop.
while (file >> fromThisCity >> toThisCity >> distance)
{
// Update the graph.
graph[fromThisCity][toThisCity] = distance;
// Since you're apparently in C++17, use modern loops.
for (const auto& from: graph)
{
for (const auto& to: from.second)
{
cout << from.first << " " << to.first << " " << to.second << endl;
}
}
}
}
else
{
cout << "File couldn't be opened" << endl;
}
// You don't need to explicitly close fstreams; the destructor does it if appropriate.
Having this kind of map is wasteful.
Note that for the problem itself you do not need to have city names.
Also learn to split code into small functions to make it easier to maintain.
Take a look on that:
struct Data {
std::map<std::string, int> namesToIds;
std::vector<std::string> names;
std::vector<std::vector<int>> distances;
int addCity(const std::string& city) {
auto cityIdCandidate = static_cast<int>(namesToIds.size());
auto [it, newInsert] =
namesToIds.insert(std::pair{city, cityIdCandidate});
if (newInsert) {
names.push_back(city);
updateDistancesSize();
}
assert(names.size() == namesToIds.size());
return it->second;
}
void addRoute(int src, int dst, int dist) {
distances[src][dst] = dist;
}
void addRoute(const std::string& src, const std::string& dst, int dist) {
addRoute(addCity(src), addCity(dst), dist);
}
void updateDistancesSize() {
auto newSize = names.size();
distances.resize(newSize, std::vector(newSize, -1));
for(auto& row : distances) {
row.resize(newSize, -1);
}
}
};
std::istream& operator>>(std::istream& in, Data& data) {
std::string src, dst;
int dist;
while (in >> src >> dst >> dist) {
data.addRoute(src, dst, dist);
}
return in;
}
void loadData(std::filesystem::path name, Data& data) {
std::ifstream stream{name};
stream >> data;
}
https://godbolt.org/z/q194a9dG6
Concerns are spited, use of file is not obligatory, city names are kept separately from adjacency matrix. Code is easy to read and maintain.
My code keeps giving me error Invalid operands to binary expression ('std::__1::ostream' (aka 'basic_ostream<char>') and 'const bus') Is this a case of the most vexing parse? if so how to fix it. I am trying to print out objects stored in vector. std::vector<bus> v = {} is my vector to contain the object whereas bus is my class
#include <iostream>
#include <vector>
class bus{
public:
int carNum, releaseYear;
};
int temp1, temp2;
void print(std::vector<bus> const &input)
{
for (auto it = input.cbegin(); it != input.cend(); it++)
{
std::cout << *it << ' '<< std::endl;
}
}
int main()
{
bus bus1;
bus1.carNum = 0;
bus1.releaseYear = 0;
bus bus2;
bus2.carNum = 0;
bus2.releaseYear = 0;
// Create a vector containing objects
std::vector<bus> v = {};
// Add two more integers to vector
std::cout<<"enter number"<<std::endl;
std::cin>>temp1;
temp1 = bus1.carNum;
std::cout<<"enter year"<<std::endl;
std::cin>>temp2;
temp2 = bus1.releaseYear;
v.push_back(bus1);
print(v)
}
There is no vexing parse here. You simply need to overload the stream insertion operator for the bus type. Since bus only has public data members, this can be a non-friend function:
std::ostream& operator<<(std::ostream& stream, bus const& b)
{
stream << "#" << b.carNum << " Year" << b.releaseYear;
return stream;
}
You can of course, format your output however you want.
I create a multiple table of string type. I keep variables inside (int, string). It gives me an error:
[Error] cannot convert 'std::string {aka std::basic_string}' to 'char' in assignment
I've created a tree-shaped suite of functions.The program create a multiple array from a file with this format:
11 10 2001
CSKA Moscow
12 1
Bayern Munich
...
Program:
void llegir(std::fstream &_contingut, std::string *_taula) {
//declaro variables
int dia, mes, any, puntsLocal, puntsVisitant, i = 0;
std::string equipLocal, equipVisitant;
while (!(_contingut.eof())) {
//llegeixo arxiu
_contingut >> dia >> mes >> any; //primera linea
_contingut.ignore();
getline(_contingut, equipLocal); //segona linea
_contingut >> puntsLocal >> puntsVisitant; //tercera linea
_contingut.ignore();
getline(_contingut, equipVisitant); //quarta linea
_taula[i][0] = dia;
_taula[i][1] = mes;
_taula[i][2] = any;
_taula[i][3] = equipLocal.c_str();
_taula[i][4] = puntsLocal;
_taula[i][5] = equipVisitant.c_str();
_taula[i][6] = puntsVisitant;
i++;
}
}
void creartaulaDelFitxer(std::string _fitxer, std::string *_taula, int &n_taula) {
std::fstream arxiu;
arxiu.open(_fitxer, std::fstream:: in );
if (arxiu.is_open()) {
std::cout << "existeix";
} else {
std::cout << "ERROR OBERTURA FITXER";
}
llegir(arxiu, _taula);
}
int main(int argc, char** argv) {
std::string fitxer;
std::string eurolliga[300][7];
int n_taula = 0;
std::cout << "INTRODUEIX NOM FITXER:" << std::endl;
std::cin >> fitxer;
creartaulaDelFitxer(fitxer, *eurolliga, int n_taula);
}
You are mixing pointers, chars and strings which will certainly cause a lot of headache. Try to use the standard containers, like std::string and std::vector. If you need many strings, put them in a vector. When you have a collection of data like
11 10 2001
CSKA Moscow
12 1
Bayern Munich
that describes some entity, create a class for it. You can then add streaming operators for that class to read in one of these entities. If you have a collection of entities, make a container and add streaming operators for that too.
Example:
#include <iostream>
#include <fstream>
#include <vector>
class Game {
std::string equipLocal{};
std::string equipVisitant{};
int dia{}, mes{}, any{};
int puntsLocal{}, puntsVisitant{};
public:
friend std::istream& operator>>(std::istream&, Game&);
friend std::ostream& operator<<(std::ostream&, const Game&);
};
// read one entity from an istream
std::istream& operator>>(std::istream& is, Game& g) {
if(is >> g.dia >> g.mes >> g.any) {
is.ignore();
if(std::getline(is, g.equipLocal) && (is >> g.puntsLocal >> g.puntsVisitant)) {
is.ignore();
std::getline(is, g.equipVisitant);
}
}
return is;
}
// write one entity to an ostream
std::ostream& operator<<(std::ostream& os, const Game& g) {
return os << g.dia << " " << g.mes << " " << g.any << "\n"
<< g.equipLocal << "\n"
<< g.puntsLocal << " " << g.puntsVisitant << "\n"
<< g.equipVisitant << "\n";
}
class EuroLiga {
std::vector<Game> games{};
public:
bool Load(const std::string& filename) {
std::ifstream arxiu(filename);
if(arxiu) {
games.clear();
arxiu >> *this; // use this class' friend, operator>>
return true;
} else
return false;
}
// support for basic non-const iteration over the 'games'
std::vector<Game>::iterator begin() { return games.begin(); }
std::vector<Game>::iterator end() { return games.end(); }
friend std::istream& operator>>(std::istream&, EuroLiga&);
};
// read all entities from an istream
std::istream& operator>>(std::istream& is, EuroLiga& el) {
Game tmp;
while(is >> tmp) {
el.games.push_back(std::move(tmp));
}
return is;
}
int main() {
EuroLiga euroliga;
std::string fitxer;
std::cout << "INTRODUEIX NOM FITXER: ";
std::cin >> fitxer;
euroliga.Load(fitxer);
// display all entities read from the file
for(auto& g : euroliga) {
std::cout << g << "\n";
}
}
void llegir(std::fstream &_contingut, std::string *_taula)
Gets a pointer to a string called _taula, this is probably your array.
However you assign something to your array like this:
_taula[i][0] = dia; // allowed, but bad because char is basically a number.
[...]
_taula[i][3] = equipLocal.c_str(); // not allowed, you are assigning a char pointer to a char.
taula[i] is the i-th string in your array. And by putting [0] you assign to the first character in that string. dia is an integer though.
For example
std::string[] = {"Hello", "world", "I", "am", "alive"};
std::cout << string[1] << std::endl; // output "world"
std::cout << string[1][0] << std::endl; // output 'w'
You can not assign a string to a single character.
As a side note, you should look into declaring an enumeration for your array index (and a constant for it's size) to make it more clear and improve maintainability.
What you should probably be doing is create a struct or class for your, whatever it is
struct whateverItIs {
int dia, mes, any, puntsLocal, puntsVisitant;
std::string equipLocal, equipVisitant;
};
Make a new instance of that in your llegir and push it to the back of a std::vector you get by reference.
Just remember to delete() them later especially before that vector goes out of scope.
std::ostringstream oss;
oss << std::setw(10);
oss << std::setfill(' ');
oss << std::setprecision(3);
float value = .1;
oss << value
I can check if value < 1 and then find the leading zero and remove it. Not very elegant.
I can check if value < 1 and then find the leading zero and remove it. Not very elegant.
Agreed, but that's what you have to do without mucking around in locales to define your own version of ostream::operator<<(float). (You do not want to do this mucking around.)
void float_without_leading_zero(float x, std::ostream &out) {
std::ostringstream ss;
ss.copyfmt(out);
ss.width(0);
ss << x;
std::string s = ss.str();
if (s.size() > 1 && s[0] == '0') {
s.erase(0);
}
out << s;
}
You could write your own manipulator. The elegance is of course debatable. It would more or less be what you've all ready proposed though.
Example:
struct myfloat
{
myfloat(float n) : _n(n) {}
float n() const { return _n; }
private:
float _n;
};
std::ostream &<<(std::ostream &out, myfloat mf)
{
if (mf.n() < 0f)
{
// Efficiency of this is solution is questionable :)
std::ios_base::fmtflags flags = out.flags();
std::ostringstream convert;
convert.flags(flags);
convert << mf.n();
std::string result;
convert >> result;
if (result.length() > 1)
return out << result.substr(1);
else
return out << result;
}
return out;
}