How to deal with last comma, when making comma separated string? [duplicate] - c++

This question already has answers here:
Closed 11 years ago.
Possible Duplicates:
Don't print space after last number
Printing lists with commas C++
#include <vector>
#include <iostream>
#include <sstream>
#include <boost/foreach.hpp>
using namespace std;
int main()
{
vector<int> VecInts;
VecInts.push_back(1);
VecInts.push_back(2);
VecInts.push_back(3);
VecInts.push_back(4);
VecInts.push_back(5);
stringstream ss;
BOOST_FOREACH(int i, VecInts)
{
ss << i << ",";
}
cout << ss.str();
return 0;
}
This prints out: 1,2,3,4,5,
However I want: 1,2,3,4,5
How can I achieve that in an elegant way?
I see there is some confusion about what I mean with "elegant": E.g. no slowing down "if-clause" in my loop. Imagine 100.000 entries in the vector! If that is all you have to offer, I'd rather remove the last comma after I have gone through the loop.

How about this:
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
#include <string>
#include <sstream>
int main()
{
std::vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
std::ostringstream ss;
if(!v.empty()) {
std::copy(v.begin(), std::prev(v.end()), std::ostream_iterator<int>(ss, ", "));
ss << v.back();
}
std::cout << ss.str() << "\n";
}
No need to add extra variables and doesn't even depend on boost! Actually, in addition to the "no additional variable in the loop" requirement, one could say that there is not even a loop :)

Detecting the one before last is always tricky, detecting the first is very easy.
bool first = true;
stringstream ss;
BOOST_FOREACH(int i, VecInts)
{
if (!first) { ss << ","; }
first = false;
ss << i;
}

Using Karma from Boost Spirit - has a reputation for being fast.
#include <iostream>
#include <vector>
#include <boost/spirit/include/karma.hpp>
int main()
{
std::vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
using namespace boost::spirit::karma;
std::cout << format(int_ % ',', v) << std::endl;
}

Try:
if (ss.tellp ())
{
ss << ",";
}
ss << i;
Alternatively, if the "if" is making you worried:
char *comma = "";
BOOST_FOREACH(int i, VecInts)
{
ss << comma << i;
comma = ",";
}

Personally, I like a solution that does not cause potential memory allocations (because the string grows larger than needed). An extra-if within the loop body should be tractable thanks to branch target buffering, but I would do so:
#include <vector>
#include <iostream>
int main () {
using std::cout;
typedef std::vector<int>::iterator iterator;
std::vector<int> ints;
ints.push_back(5);
ints.push_back(1);
ints.push_back(4);
ints.push_back(2);
ints.push_back(3);
if (!ints.empty()) {
iterator it = ints.begin();
const iterator end = ints.end();
cout << *it;
for (++it; it!=end; ++it) {
cout << ", " << *it;
}
cout << std::endl;
}
}
Alternatively, BYORA (bring your own re-usable algorithm):
// Follow the signature of std::getline. Allows us to stay completely
// type agnostic.
template <typename Stream, typename Iter, typename Infix>
inline Stream& infix (Stream &os, Iter from, Iter to, Infix infix_) {
if (from == to) return os;
os << *from;
for (++from; from!=to; ++from) {
os << infix_ << *from;
}
return os;
}
template <typename Stream, typename Iter>
inline Stream& comma_seperated (Stream &os, Iter from, Iter to) {
return infix (os, from, to, ", ");
}
so that
...
comma_seperated(cout, ints.begin(), ints.end()) << std::endl;
infix(cout, ints.begin(), ints.end(), "-") << std::endl;
infix(cout, ints.begin(), ints.end(), "> <") << std::endl;
...
output:
5, 1, 4, 2, 3
5-1-4-2-3
5> <1> <4> <2> <3
The neat thing is it works for every output stream, any container that has forward iterators, with any infix, and with any infix type (interesting e.g. when you use wide strings).

I like moving the test outside the loop.
It only needs to be done once. So do it first.
Like this:
if (!VecInts.empty())
{
ss << VecInts[0]
for(any loop = ++(VecInts.begin()); loop != VecInts.end(); ++loop)
{
ss << "," << *loop;
}
}

You can either trim the string at the end, or using single for loop instead of foreach and dont concatenate at the last iteration

Well, if you format into a stringstream anyway, you can just trim the resulting string by one character:
cout << ss.str().substr(0, ss.str().size() - 1);
If the string is empty, than the second argument says -1, which means everything and does not crash and if the string is non-empty, it always ends with a comma.
But if you write to an output stream directly, I never found anything better than the first flag.
That is unless you want to use join from boost.string algo.

This would work
stringstream ss;
BOOST_FOREACH(int const& i, VecInts)
{
if(&i != &VecInts[0])
ss << ", ";
ss << i;
}
I suspect with "elegant" you mean "without introducing a new variable". But I think I would just do it "non-elegant" if I couldn't find anything else. It's still clear
stringstream ss;
bool comma = false;
BOOST_FOREACH(int i, VecInts)
{
if(comma)
ss << ", ";
ss << i;
comma = true;
}
Imagine 100.000 entries in the vector! If that is all you have to offer, I'd rather remove the last comma after I have gone thorough the loop.
You are saying that as if printing ss << i is one machine instruction. Come on, executing that expression will execute lots of if's and loops inside. Your if will be nothing compared to that.

cout << ss.str()<<"\b" <<" ";
You can add the "\b" backspace
This will overwrite the extra "," .
for Example :
int main()
{
cout<<"Hi";
cout<<'\b'; //Cursor moves 1 position backwards
cout<<" "; //Overwrites letter 'i' with space
}
So the output would be
H

Related

How can I concatenate the elements of a vector of integers to a string in C++?

I've been trying to put every single element of a vector of integers into a string. I want to achieve this by type casting the integers into strings, after that I cocatenate those "small strings" into a single big string, which is going to represent all the elements of that specific vector.
This may look silly, but is really useful if you want to make a function that returns a vector like-a-thing, or etc.
The only problem is that I'm getting an error on line 13, which says :
error: no matching function for call to ‘std::__cxx11::basic_string<char>::basic_string(int&)’
13 | myString += (string) myVector[i];
| ^
and I don't have the slightest idea on why this is happening. My code follows below :
#include <iostream>
#include <vector>
using namespace std;
int main()
{
int myVector[5] = {1,2,3,4,5};
string myString = "";
for (int i =0; i < 5; i++)
{
myString += (string) myVector[i];
myString += "\n";
}
cout << myString << endl;
any help will be much appreciated.
You can use std::to_string to convert an int to a std::string.
Change this line:
myString += (string) myVector[i];
To:
myString += std::to_string(myVector[i]);
Note: concatenating strings like that might not be so efficient due to temporary strings being created and destroyed (although it is likely that small strings optimization will kick in, so no additional heap allocations will take place).
As #Someprogrammerdude commented, you can consider to use std::ostringstream.
Side notes:
You are missing #include <string>.
Why is "using namespace std;" considered bad practice?.
You can use the fmt library:
fmt::join will accept a range, in your case a vector of ints, and join its contents with a given separator (e.g. an empty string if you just want all of the elements together).
fmt::format will create a string with a given format, in this case just the contents of the joined vector.
Demo
#include <fmt/ranges.h>
int main() {
int myVector[5] = {1,2,3,4,5};
auto myString = fmt::format("{}", fmt::join(myVector, ""));
fmt::print("{}\n", myString);
}
// Outputs: 12345
Or, simpler, if you don't need the string:
int main() {
int myVector[5] = {1,2,3,4,5};
fmt::print("{}\n", fmt::join(myVector, ""));
}
The error you are getting is saying that the compiler cannot find a std::__cxx11::basic_string<char>::basic_string(int&) function, i.e., a std::string constructor accepting an int&.
You can use std::stringstream
#include <iostream>
#include <string>
#include <sstream>
int main()
{
int myVector[5] = {1,2,3,4,5};
std::string myString;
std::stringstream sstream;
for (auto i : myVector)
sstream << i;
sstream >> myString;
std::cout << myString;
}
Link.
I'll add my own solution, as laid out in my comment:
#include <iostream>
#include <iterator>
#include <algorithm>
#include <vector>
int main()
{
std::vector myvector = { 1, 2, 3, 4, 5 };
std::copy(std::begin(myvector), std::end(myvector),
std::ostream_iterator<int>(std::cout, "\n"));
}
Overload the output stream operator, and then you have something reusable for a lot of scenarios.
Based on the feedback below overloading is not the best answer, another approach here : https://www.onlinegdb.com/zDUjVbSTp
#include <vector>
#include <iostream>
#include <string>
#include <sstream>
// Overloading operator<< will have many benefits.
// you can use it to output an array to std::cout directly
// or you can write it to a file or stringstream
std::ostream& operator<<(std::ostream& os, const std::vector<int>& values)
{
os << "[";
bool comma = false;
for (const auto& value : values)
{
if (comma) os << ", ";
os << value;
comma = true;
}
os << "]";
return os;
}
int main()
{
std::vector<int> values{ 1,2,3,4,5 };
// write directly to std::cout
std::cout << "direct : " << values << "\n";
// adding array to a string
std::ostringstream os;
std::string string{ "output = " };
os << values;
string += os.str();
std::cout << string << "\n";
return 0;
}
You can use for_each algorithm as well to do the concatenation.
#include <iostream>
#include <algorithm>
int main()
{
std::vector<int> v{1, 2, 3, 4, 5, 6};
std::string all;
std::for_each(v.begin(), v.end(), [&, del=""](const int &e) {
all += std::to_string(e) + (&e == &v.back() ? "" : del);
});
std::cout << all << std::endl;
}
output:
123456
If you want to add a delimiter in between, just change the del value in lambda capture.
std::for_each(v.begin(), v.end(), [&, del="-"](const int &e) {
all += std::to_string(e) + (&e == &v.back() ? "" : del);
});
Output:
1-2-3-4-5-6

c++ stack overflow due to recursive function how can I improve the data handling

I'm tackling a exercise which is designed to cause exactly this problem, of overloading the memory. Pretty much I'm loading various file sizes from 1,000 to 5 million lines of entries like this in a txt file (1 line = 1 entry):
SHFIv,aiSdG
PlgNB,bPHoP
ZHWJU,gfwgC
UAygL,Vqvhi
BlyzX,LLbCo
jbvrT,Utblj
...
pretty much every entry has 2 values separated by comma, in my code, I separate these values and try to find another matching value, there are always only 2 exactly matching values and each time 1 value is found the other one with which it is paired points to another pair, and so on until the final one gets found.
For example SHFIv,aiSdG would point to aiSdG,YDUVo.
I know my code is not very efficient, partly due to using recursion, but I could'nt figure out a better way to do the job, so any suggestions on how to possibly improve it to handle larger inputs would be greatly appriciated
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <map>
#include <unordered_map>
#include <stdio.h>
#include <vector>
#include <iterator>
#include <utility>
#include <functional>
#include <algorithm>
using namespace std;
template<typename T>
void search_bricks_backwards(string resume, vector<T>& vec, vector<string>& vec2) {
int index = 0;
for (const auto& pair : vec) {
//cout << "iteration " << index << endl;
if (pair.second == resume) {
vec2.insert(vec2.begin(), resume);
cout << "found " << resume << " and " << pair.second << endl;
search_bricks_backwards(pair.first, vec, vec2);
}
if (index + 1 == vec.size()) {
cout << "end of backward search, exitting..." << endl;
}
index++;
}
}
template<typename T>
void search_bricks(string start, vector<T>& vec, vector<string>& vec2) {
int index = 0;
for (const auto& pair : vec) {
//cout << "iteration " << index << endl;
if (pair.first == start) {
vec2.push_back(start);
cout << "found " << start << " and " << pair.first << endl;
search_bricks(pair.second, vec, vec2);
}
if (index + 1 == vec.size()) {
//search_bricks_backwards(start, vec, vec2);
// this also gets called on every recursion rather than just once
// as I originally intended when the forward iteration gets finished
}
index++;
}
}
template<typename T> // printing function
void printVectorElements(vector<T>& vec)
{
for (auto i = 0; i < vec.size(); ++i) {
cout << "(" << vec.at(i).first << ","
<< vec.at(i).second << ")" << endl ;
}
cout << endl;
}
vector<string> split(string s, string delimiter) { // filtering function
size_t pos_start = 0, pos_end, delim_len = delimiter.length();
string token;
vector<string> res;
while ((pos_end = s.find(delimiter, pos_start)) != string::npos) {
token = s.substr(pos_start, pos_end - pos_start);
pos_start = pos_end + delim_len;
res.push_back(token);
}
res.push_back(s.substr(pos_start));
return res;
}
int main()
{
vector<pair<string, string>> bricks;
vector<string> sorted_bricks;
ifstream inFile;
inFile.open("input-pairs-5K.txt"); // transferring data from .txt to a string
stringstream strStream;
strStream << inFile.rdbuf();
string str = strStream.str();
istringstream iss(str);
for (string line; getline(iss, line); )
// filtering data from string and dividing on ","
{
string delimiter = ",";
string s = line;
vector<string> v = split(s, delimiter);
string s1 = v.at(0);
string s2 = v.at(1);
bricks.push_back(make_pair(s1, s2));
}
search_bricks(bricks[0].second, bricks, sorted_bricks);
//printVectorElements(bricks);
//for (auto i = sorted_bricks.begin(); i != sorted_bricks.end(); ++i)
//cout << *i << " "; // this is just to check if vectors have data
}
Here is link to the 1k test data that works for me (only for the search bricks without backwards searching since it triggers on every recursion) again thanks for any suggestions on how to improve or get rid of the recursion. I don't code in c++ often and don't really know how else to tackle this.
Although implementing non-recursive version of your algorithm is canonical solution, if you really need to solve the problem without code modification, you can increase the stack size by modifying compiler option. ~100Mb will be usually sufficient.
In MSVC : /STACK:commit 104857600
In gcc : --stack, 104857600

How to join an int array to string in c++ separated by a dot? [duplicate]

This question already has answers here:
Convert a vector<int> to a string
(25 answers)
Closed 4 years ago.
i have an int array
int arr[]={192,168,1,0};
What is the best way to convert it into a string separated by dots so that i can obtain the following output:
192.168.1.0
A completely generic approach, typical C++ style:
template <typename Iterator>
std::string join(Iterator begin, Iterator end, char separator = '.')
{
std::ostringstream o;
if(begin != end)
{
o << *begin++;
for(;begin != end; ++begin)
o << separator << *begin;
}
return o.str();
}
Could be combined with a convenience function to avoid having to get the iterators all the time:
template <typename Container>
std::string join(Container const& c, char separator = '.') // can pass array as reference, too
{
using std::begin;
using std::end;
return join(begin(c), end(c), separator);
// not using std::... directly:
// there might be a non-std overload that wouldn't be found if we did
}
The period as default separator might fit your need, but possibly is a rather bad choice for general, would we rather want to join with space?
If you modify the signature as follows:
template <typename Iterator, typename Separator>
std::string join(Iterator begin, Iterator end, Separator&& separator);
You could use arbitrary separators, as long as supported by operator<< – but you lose the default separator...
you can use the intuitive std::ostringstream class here:
#include <sstream>
#include <string>
#include <iostream>
int main() {
int arr[] = { 192,168,1,0 };
std::ostringstream stream;
for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); ++i) {
if (i) stream << '.';
stream << arr[i];
}
std::string string = stream.str();
std::cout << string << '\n';
}
output:
192.168.1.0
The most important thing is that there is no best way. You should get the confidence to develop your own style.
Assuming your array is always size 4 and your int values range from 0 to 255 (which I think is reasonable) I would do this
char buffer[99];
sprintf(buffer, "%d.%d.%d.%d", arr[0], arr[1], arr[2], arr[3]);
std::string ip_address(buffer);
You can use stringstream
#include <iostream>
#include <sstream>
int main(void)
{
int arr[] = { 192,168,1,0 };
std::stringstream ss;
for (int i = 0; i < 4; ++i)
{
std::string sep = (i < 3) ? "." : "";
ss << arr[i] << sep;
}
std::cout << ss.str() << std::endl;
}
or without conditional
#include <iostream>
#include <sstream>
int main(void)
{
int arr[] = { 192,168,1,0 };
std::stringstream ss;
ss << arr[0] << "." << arr[1] << "." << arr[2] << "." << arr[3];
std::cout << ss.str() << std::endl;
}
Define a string object into which put each elements of the int array by adding . character after each element except for the last one.
If the length of arr varies a basic code would be something like this:
#include <iostream>
int main()
{
int arr[]={192,168,1,0};
std::string output;
size_t arr_size = sizeof(arr)/sizeof(arr[0]);
for (int i=0; i<arr_size; i++)
{
output.append(std::to_string(arr[i]));
//skip the last dot .
if(i != arr_size-1)
output.append(".");
}
std::cout << "output: " << output << std::endl;
return 0;
}

Remove trailing comma in CSV file written for a vector using copy and ostream_iterator

I have the following function, which writes a vector to a CSV file:
#include <math.h>
#include <vector>
#include <string>
#include <fstream>
#include <iostream>
#include <iterator>
using namespace std;
bool save_vector(vector<double>* pdata, size_t length,
const string& file_path)
{
ofstream os(file_path.c_str(), ios::binary | ios::out);
if (!os.is_open())
{
cout << "Failure!" << endl;
return false;
}
os.precision(11);
copy(pdata->begin(), pdata->end(), ostream_iterator<double>(os, ","));
os.close();
return true;
}
However, the end of the CSV file looks like this:
1.2000414752e-08,1.1040914566e-08,1.0158131779e-08,9.3459324063e-09,
That is, a trailing comma is written into the file. This is causing an error when I attempt to load the file using another software program.
What is the easiest, most efficient way to get rid of (ideally, never write) this trailing comma?
As you observed, copying via std::copy doesn't do the trick, one additional , is output. There is a proposal that will probably make it in the future C++17 standard: ostream_joiner, which will do exactly what you expect.
However, a quick solution available now is to do it manually.
for(auto it = std::begin(*pdata); it != std::end(*pdata); ++it)
{
if (it != std::begin(*pdata))
std::cout << ",";
std::cout << *it;
}
I'd omit printing the comma by treating the first element special:
if (!pdata->empty()) {
os << pdata->front();
std::for_each(std::next(pdata->begin()), pdata->end(),
[&os](auto&& v){ os << ", " << v; });
}
Obviously, this code goes into a function printing a printable range adapter.
There are many ways, besides already listed:
std::string sep;
for (const auto& x : *pdata) {
os << x << clusAvg;
sep = ", ";
}
or
auto it = pdata->begin();
if (it != pdata->end()) {
os << *it;
for(; it != pdata->end(); ++it)
os << ", " << *it;
}
or
auto it = pdata->end();
if (it != pdata->begin()) {
--it;
std::copy(pdata->begin(), it, ostream_iterator<double>(os, ", "));
os << *it;
}

Limiting decimal places of a float when converting to string in C++

I'm making a function which prints the elements of a std::vector<float>.
Working code:
std::vector<float> components { 1, 2, 3 };
string result = "<";
for ( auto it = begin(this->components); it != end(this->components); ++it ) {
result.append(to_string(*it));
if (it != (this->components))
result.append(", ");
}
result.append(">");
std::cout << result;
The intended result is if "components" has elements 1,2,3, for example, it will print: <1, 2, 3>.
Right now it is printing the numbers as floats, of course, like < 1.000000, 2.000000, 3.000000, >.
Is there a way I can control how many decimal places are put into the string, without having to manually go through it character by character?
As a side note, how do I prevent it from adding a ',' after the last element?
You may use std::stringstream.precision for that.
Just create a std::stringstream convert it to a string and your done.
Like so:
stringstream ss;
ss.precision(3);
ss << "<";
for ( auto it = begin(this->components); it != end(this->components); ++it ) {
ss << *it;
if (it != (this->components))
ss << ", ";
}
ss << ">";
string result = ss.str();
you can use sprintf() before casting:
float a = 1.000000;
char aa[20];
sprintf(aa, "%1.3f", a);
Here is the complete code which i ran:
#include <vector>
#include <iterator>
#include <iostream>
using namespace std;
int main()
{
std::vector<float> components{ 1, 2, 3 };
string result = "<";
for (auto it = components.begin(); it != components.end(); ++it) {
float a = *it;
char aa[20];
sprintf(aa, "%1.3f", a);
result.append(string(aa));
if (it+1 != components.end())
result.append(", ");
}
result.append(">");
std::cout << result.c_str();
getchar();
return 0;
}
Output:
I would do it like this using a stringstream.
#include <iostream>
#include <sstream>
#include <iomanip>
#include <vector>
int main()
{
std::vector<float> components {1, 2, 3, 1.5f, 2.5f, 3.5f, 1.25f, 2.25f, 3.25f, 1.12345f};
std::stringstream result;
result << "<";
for(auto it = std::begin(components); it != std::end(components); ++it)
{
if(it != std::begin(components))
{
result << ", ";
}
result << *it;
}
result << ">";
std::cout << result.str();
return 0;
}
You could also use std::fixed and std::setprecision to modify the output further as you desire.
Printing the comma before the next item for all but the first item fixes your trailing comma issue.
Here's a demo of it working:
As #Axalo already noted, you can use setprecision with an ostream to set its precision (and it can be used with any ostream, not just cout).
To eliminate the trailing comma, I'd probably use the infix iterator I've posted elsewhere.
Using that, the code could be written something like this:
#include <iostream>
#include <sstream>
#include <vector>
#include <iomanip>
#include "infix_iterator.h"
int main () {
// Data that would display extra precision if we didn't stop it
std::vector<float> components { 1.123f, 2.234f, 3.345f };
std::ostringstream buff("<", std::ios::app);
buff << std::setprecision(2);
std::copy(components.begin(), components.end(),
infix_ostream_iterator<float>(buff, ", "));
buff << ">";
std::cout << buff.str();
}
Result: <1.1, 2.2, 3.3>