I'm attempting to create a custom std::streambuf which acts as a sub-stream to a parent stream. This is an adaptation of the implementation outlined in this SO thread answer.
In this example below I am attempting to simply read the first 5 characters "Hello," of the stream. However, when I call ifstream.read() into the buffer, the buffer is filled with "ello, ", as though it is off by one.
#include <iostream>
#include <sstream>
using namespace std;
int main()
{
stringstream ss;
ss << "Hello, World!";
substreambuf asd(ss.rdbuf(), 0, 5);
istream istream(&asd);
char buffer[6] = { '\0' };
istream.read(buffer, sizeof(buffer));
cout << buffer << endl; //prints "ello, "
}
I am new to streambufs and I feel like I'm missing something obvious here. Any help would be appreciated.
Here is the definition for substreambuf:
#include <iostream>
#include <sstream>
namespace std
{
struct substreambuf : public streambuf
{
explicit substreambuf(streambuf* sbuf, streampos pos, streampos len) :
m_sbuf(sbuf),
m_pos(pos),
m_len(len),
m_read(0)
{
m_sbuf->pubseekpos(pos);
setbuf(nullptr, 0);
}
protected:
int underflow()
{
if (m_read >= m_len)
{
return traits_type::eof();
}
return m_sbuf->sgetc();
}
int uflow()
{
if (m_read >= m_len)
{
return traits_type::eof();
}
m_read += 1;
return m_sbuf->snextc();
}
streampos seekoff(streamoff off, ios_base::seekdir seekdir, ios_base::openmode openmode = ios_base::in | ios_base::out)
{
if (seekdir == ios_base::beg)
{
off += m_pos;
}
else if (seekdir == ios_base::cur)
{
off += m_pos + m_read;
}
else if (seekdir == ios_base::end)
{
off += m_pos + m_len;
}
return m_sbuf->pubseekpos(off, openmode) - m_pos;
}
streampos seekpos(streampos streampos, ios_base::openmode openmode = ios_base::in | ios_base::out)
{
streampos += m_pos;
if (streampos > m_pos + m_len)
{
return -1;
}
return m_sbuf->pubseekpos(streampos, openmode) - m_pos;
}
private:
streambuf* m_sbuf;
streampos m_pos;
streamsize m_len;
streampos m_read;
};
};
I thought this was strange when I first saw the code.
int uflow()
{
// ...
return m_sbuf->snextc();
}
Why is he returning the result of snextc()? The policy defined for uflow() is "return the next available character and advance the input stream by one character". If snextc() is called, the input sequence will be advanced, and then it will return the next character. The result is that at least 1 character is skipped.
The correct method to call would be sbumpc() because it will cache the next character first, advance the input stream, and then return it.
return m_sbuf->sbumpc();
Here is a demo.
Related
I have an assignment of saving some objects data in a specific order of its data members, I'll try to simplfy . Consider this Base class constructor from a binary file. (Please note that it was not my choice to use char *) .
Base(ifstream & in_file) {
int n;
in_file.read((char *)&n, sizeof(n));
m_var = new char[n + 1];
in_file.read(m_var, n);
m_var[n] = '\0';
in_file.read((char *)&m_intvar, sizeof(m_intvar));
}
It has to initialize m_var (char *) and another integer variable. This code works, though it requiers to save the length of the char * for me to allocate the memory.
The problem starts here. I was instructed not to save the size of the string, but to only enter a \n after each value i write to the file. So I need some how to read the file, and get the string until the \n character.
I was thinking about reading char by char, but couldn't find a way to do it, I assume there is an istream function that offers that. Some similar function to >> of a text file would also be good I assume.
After consulting cppreference.com I end up as follows:
#include <fstream>
#include <iostream>
#include <cstring>
class Base
{
public:
Base(std::istream & in_file) { // NOTE: changed to istream to allow reading from any stream not just files
in_file.read((char *)&n, sizeof(n));
char buffer[1024];
in_file.get(buffer, sizeof(buffer), '\n');
size_t gcount = in_file.gcount();
if (in_file.get() != '\n')
{
throw std::runtime_error("binary string to long"); // you may want to implement a loop here using peek() to check for newline
}
m_var = new char[gcount];
std::copy(buffer, buffer + gcount, m_var);
}
Base(int num, const char* strg)
: n(num)
, m_var(strdup(strg))
{
}
bool operator == (const Base& rhs)
{
return n == rhs.n && strcpy(m_var, rhs.m_var);
}
void write(std::ostream& out)
{
out.write((char*)&n, sizeof(n));
out.write(m_var, strlen(m_var));
out.write("\n", 1);
}
int n;
char* m_var = nullptr;
~Base()
{
delete m_var;
}
};
int main(int, char**)
{
Base b1(10, "Hello Word!");
{
std::ofstream out("testfile.bin");
b1.write(out);
}
std::ifstream in("testfile.bin");
Base b2(in);
if (b1 == b2)
{
std::cout << "read ok!" << std::endl;
}
else
{
std::cout << "read failed!" << std::endl;
}
return 0;
}
4 years later but I was having a similar problem and found this post.
You can read char by char as you mention, with a loop.
int i;
for(i=0;;i++){
cout<<i;
in_file.read(&buffer[i],sizeof(char));
if buffer[i]=='\n') break;
}
The only problem with the code I came up is it saves the '\n'. But you can and should replace it to the NULL char '\0' after having found the new line '\n'.
(buffer[i]=='\0')
Please correct me if I am mistaken.
I'm looking for some analog scanf("%1d", &sequence) for std::cin >> sequence.
For example:
for ( ; scanf("%1d", &sequence) == 1; ) {
printf("%d ", sequence);
}
stdin: 5341235
stdout: 5 3 4 1 2 3 5
How does it work in C++ ?!
for ( ; std::cin >> *some_magic* sequence; ) {
std::cout << sequence << " ";
}
you can do this if you want (the sequence variable must be of type char)
for ( ; std::cin.read(&sequence,1); ) {
sequence-='0';
std::cout << sequence << " ";;
}
With respect to input parsing there are a number of features unfortunately missing from IOStreams which are present for scanf(). Setting a field width for numeric types is one of them (another one is matching strings in inputs). Assuming you want to stay with formatted input, one way to deal with it is to create a filtering stream buffer which injects a space character after a given number of characters.
Another approach consists of writing a custom std::num_get<char> facet, to imbue() it into the current stream, and then just set up width. Instead of injecting spaces the actual character parsing would observe if either the end of the stream is reached or the number of allowed characters is exceeded. The corresponding code to use this facet would set up a custom std::locale but otherwise look like one would expect:
int main() {
std::istringstream in("1234567890123456789");
std::locale loc(std::locale(), new width_num_get);
in.imbue(loc);
int w(0);
for (int value(0); in >> std::setw(++w) >> value; ) {
std::cout << "value=" << value << "\n";
}
}
Here is a somewhat naive implementation of a corresponding std::num_get<char> facet which just collects the appropriate digits (assuming base 10) and then just calls std::stoi() to get the value converted. It can be done more flexible and more efficient but you get the picture:
#include <iostream>
#include <streambuf>
#include <sstream>
#include <locale>
#include <string>
#include <iomanip>
#include <cctype>
struct width_num_get
: std::num_get<char> {
auto do_get(iter_type it, iter_type end, std::ios_base& fmt,
std::ios_base::iostate& err, long& value) const
-> iter_type override {
int width(fmt.width(0)), count(0);
if (width == 0) {
width = -1;
}
std::string digits;
if (it != end && (*it == '-' || *it == '+')) {
digits.push_back(*it++);
++count;
}
while (it != end && count != width && std::isdigit(static_cast<unsigned char>(*it))) {
digits.push_back(*it);
++it;
++count;
}
try { value = std::stol(digits); }
catch (...) { err |= std::ios_base::failbit; } // should probably distinguish overflow
return it;
}
};
The first described approach could use code like this for reading integers with increasing width (I'm using different width to show that it can flexibly be set):
int main() {
std::istringstream in("1234567890123456789");
int w(0);
for (int value(0); in >> fw(++w) >> value; ) {
std::cout << "value=" << value << "\n";
}
}
Of course, the entire magic is in the little fw() which is a custom manipulator: it installs a filtering stream buffer if the currently used stream buffer isn't of the appropriate type and set the number for characters after which the a space should be injected. The filtering stream buffer reads individual characters and simply injects a space after the corresponding number of characters. The code could be something like this (which currently doesn't do clean-up once the stream is done - I'll add that next):
#include <iostream>
#include <streambuf>
#include <sstream>
class fieldbuf
: public std::streambuf {
std::streambuf* sbuf;
int width;
char buffer[1];
int underflow() {
if (this->width == 0) {
buffer[0] = ' ';
this->width = -1;
}
else {
int c = this->sbuf->snextc();
if (c == std::char_traits<char>::eof()) {
return c;
}
buffer[0] = std::char_traits<char>::to_char_type(c);
if (0 < this->width) {
--this->width;
}
}
this->setg(buffer, buffer, buffer + 1);
return std::char_traits<char>::to_int_type(buffer[0]);
}
public:
fieldbuf(std::streambuf* sbuf): sbuf(sbuf), width(-1) {}
void setwidth(int width) { this->width = width; }
};
struct fw {
int width;
fw(int width): width(width) {}
};
std::istream& operator>> (std::istream& in, fw const& width) {
fieldbuf* fbuf(dynamic_cast<fieldbuf*>(in.rdbuf()));
if (!fbuf) {
fbuf = new fieldbuf(in.rdbuf());
in.rdbuf(fbuf);
static int index = std::ios_base::xalloc();
in.pword(index) = fbuf;
in.register_callback([](std::ios_base::event ev, std::ios_base& stream, int index){
if (ev == std::ios_base::copyfmt_event) {
stream.pword(index) = 0;
}
else if (ev == std::ios_base::erase_event) {
delete static_cast<fieldbuf*>(stream.pword(index));
stream.pword(index) = 0;
}
}, index);
}
fbuf->setwidth(width.width);
return in;
}
The C fprintf() function returns the number of characters printed. Is there similar functionality in C++ when writing to a file with ofstream? I am interested in a solution that is compatible with C++03 if possible.
For example:
ofstream file("outputFile");
file << "hello";
// Here would I like to know that five characters were printed.
file << " world";
// Here I would like to know that six characters were printed.
What you're looking for is tellp().
You could use it like so:
ofstream file("outputFile");
auto pos1 = file.tellp();
file << "hello";
auto pos2 = file.tellp();
std::cout << pos2 - pos1 << std::endl;
Seek operations are rather expensive (primarily because they need to prepare streams to potentially switch between reading and writing). I'd personally rather use a filtering stream buffer which provides the counts, e.g.:
class countbuf: public std::streambuf {
std::streambuf* sbuf;
std::size_t count;
char buffer[256];
int overflow(int c) {
if (c != std::char_traits<char>::eof()) {
*this->pptr() = c;
this->pbump(1);
}
return this->sync() == -1
? std::char_traits<char>::eof()
: std::char_traits<char>::not_eof(c);
}
int sync() {
std::size_t size(this->pptr() - this->pbase());
this->count += size;
this->setp(this->buffer, this->buffer + 256);
return size == this->sbuf->sputn(this->pbase(), this->pptr() - this->pbase())
? this->sbuf->pubsync(): -1;
}
public:
countbuf(std::streambuf* sbuf): sbuf(sbuf), count() {
this->setp(buffer, buffer + 256);
}
std::size_t count() const { return count + this->pptr() - this->pbase(); }
std::size_t reset() const {
std::size_t rc(this->count());
this->sync();
this->count = 0;
return rc;
}
};
Once you got this stream buffer, you could just install into an std::ostream (and possibly package the construction into a custom stream class):
countbuf sbuf(std::cout.rdbuf()); // can't seek on this stream anyway...
std::ostream out(&sbuf);
out << "hello!\n" << std::flush;
std::cout << "count=" << out.reset() << '\n';
I would like to know if it is possible to inherit from std::ostream, and to override flush() in such a way that some information (say, the line number) is added to the beginning of each line. I would then like to attach it to a std::ofstream (or cout) through rdbuf() so that I get something like this:
ofstream fout("file.txt");
myostream os;
os.rdbuf(fout.rdbuf());
os << "this is the first line.\n";
os << "this is the second line.\n";
would put this into file.txt
1 this is the first line.
2 this is the second line.
flush() wouldn't be the function to override in this context, though you're on the right track. You should redefine overflow() on the underlying std::streambuf interface. For example:
class linebuf : public std::streambuf
{
public:
linebuf() : m_sbuf() { m_sbuf.open("file.txt", std::ios_base::out); }
int_type overflow(int_type c) override
{
char_type ch = traits_type::to_char_type(c);
if (c != traits_type::eof() && new_line)
{
std::ostream os(&m_sbuf);
os << line_number++ << " ";
}
new_line = (ch == '\n');
return m_sbuf.sputc(ch);
}
int sync() override { return m_sbuf.pubsync() ? 0 : -1; }
private:
std::filebuf m_sbuf;
bool new_line = true;
int line_number = 1;
};
Now you can do:
linebuf buf;
std::ostream os(&buf);
os << "this is the first line.\n"; // "1 this is the first line."
os << "this is the second line.\n"; // "2 this is the second line."
Live example
James Kanze's classic article on Filtering Streambufs has a very similar example which puts a timestamp at the beginning of every line. You could adapt that code.
Or, you could use the Boost tools that grew out of the ideas in that article.
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/array.hpp>
#include <cstring>
#include <limits>
// line_num_filter is a model of the Boost concept OutputFilter which
// inserts a sequential line number at the beginning of every line.
class line_num_filter
: public boost::iostreams::output_filter
{
public:
line_num_filter();
template<typename Sink>
bool put(Sink& snk, char c);
template<typename Device>
void close(Device&);
private:
bool m_start_of_line;
unsigned int m_line_num;
boost::array<char, std::numeric_limits<unsigned int>::digits10 + 4> m_buf;
const char* m_buf_pos;
const char* m_buf_end;
};
line_num_filter::line_num_filter() :
m_start_of_line(true),
m_line_num(1),
m_buf_pos(m_buf.data()),
m_buf_end(m_buf_pos)
{}
// put() must return true if c was written to dest, or false if not.
// After returning false, put() with the same c might be tried again later.
template<typename Sink>
bool line_num_filter::put(Sink& dest, char c)
{
// If at the start of a line, print the line number into a buffer.
if (m_start_of_line) {
m_buf_pos = m_buf.data();
m_buf_end = m_buf_pos +
std::snprintf(m_buf.data(), m_buf.size(), "%u ", m_line_num);
m_start_of_line = false;
}
// If there are buffer characters to be written, write them.
// This can be interrupted and resumed if the sink is not accepting
// input, which is why the buffer and pointers need to be members.
while (m_buf_pos != m_buf_end) {
if (!boost::iostreams::put(dest, *m_buf_pos))
return false;
++m_buf_pos;
}
// Copy the actual character of data.
if (!boost::iostreams::put(dest, c))
return false;
// If the character copied was a newline, get ready for the next line.
if (c == '\n') {
++m_line_num;
m_start_of_line = true;
}
return true;
}
// Reset the filter object.
template<typename Device>
void line_num_filter::close(Device&)
{
m_start_of_line = true;
m_line_num = 1;
m_buf_pos = m_buf_end = m_buf.data();
}
int main() {
using namespace boost::iostreams;
filtering_ostream myout;
myout.push(line_num_filter());
myout.push(std::cout);
myout << "this is the first line.\n";
myout << "this is the second line.\n";
}
What would be the best way to get the line number of the current line in a file that I have opened with a ifstream? So I am reading in the data and I need to store the line number that it is on so that I can display it later if the data doesn't match the specifications.
If you don't want to limit yourself to std::getline, then you could use class derived from std::streambuf, and which keeps track of the current line number:
class CountingStreamBuffer : public std::streambuf { /* see below */ };
// open file
std::ifstream file("somefile.txt");
// "pipe" through counting stream buffer
CountingStreamBuffer cntstreambuf(file.rdbuf());
std::istream is(&cntstreambuf);
// sample usage
is >> x >> y >> z;
cout << "At line " << cntstreambuf.lineNumber();
std::getline(is, str);
cout << "At line " << cntstreambuf.lineNumber();
Here is a sample implementation of CountingStreamBuffer:
#include <streambuf>
class CountingStreamBuffer : public std::streambuf
{
public:
// constructor
CountingStreamBuffer(std::streambuf* sbuf) :
streamBuf_(sbuf),
lineNumber_(1),
lastLineNumber_(1),
column_(0),
prevColumn_(static_cast<unsigned int>(-1)),
filePos_(0)
{
}
// Get current line number
unsigned int lineNumber() const { return lineNumber_; }
// Get line number of previously read character
unsigned int prevLineNumber() const { return lastLineNumber_; }
// Get current column
unsigned int column() const { return column_; }
// Get file position
std::streamsize filepos() const { return filePos_; }
protected:
CountingStreamBuffer(const CountingStreamBuffer&);
CountingStreamBuffer& operator=(const CountingStreamBuffer&);
// extract next character from stream w/o advancing read pos
std::streambuf::int_type underflow()
{
return streamBuf_->sgetc();
}
// extract next character from stream
std::streambuf::int_type uflow()
{
int_type rc = streamBuf_->sbumpc();
lastLineNumber_ = lineNumber_;
if (traits_type::eq_int_type(rc, traits_type::to_int_type('\n')))
{
++lineNumber_;
prevColumn_ = column_ + 1;
column_ = static_cast<unsigned int>(-1);
}
++column_;
++filePos_;
return rc;
}
// put back last character
std::streambuf::int_type pbackfail(std::streambuf::int_type c)
{
if (traits_type::eq_int_type(c, traits_type::to_int_type('\n')))
{
--lineNumber_;
lastLineNumber_ = lineNumber_;
column_ = prevColumn_;
prevColumn_ = 0;
}
--column_;
--filePos_;
if (c != traits_type::eof())
return streamBuf_->sputbackc(traits_type::to_char_type(c));
else
return streamBuf_->sungetc();
}
// change position by offset, according to way and mode
virtual std::ios::pos_type seekoff(std::ios::off_type pos,
std::ios_base::seekdir dir,
std::ios_base::openmode mode)
{
if (dir == std::ios_base::beg
&& pos == static_cast<std::ios::off_type>(0))
{
lastLineNumber_ = 1;
lineNumber_ = 1;
column_ = 0;
prevColumn_ = static_cast<unsigned int>(-1);
filePos_ = 0;
return streamBuf_->pubseekoff(pos, dir, mode);
}
else
return std::streambuf::seekoff(pos, dir, mode);
}
// change to specified position, according to mode
virtual std::ios::pos_type seekpos(std::ios::pos_type pos,
std::ios_base::openmode mode)
{
if (pos == static_cast<std::ios::pos_type>(0))
{
lastLineNumber_ = 1;
lineNumber_ = 1;
column_ = 0;
prevColumn_ = static_cast<unsigned int>(-1);
filePos_ = 0;
return streamBuf_->pubseekpos(pos, mode);
}
else
return std::streambuf::seekpos(pos, mode);
}
private:
std::streambuf* streamBuf_; // hosted streambuffer
unsigned int lineNumber_; // current line number
unsigned int lastLineNumber_;// line number of last read character
unsigned int column_; // current column
unsigned int prevColumn_; // previous column
std::streamsize filePos_; // file position
};
From an ifstream point of view there is no line number. If you read in the file line by line, then you just have to keep track of it yourself.
Use std::getline to read each line in one by one. Keep an integer indicating the number of lines you have read: initialize it to zero and each time you call std::getline and it succeeds, increment it.
An inefficient but dead simple way is to have a function that given a stream, it counts the new line characters from the beginning of the stream to the current position.
int getCurrentLine(std::istream& is)
{
int lineCount = 1;
is.clear(); // need to clear error bits otherwise tellg returns -1.
auto originalPos = is.tellg();
if (originalPos < 0)
return -1;
is.seekg(0);
char c;
while ((is.tellg() < originalPos) && is.get(c))
{
if (c == '\n') ++lineCount;
}
return lineCount;
}
In some code I am working on, I am only interested to know the line number if invalid input is encountered, in which case import is aborted immediately. Since the function is called only once the inefficiency is not really a problem.
The following is a full example:
#include <iostream>
#include <sstream>
int getCurrentLine(std::istream& is)
{
int lineCount = 1;
is.clear(); // need to clear error bits otherwise tellg returns -1.
auto originalPos = is.tellg();
if (originalPos < 0)
return -1;
is.seekg(0);
char c;
while ((is.tellg() < originalPos) && is.get(c))
{
if (c == '\n') ++lineCount;
}
return lineCount;
}
void ReadDataFromStream(std::istream& s)
{
double x, y, z;
while (!s.fail() && !s.eof())
{
s >> x >> y >> z;
if (!s.fail())
std::cout << x << "," << y << "," << z << "\n";
}
if (s.fail())
std::cout << "Error at line: " << getCurrentLine(s) << "\n";
else
std::cout << "Read until line: " << getCurrentLine(s) << "\n";
}
int main(int argc, char* argv[])
{
std::stringstream s;
s << "0.0 0.0 0.0\n";
s << "1.0 ??? 0.0\n";
s << "0.0 1.0 0.0\n";
ReadDataFromStream(s);
std::stringstream s2;
s2 << "0.0 0.0 0.0\n";
s2 << "1.0 0.0 0.0\n";
s2 << "0.0 1.0 0.0";
ReadDataFromStream(s2);
return 0;
}