Partial copy of a stringstream - c++

How can I copy three characters beginning with the second one of str1 to str2? This code doesn't work:
#include <iostream>
#include <sstream>
#include <iterator>
#include <algorithm>
int main()
{
std::stringstream str1("0123456789");
std::stringstream str2;
str1.seekg(1, str1.beg);
std::copy(std::istream_iterator<char>(str1), std::istream_iterator<char>(str1)+3, std::ostream_iterator<char>(str2));
std::cout << str2.str() << '\n';
}
The expected output is:
123
This is a dummy test, but I'd like to work with larger stringstreams and efficient methods.

There is another standard library algorithm called std::copy_n that will do what you want. It takes a begin iterator and the number of elements to copy. Your program works if you change the std::copy line to:
std::copy_n(std::istream_iterator<char>(str1), 3, std::ostream_iterator<char>(str2));
Demo on Compiler Explorer
An improvement might be to use std::next instead of the seekg() to advance the first iterator to the desired offset (Thanks to Armin Montigny). That would look like:
std::copy_n(std::next(std::istream_iterator<char>(str1)), 3, std::ostream_iterator<char>(str2));

Use substr() of std::string. To use the it for std::stringstream use str1.str()
Then assign it to str::string str2.
Step 1: Take the string our of the std::stringstream.
Step 2: Perform the operation on std::string.
Step 3: Assign the string to std::stringstream
Other way to get the part of stringstream:
char c[10] = {};
std::istringstream input("This is sample text."); // std::stringbuf makes its entire
// buffer available for unblocking read
input.readsome(c, 5); // reads 'This ' and stores in c[0] .. c[4]
input.readsome(c, 9); // reads 'is sample' and stores in c[0] .. c[8]

Related

How to read double digits and single digits in C++

I have an issue where I cannot get my C++ program to read double digit integers.
My idea is to read it as string and then somehow parse it into separate integers and insert them into an array, but I am stuck on getting the code to read digits properly.
Sample Output:
i: 0 codeColumn 0
i: 1 codeColumn 1
i: 2 codeColumn 0 0
i: 3 codeColumn 0
i: 4 codeColumn 31 0
i: 5 codeColumn 1
i: 6 codeColumn 43 0
i: 7 codeColumn 3
i: 8 codeColumn 9 0
So the file is basically a line of triplets delimited by a comma:
0,1,0 0,0,31 0,0,18 0,0,8 0,11,0
My question is how do you get the trailing zeroes (see above) to move to a new line? I tried using "char" and a bunch of if statements to concatenate the single digits into double digits, but I feel like that's not really efficient or ideal. Any ideas?
My code:
#include <iostream> // Basic I/O
#include <string> // string classes
#include <fstream> // file stream classes
#include <sstream>
#include <vector>
int main()
{
ifstream fCode;
fCode.open("code.txt");
vector<string> codeColumn;
while (getline(fCode, codeLine, ',')) {
codeColumn.push_back(codeLine);
}
for (size_t i = 0; i < codeColumn.size(); ++i) {
cout << " i: " << i << " codeColumn " << codeColumn[i] << endl;
}
fCode.close();
}
getline(fCode, codeLine, ',')
is going to read between commas, so 0,1,0 0,0,31 will split up exactly as you have seen.
0,1,0 0,0,31
^ ^ ^ ^
The tokens collected are everything between the ^s
You have two delimiters you need to take into account comma and space. The easiest way to handle the space is with dumb old >>.
std::string triplet;
while (fCode >> triplet)
{
// do stuff with triplet. Maybe something like
std::istringstream strm(triplet); // make a stream out of the triplet
int a;
int b;
int c;
char sep1;
char sep2;
while (strm >> a >> sep1 >> b >> sep2 >> c // read all the tokens we want from triplet
&& sep1 == sep2 == ',') // and the separators are commas. Triplet is valid
{
// do something with a, b, and c
}
}
Documentation for std::istringstream.
So, I will show you 3 solutions from easy to understand C-Style code, then more-modern C++ code using the std::algorithm library and iterators, and, at the end an object oriented C++ solution.
I will also explain to you that std::getline can be, but should not be used for splitting strings into tokens.
I saw from your question that you had difficulties to understand that. And I understand your concern.
But let's start with an easy solution. I show the code and then explain it to you:
#include <iostream>
#include <fstream>
#include <string>
int main() {
// Open the source text file, and check, if there was no failure
if (std::ifstream fCode{ "r:\\code.txt" }; fCode) {
size_t tripletCounter{ 0 };
// Now, read all triplets from the file in a simple for loop
for (std::string triplet{}; fCode >> triplet; ) {
// Prepare output
std::cout << "\ni:\t" << tripletCounter++ << "\tcodeColumn:\t";
// Go through the triplet, search for comma, then output the parts
for (size_t i{ 0U }, startpos{ 0U }; i <= triplet.size(); ++i) {
// So, if there is a comma or the end of the string
if ((triplet[i] == ',') || (i == (triplet.size()))) {
// Print substring
std::cout << (triplet.substr(startpos, i - startpos)) << ' ';
startpos = i + 1;
}
}
}
}
else {
std::cerr << "\n*** Error, Could not open source file\n";
}
return 0;
}
You see, we need just a few lines of easy to understand code that will fullfil your requirements and produce the desired output.
Some maybe for you new features:
The if statement with initializer. This is available since C++17. You can (in addition to the condition) define a variable and initalize it. So, in
if (std::ifstream fCode{ "r:\\code.txt" }; fCode) {
we first define a variable with name "fCode" of type std::ifstream. We use the uniform initialzer "{}", to initialze it with the input file name.
This will call the constructor for the variable "fCode", and open the file. (This is was this constructor does). After the closing "}" of the "if-statement" the variable "fCode" will fall out of scope and the destructor for the std::ifstream will be called. This will close the file automatically.
This type of if-statement has been introduced to help to prevent name space solution. The variable shall only be visible in the scope, where it is used. Without that, you would have to define the std::ifstream outside (before) the if and it would be visible for the outer context and the file would be closed at a very late time. So, please get aquainted to that.
Next we define the a "tripletCounter". That is hust necessary for output. There is no other usage.
Then, again such an if-statement with initailizer. We first define an empty std::string "triplet" and then use the extractor operator to read text until the next white space. This is how the "extractor" (>>) works. We use the whole expression as condition, to check, if the extraction worlked, or if we hit the end of file (or some other error). This works because the extractor operator returns the stream in that is was working, so a reference to "fCode". And the stream has on overwritten boolen operator !, to check the condition of the stream. Please see here.
You should always and for every IO-Operation check, if it worked or not.
So, next we split the triple (e.g. "0,1,0") into its sub-strings with an very easy for loop. We go through all characters in the string and check, if the current chacter is a comma or the end of string. In that case, we output, the characters before the delimiter.
Very simple and easy to understand. std::getline is not needed here.
So, next solution, more advanced:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <iterator>
#include <regex>
std::regex re(",");
int main() {
// Open the source text file, and check, if there was no failure
if (std::ifstream fCode{ "r:\\code.txt" }; fCode) {
size_t tripletCounter{ 0 };
// Now, read all triplets from the file into a vector
std::vector triplets(std::istream_iterator<std::string>(fCode), {});
// Next, go through all triplets
for (const std::string &triplet : triplets) {
// Prepare output
std::cout << "\ni:\t" << tripletCounter++ << "\tcodeColumn:\t";
// Split triplet into code column. All codes are in vector codeColums
std::vector codeColumns(std::sregex_token_iterator(triplet.begin(), triplet.end(), re, -1), {});
//Show codes
for (const std::string& code : codeColumns) std::cout << code << ' ';
}
}
else {
std::cerr << "\n*** Error, Could not open source file\n";
}
return 0;
}
The beginning is the same. But then:
// Now, read all triplets from the file into a vector
std::vector triplets(std::istream_iterator<std::string>(fCode), {});
UhOh. Whats that. Let's start with the std::istream_iterator. If you read the linked description, then you will find out, that it will basically call the extractor operator >> for the specified type. And since it is an iterator, it will call it again and again, if the iterator is incremented. Ok, understandable, but then
We define variable triplets as std::vector and call its constructor with 2 arguments. That constructor is the the so called range constructor of the std::vector. Please see the descrition for constructor 5. Aha, it gets a "begin()" iterator and an "end()" iterator. Aha, but what is this strange {} instead of the "end()"-iterator. This is the default initializer (please see here and here. And if we look at the description of the std::istream_iterator we can see the the default is the end iterator. OK, understood.
I assum that you know about the range based for, which comes next. Good. But now, we come to the most difficult point. Splitting a string with delimiters. People are using std::getline. But why? Why are people doing such strange stuff?
What do people expect from the function, when they read
getline ?
Most people would say, Hm, I guess it will read a complete line from somewhere. And guess what, that was the basic intention for this function. Read a line from a stream and put it into a string.
As you can see here std::getline has some additional functionality.
And this lead to a major misuse of this function for splitting up std::strings into tokens.
Splitting strings into tokens is a very old task. In very early C there was the function strtok, which still exists, even in C++. Please see std::strtok.
But because of the additional functionality of std::getline is has been heavily misused for tokenizing strings. If you look on the top question/answer regarding how to parse a CSV file (please see here), then you will see what I mean.
People are using std::getline to read a text line, a string, from the original stream, then stuffing it into an std::istringstream again and use std::getline with delimiter again to parse the string into tokens.
Weird.
Because, since many many years, we have a dedicated, special function for tokenizing strings, especially and explicitly designed for that purpose. It is the
std::sregex_token_iterator
And since we have such a dedicated function, we should simply use it.
This thing is an iterator. For iterating over a string, hence the function name is starting with an s. The begin part defines, on what range of input we shall operate, (begin(), end()), then there is a std::regex for what should be matched / or what should not be matched in the input string. The type of matching strategy is given with last parameter.
0 --> give me the stuff that I defined in the regex and
-1 --> give me that what is NOT matched based on the regex.
We can use this iterator for storing the tokens in a std::vector. The std::vector has a range constructor, which takes 2 iterators as parameter, and copies the data between the first iterator and 2nd iterator to the std::vector. The statement
std::vector tokens(std::sregex_token_iterator(s.begin(), s.end(), re, -1), {});
defines a variable “tokens” as a std::vector and uses again the range-constructor of the std::vector. Please note: I am using C++17 and can define the std::vector without template argument. The compiler can deduce the argument from the given function parameters. This feature is called CTAD ("class template argument deduction"). I also used that for the vector above.
Additionally, you can see that I do not use the "end()"-iterator explicitly.
This iterator will be constructed from the empty brace-enclosed default initializer with the correct type, because it will be deduced to be the same as the type of the first argument due to the std::vector constructor requiring that, as already described.
You can read any number of tokens in a line and put it into the std::vector
But you can do even more. You can validate your input. If you use 0 as last parameter, you define a std::regex that even validates your input. And you get only valid tokens.
Overall, the usage of a dedicated functionality is superior over the misused std::getline and people should simply use it.
Some people may complain about the function overhead, but how many of them are using big data. And even then, the approach would be probably then to use string.findand string.substring or std::stringviews or whatever.
So, somehow advanced, but you will eventually learn it.
And now we will use an object oriented approach. As you know, C++ is an object oriented language.
We can put data, and methods working with that data, in a class (struct). The functionality is encapsulated. Only the class should know, how to operate on its data. Sw, we will define a class "Code". This contains a std::array consisting of 3 st::strings. and associated functions. For the array we made a typedef for easier writing. The functions that we need, are input and output. So, we will overwrite the extractor and the inserter operator.
In these operators, we use functions as dscribed above.
And as a result of all this work, we get an elegant main function, where all the work is done in 3 lines of code.
Please see:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <iterator>
#include <regex>
#include <array>
#include <algorithm>
using Triplet = std::array<std::string, 3>;
std::regex re(",");
struct Code {
// Our Data
Triplet triplet{};
// Overwrite extractor operator for easier input
friend std::istream& operator >> (std::istream& is, Code& c) {
// Read a triplet with commans
if (std::string s{}; is >> s) {
// Copy the single columns of the triplet in to our internal Data structure
std::copy(std::sregex_token_iterator(s.begin(), s.end(), re, -1), {}, c.triplet.begin());
}
return is;
}
// Overwrite inserter for easier output
friend std::ostream& operator << (std::ostream& os, const Code& c) {
return os << c.triplet[0] << ' ' << c.triplet[1] << ' ' << c.triplet[2];
}
};
int main() {
// Open the source text file, and check, if there was no failure
if (std::ifstream fCode{ "r:\\code.txt" }; fCode) {
// Now, read all triplets from the file, split it and put the Codes into a vector
std::vector code(std::istream_iterator<Code>(fCode), {});
// Show output
for (size_t tripletCounter{ 0U }; tripletCounter < code.size(); tripletCounter++)
std::cout << "\ni:\t" << tripletCounter << "\tcodeColumn:\t" << code[tripletCounter];
}
else {
std::cerr << "\n*** Error, Could not open source file\n";
}
return 0;
}

can I std::find a string in a stringstream?

I have a stringstream that I'd like to iterate and determine if a substring exists in it.
I know that I could just convert to a string and do std::string::find(), but I was just hoping to avoid the conversion from stringstream to string if possible.
I understand the following won't work because the istream_iterator uses char as its type (not string)
stringstream ssBody;
string sFindThis;
...
auto itr = std::find (
istreambuf_iterator<char>(ssBody),
istreambuf_iterator<char>(),
sFindThis
);
But can I somehow search for a string in stringstream with std::find or similar without a conversion to string?
The C++ standard does not define any std::[io]?stringstream methods for searching its contents.
Neither can you use std::istreambuf_iterator together with std::search(), since std::istreambuf_iterator is an input iterator, but std::search() requires a forward iterator.
The only effective way to search a string stream is to convert it to a std::string, first.
using pubsetbuf it is possible to associate a buffer with basic_stringbuf member and then search the buffer, however behavior of this function is implementation defined. explanations and the example are from http://en.cppreference.com/w/cpp/io/basic_stringbuf/setbuf
#include <iostream>
#include <sstream>
int main()
{
std::ostringstream ss;
char c[1024] = {};
ss.rdbuf()->pubsetbuf(c, 1024);
ss << 3.14 << '\n';
std::cout << c << '\n';
}

How do I use write with stringstream?

I have a vector<char> of data which I want to write into std::stringstream.
I tried:
my_ss.write(vector.data(), vector.size());
...but it seems to put nothing into my_ss which I declared as follows:
std::stringstream my_ss( std::stringstream::binary);
Why write is not working (app does not crash and compiles with 0 errors, 0 warnings)?
For the "how do I do it" you can use a std::ostream_iterator:
std::copy(vector.begin(), vector.end(), std::ostream_iterator<char>(my_ss));
Complete example:
#include <iterator>
#include <sstream>
#include <vector>
#include <iostream>
#include <algorithm>
int main() {
std::vector<char> vector(60, 'a');
std::ostringstream my_ss;
std::copy(vector.begin(), vector.end(), std::ostream_iterator<char>(my_ss));
std::cout << my_ss.str() << std::endl;
}
You could also just use that to construct a string directly, without going via a stringstream at all:
std::string str(vector.begin(), vector.end()); // skip all of the copy and stringstream
Though you haven't given any code, it sounds like you probably just wrote:
std::stringstream my_ss (std::stringstream::binary);
If you wish to write to a stringstream you need to combine the flag std::stringstream::out in the constructor. If I'm right, then you would see things working fine if you changed this to:
std::stringstream my_ss (std::stringstream::out | std::stringstream::binary);
(Obviously if you wish to read from that stringstream you need to add std::stringstream::in)
UPDATE Now that you've given your code...yup, this is your specific problem. Note #awoodland's point about the fact that you can just construct a string from a vector of chars instead (if that's the only thing you were planning on doing with this stream.)
The default parameter for the mode of stringbuf in stringstream is out|in.
explicit basic_stringstream(ios_base::openmode _Mode =
ios_base::in | ios_base::out)
: _Mybase(&_Stringbuffer),
_Stringbuffer(_Mode)
{ // construct empty character buffer
}
You need to add stringstream::out if you pass something explicitly like stringstream:binary
Or just use std::ostringstream

Character by Character Input from a file, in C++

Is there any way to get input from a file one number at a time?
For example I want to store the following integer in an vector of integers since it is so long and can't be held by even a long long int.
12345678901234567900
So how can I read this number from a file so that I can:
vector<int> numbers;
number.push_back(/*>>number goes here<<*/)
I know that the above code isn't really complete but I hope that it explains what I am trying to do.
Also I've tried google and so far it has proved innefective because only tutorials for C are coming up which aren't really helping me all too much.
Thank is advance,
Dan Chevalier
This could be done in a variety of ways, all of them boiling down to converting each char '0'..'9' to the corresponding integer 0..9. Here's how it can be done with a single function call:
#include <string>
#include <iostream>
#include <vector>
#include <iterator>
#include <functional>
#include <algorithm>
int main()
{
std::string s = "12345678901234567900";
std::vector<int> numbers;
transform(s.begin(), s.end(), back_inserter(numbers),
std::bind2nd(std::minus<char>(), '0'));
// output
copy(numbers.begin(), numbers.end(),
std::ostream_iterator<int>(std::cout, " "));
std::cout << '\n';
}
When reading from a file, you could read the string and transform(), or even transform() directly from istream iterators, if there is nothing else in that file besides your number:
std::ifstream f("test.txt");
std::vector<int> numbers;
transform(std::istream_iterator<char>(f),
std::istream_iterator<char>(),
back_inserter(numbers),
std::bind2nd(std::minus<char>(), '0'));
Off the top of my head this should fill up a character array which you can then iterate through. I realize it's not exactly what you were after but it's my preferred method.
void readfile(char *string)
{
ifstream NumberFile;
NumberFile.open("./Number"); //For a unix file-system
NumberFile >> string;
NumberFile.close();
}
Also, to perform operations on the actual numbers you can use:
CharacterArray[ElementNumber] - '0'
and to get the number when it is small enough to fit in a datatype you add each element of the array multiplied by 10 to the power of its index.
You can read a char at a time with char c; cin.get(c); and convert it to the numeral with c -= '0'. But perhaps you can just read it as a string or use something like BigNum.

How to read and write a STL C++ string?

#include<string>
...
string in;
//How do I store a string from stdin to in?
//
//gets(in) - 16 cannot convert `std::string' to `char*' for argument `1' to
//char* gets (char*)'
//
//scanf("%s",in) also gives some weird error
Similarly, how do I write out in to stdout or to a file??
You are trying to mix C style I/O with C++ types. When using C++ you should use the std::cin and std::cout streams for console input and output.
#include <string>
#include <iostream>
...
std::string in;
std::string out("hello world");
std::cin >> in;
std::cout << out;
But when reading a string std::cin stops reading as soon as it encounters a space or new line. You may want to use std::getline to get a entire line of input from the console.
std::getline(std::cin, in);
You use the same methods with a file (when dealing with non binary data).
std::ofstream ofs("myfile.txt");
ofs << myString;
There are many way to read text from stdin into a std::string. The thing about std::strings though is that they grow as needed, which in turn means they reallocate. Internally a std::string has a pointer to a fixed-length buffer. When the buffer is full and you request to add one or more character onto it, the std::string object will create a new, larger buffer instead of the old one and move all the text to the new buffer.
All this to say that if you know the length of text you are about to read beforehand then you can improve performance by avoiding these reallocations.
#include <iostream>
#include <string>
#include <streambuf>
using namespace std;
// ...
// if you don't know the length of string ahead of time:
string in(istreambuf_iterator<char>(cin), istreambuf_iterator<char>());
// if you do know the length of string:
in.reserve(TEXT_LENGTH);
in.assign(istreambuf_iterator<char>(cin), istreambuf_iterator<char>());
// alternatively (include <algorithm> for this):
copy(istreambuf_iterator<char>(cin), istreambuf_iterator<char>(),
back_inserter(in));
All of the above will copy all text found in stdin, untill end-of-file. If you only want a single line, use std::getline():
#include <string>
#include <iostream>
// ...
string in;
while( getline(cin, in) ) {
// ...
}
If you want a single character, use std::istream::get():
#include <iostream>
// ...
char ch;
while( cin.get(ch) ) {
// ...
}
C++ strings must be read and written using >> and << operators and other C++ equivalents. However, if you want to use scanf as in C, you can always read a string the C++ way and use sscanf with it:
std::string s;
std::getline(cin, s);
sscanf(s.c_str(), "%i%i%c", ...);
The easiest way to output a string is with:
s = "string...";
cout << s;
But printf will work too:
[fixed printf]
printf("%s", s.c_str());
The method c_str() returns a pointer to a null-terminated ASCII string, which can be used by all standard C functions.