I tested the following two code snippets and found that code snippet A was more efficient than code snippet B. Why? str() has copy operations but rdbuf() don't. Is str("") is more expensive than str()?
code snippet A:
ofstream out("foo.txt");
stringstream ss;
for(int i = 0; i < 300000; i++) {
// append long text to ss
out<<ss.str();
ss.seekp(ios_base::beg);
}
out.close();
code snippet B:
ofstream out("foo.txt");
stringstream ss;
for(int i = 0; i < 300000; i++) {
// append long text to ss
out<<ss.rdbuf();
ss.str("");
}
out.close();
Two potential differences can be detected:
out<<ss.str(); copies the data as std::basic_string, where out<<ss.rdbuf(); returns a pointer of type std::basic_streambuf<CharT,Traits>
ss.str(""); replaces the content in the stringstream, where ss.seekp(ios_base::beg); only sets the output position indicator
ps.
ss.str("") and ss.str() are two different operations.
See:
http://en.cppreference.com/w/cpp/io/basic_stringstream/str
Confusingly, ss.str() does not do the same thing as ss.str(""):
ss.str() returns a copy of the underlying string, whereas ss.str(const string&) changes the underlying storage.
Documentation
Related
I have to create a program that will output various information about 1343 runners in a marathon. I'm having to import the data from a csv spreadsheet, so I chose to use the getline function. I use simple recursion to fill a string array and then simply use recursion once more to output the data. But for some reason, it only wants to display 300 or so runners' data. Here's the code:
int main(){
string data[1344];
vector<string> datav;
string header;
ifstream infile("C:\\Users\\Anthony\\Desktop\\cmarathon.csv");
int i = 0;
if (infile.is_open()) {
for (i=0; i<=1343; i++) {
getline(infile, data[i]);
}
datav.assign(data, data+1344);
for (int i = 0; i < datav.size(); i++) {
cout << datav[i] << "\n";
}
}
}
I attempted to use a vector in hopes it would help to allocate the required memory to execute the program properly (if that is in fact the problem here).
That code yields the perfect output of runners 1045-1343. I've tried simple work arounds, such as using several for() loops to combine the output seamlessly to no avail. Any information would be appreciated.
You do not need to copy from the array to the vector. You can add to the vector directly instead. Also, it is somewhat bad practice to shadow another local variable at the outer scope.
int main(){
string line;
vector<string> datav;
string header;
ifstream infile("C:\\Users\\Anthony\\Desktop\\cmarathon.csv");
if (infile.is_open()) {
// Are you supposed to read the header line first?
getline( infile, header );
while( getline( infile, line ).good() )
datav.push_back( line );
cout << "Container has " << datav.size() << " lines\n";
for (size_t i = 0; i < datav.size(); i++) {
cout << datav[i] << "\n";
}
}
}
Of course, you still have to break down each line to the individual fields, so pushing back a class or struct as EToreo suggested would be a good idea.
You should try using a struct to represent the fields in the CSV file and then make a vector of that struct type.
Now, loop through the file, reading each line till you reach the end of the file (Google how to do that) - DO NOT assume 1343, you don't have to. When you read in each line, create a new object from your struct and fill it with the content of that line (you will need to parse it by reading till a tab (\t) or the end of the string) and then datav.push(newObj) it onto your vector.
I suggest using the correct type's in your struct (int for age, string for name, etc.) and passing the string values from the file into those types. It will be much easier to do things like make a sum of everyone's age. You will thank yourself (and maybe me?) later.
If your not needing to use a vector:
for (i=0; i<=1343; i++) {
cout << data[i] << endl;
}
should work to print out whatever is in the data array
It is also possible to specify a delimeter for the getline function if you need to put different strings in different variables.
However EToreo's method may be more useful to you in the long run.
I created a very simple code, but the push_back function doesn't want to work. It gives me an absolutely different result than expected.
Here is the code:
std::vector<std::string> words;
std::ifstream infile ("words.txt");
std::string temp;
while (std::getline(infile, temp))
{
words.push_back(temp);
}
for (std::size_t i = 0; i < words.size(); i++)
{
std::cout << words[i] << " ";
}
The "words.txt" file contains only 4 words:
window
tyre
give
speaker
The result is supposed to be "window tyre give speaker", but for me it is " speaker".
What is the problem?
This proved to be the underlying problem:
Have you tried dumping the input file (e.g. with hexdump -C or similar) to check for rogue control sequences such as \r which might explain the behaviour which you are seeing.
Your input file might be a text file from a DOS/Windows-like system and you might be using a Unix-like system.
I have tried to solve this with previously answered questions like Conversion from string to float changes the number but I have not been successful.
In my code I take a string full of ' ' characters and convert it to float using stringstream. It worked fine (returned me a zero valued float) until I performed another conversion right after that. When a conversion is executed afterwards, the value stored in the float previously converted is not zero, but 4.57048e-41. I hope the following code explains my problem more clearly.
I started with:
std::stringstream ss;
float a;
float b;
for(int i=0; i<LIM; ++i){
//some other conversions using same stringstream
//clearing stringstream
ss.str( std::string() );
ss.clear();
ss << str1; //string full of empty spaces, length of 5
ss >> a;
std::cout << a;//prints zero
}
That worked just fine, but when I changed it to
std::stringstream ss;
float a;
float b;
for(int i=0; i<LIM; ++i){
//some other conversions using same stringstream
//clearing stringstream
ss.str( std::string() );
ss.clear();
ss << str1; //string full of empty spaces, length of 5
ss >> a;
std::cout << a;//prints 4.57048e-41
ss.str ( std::string() );
ss.clear();
ss << str2; //another string full of empty spaces, length of 5
ss >> b;
std::cout << b;//prints zero
}
I am using gcc 4.6.3 with the following flags:
-o2 -Wall -Wextra -ansi -pedantic
Any kind of help will be greatly appreciated, but I am not willing to use doubles.
Many thanks!
If the conversion fails, then the target value isn't changed. In your case, it still has its original uninitialised value; so printing it gives garbage or other undefined behaviour.
You should check whether the conversion succeeded:
if (!(ss >> a)) {
a = 0; // or handle the failure
}
or use conversion functions like std::stof in C++11, or boost::lexical_cast, which throw to indicate conversion failure. (Or, as mentioned in the comments, just set it to zero to start with if you don't otherwise need to detect failure).
I am using C++ and writing a program that is supposed to do a bunch of stuff with primes. However the main issue is that I am having trouble converting in between ints and strings. I believe the following is the relevant code:
for(int j=0;j<size-1;j++){
num=primes[j];
ss<<num;
ss>>temp;
ss.str("");
for (int count=0; count < temp.size(); count++) {
cout<<temp<<endl;
}
I know that I could Google and figure out how to convert from an integer another way. However, I have a feeling that the reason I can't figure out what is going wrong is because I'm lacking some fundamental knowledge about stringstreams which I'm not aware of which I'm hoping can be fixed. num is an int and ss is a stringstream and cout temp is printing out 2 every single time, which is the value of primes[0]. I think the stringstream might be not reading after the first trial because of something to do with a newline character but I don't really know.
The reason for what you are experiencing is that the EOF_BIT will be set in ss after reading the first value into temp, after that no read/writes can be made to the std::stringstream and therefore temp is not updated with a new value.
A more human readable way of explaining the above; the std::stringstream ss will think that it has reached the end (which it has, at one point). You'll need to tell it to "start all over again" (reset all error-flags) for it to be usable in another iteration.
How do I solve this issue?
There are a few methods available, to me the most clear (in code readability) is to use a new std::stringstream for each iterator in your loop (see "Example solution #2).
Check out the snippets below that all will output:
2
3
5
7
11
13
17
Example solution #1
int const PRIMES_SIZE = 7;
int const primes[PRIMES_SIZE] = {2,3,5,7,11,13,17};
std::stringstream ss;
std::string temp;
for (int i =0; i < PRIMES_SIZE; ++i) {
ss << primes[i];
ss >> temp;
std::cout << temp << std::endl;
ss.clear (); // unset error flags
}
Example solution #2
int const PRIMES_SIZE = 7;
int const primes[PRIMES_SIZE] = {2,3,5,7,11,13,17};
for (int i =0; i < PRIMES_SIZE; ++i) {
std::stringstream ss;
ss << primes[i];
std::cout << ss.str () << std::endl;
}
Example solution #3
#include <iterator>
...
int const PRIMES_SIZE = 7;
int const primes[PRIMES_SIZE] = {2,3,5,7,11,13,17};
std::stringstream ss;
std::copy (primes, primes+PRIMES_SIZE, std::ostream_iterator<int> (ss, "\n"));
std::cout << ss.str ();
everybody I have problem with string concatenation in C++, here is my code
map<double, string> fracs;
for(int d=1; d<=N; d++)
for(int n=0; n<=d; n++)
if(gcd(n, d)==1){
string s = n+"/"+d;// this does not work in C++ but works in Java
fracs.insert(make_pair((double)(n/d), s));
}
How can I fix my code?
Try like this.
stringstream os;
os << n << "/" << d;
string s =os.str();
In C++ you have to convert an int to a string before you can concatenate it with another string using the + operator.
See Easiest way to convert int to string in C++.
Use streams, in your case, a stringstream:
#include <sstream>
...
std::stringstream ss;
ss << n << '/' << d;
Later, when done with your work, you can store it as an ordinary string:
const std::string s = ss.str();
Important (side-) note: Never do
const char *s = ss.str().c_str();
stringstream::str() produces a temporary std::string, and according to the standard, temporaries live until the end of the expression. Then, std::string::c_str() gives you a pointer to a null-terminated string, but according to The Holy Law, that C-style-string becomes invalid once the std::string (from which you receved it) changes.
It might work this time, and next time, and even on QA, but explodes right in the face of your most valuable customer.
The std::string must survive until the battle is over:
const std::string s = ss.str(); // must exist as long as sz is being used
const char *sz = s.c_str();
n and d are integers. Here is how you can convert integer to string:
std::string s;
std::stringstream out;
out << n << "/" << d;
s = out.str();
You could use a stringstream.
stringstream s;
s << n << "/" << d;
fracs.insert(make_pair((double)n/d, s.str()));
No one has suggested it yet but you can also take a look at boost::lexical_cast<>.
While this method is sometimes criticized because of performance issues, it might be ok in your situation, and it surely makes the code more readable.
Unlike in Java, in C++ there is no operator+ that explicitly converts a number to a string. What is usually done in C++ in cases like this is...
#include <sstream>
stringstream ss;
ss << n << '/' << d; // Just like you'd do with cout
string s = ss.str(); // Convert the stringstream to a string
I think sprintf(), which is a function used to send formatted data to strings, would be a much clearer way to do it. Just the way you would use printf, but with the c-style string type char* as a first(additional) argument:
char* temp;
sprint(temp, "%d/%d", n, d);
std::string g(temp);
You could check it out at http://www.cplusplus.com/reference/cstdio/sprintf/