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';
Related
Hi I am working with existing C++ code, I normally use VB.NET and much of what I am seeing is confusing and contradictory to me.
The existing code loads neural network weights from a file that is encoded as follows:
2
model.0.conv.conv.weight 5 3e17c000 3e9be000 3e844000 bc2f8000 3d676000
model.0.conv.bn.weight 7 4006a000 3f664000 3fc98000 3fa6a000 3ff2e000 3f5dc000 3fc94000
The first line gives the number of subsequent lines. Each of these lines has a description, a number representing how many values follow, then the weight values in hex. In the real file there are hundreds of rows and each row might have hundreds of thousands of weights. The weight file is 400MB in size. The values are converted to floats for use in the NN.
It takes over 3 minutes to decode this file. I am hoping to improve performance by eliminating the conversion from hex encoding to binary and just store the values natively as floats. The problem is I cant understand what the code is doing, nor how I should be storing the values in binary. The relevant section that decodes the rows is here:
while (count--)
{
Weights wt{ DataType::kFLOAT, nullptr, 0 };
uint32_t size;
// Read name and type of blob
std::string name;
input >> name >> std::dec >> size;
wt.type = DataType::kFLOAT;
// Load blob
uint32_t* val = reinterpret_cast<uint32_t*>(malloc(sizeof(val) * size));
for (uint32_t x = 0, y = size; x < y; ++x)
{
input >> std::hex >> val[x];
}
wt.values = val;
wt.count = size;
weightMap[name] = wt;
}
The Weights class is described here. DataType::kFLOAT is a 32bit float.
I was hoping to add a line(s) in the inner loop below input >> std::hex >> val[x]; so that I could write the float values to a binary file as the values are converted from hex, but I dont understand what is going on. It looks like memory is being assigned to hold the values but sizeof(val) is 8 bytes and uint32_t are 4 bytes. Furthermore it looks like the values are being stored in wt.values from val but val contains integers not floats. I really dont see what the intent is here.
Could I please get some advice on how to store and load binary values to eliminate the hex conversion. Any advice would be appreciated. A lot.
Here's an example program that will convert the text format shown into a binary format and back again. I took the data from the question and converted to binary and back successfully. My feeling is it's better to cook the data with a separate program before consuming it with the actual application so the app reading code is single purpose.
There's also an example of how to read the binary file into the Weights class at the end. I don't use TensorRT so I copied the two classes used from the documentation so the example compiles. Make sure you don't add those to your actual code.
If you have any questions let me know. Hope this helps and makes loading faster.
#include <fstream>
#include <iostream>
#include <unordered_map>
#include <vector>
void usage()
{
std::cerr << "Usage: convert <operation> <input file> <output file>\n";
std::cerr << "\tconvert b in.txt out.bin - Convert text to binary\n";
std::cerr << "\tconvert t in.bin out.txt - Convert binary to text\n";
}
bool text_to_binary(const char *infilename, const char *outfilename)
{
std::ifstream in(infilename);
if (!in)
{
std::cerr << "Error: Could not open input file '" << infilename << "'\n";
return false;
}
std::ofstream out(outfilename, std::ios::binary);
if (!out)
{
std::cerr << "Error: Could not open output file '" << outfilename << "'\n";
return false;
}
uint32_t line_count;
if (!(in >> line_count))
{
return false;
}
if (!out.write(reinterpret_cast<const char *>(&line_count), sizeof(line_count)))
{
return false;
}
for (uint32_t l = 0; l < line_count; ++l)
{
std::string name;
uint32_t num_values;
if (!(in >> name >> std::dec >> num_values))
{
return false;
}
std::vector<uint32_t> values(num_values);
for (uint32_t i = 0; i < num_values; ++i)
{
if (!(in >> std::hex >> values[i]))
{
return false;
}
}
uint32_t name_size = static_cast<uint32_t>(name.size());
bool result = out.write(reinterpret_cast<const char *>(&name_size), sizeof(name_size)) &&
out.write(name.data(), name.size()) &&
out.write(reinterpret_cast<const char *>(&num_values), sizeof(num_values)) &&
out.write(reinterpret_cast<const char *>(values.data()), values.size() * sizeof(values[0]));
if (!result)
{
return false;
}
}
return true;
}
bool binary_to_text(const char *infilename, const char *outfilename)
{
std::ifstream in(infilename, std::ios::binary);
if (!in)
{
std::cerr << "Error: Could not open input file '" << infilename << "'\n";
return false;
}
std::ofstream out(outfilename);
if (!out)
{
std::cerr << "Error: Could not open output file '" << outfilename << "'\n";
return false;
}
uint32_t line_count;
if (!in.read(reinterpret_cast<char *>(&line_count), sizeof(line_count)))
{
return false;
}
if (!(out << line_count << "\n"))
{
return false;
}
for (uint32_t l = 0; l < line_count; ++l)
{
uint32_t name_size;
if (!in.read(reinterpret_cast<char *>(&name_size), sizeof(name_size)))
{
return false;
}
std::string name(name_size, 0);
if (!in.read(name.data(), name_size))
{
return false;
}
uint32_t num_values;
if (!in.read(reinterpret_cast<char *>(&num_values), sizeof(num_values)))
{
return false;
}
std::vector<float> values(num_values);
if (!in.read(reinterpret_cast<char *>(values.data()), num_values * sizeof(values[0])))
{
return false;
}
if (!(out << name << " " << std::dec << num_values))
{
return false;
}
for (float &f : values)
{
uint32_t i;
memcpy(&i, &f, sizeof(i));
if (!(out << " " << std::hex << i))
{
return false;
}
}
if (!(out << "\n"))
{
return false;
}
}
return true;
}
int main(int argc, const char *argv[])
{
if (argc != 4)
{
usage();
return EXIT_FAILURE;
}
char op = argv[1][0];
bool result = false;
switch (op)
{
case 'b':
case 'B':
result = text_to_binary(argv[2], argv[3]);
break;
case 't':
case 'T':
result = binary_to_text(argv[2], argv[3]);
break;
default:
usage();
break;
}
return result ? EXIT_SUCCESS : EXIT_FAILURE;
}
// Possible implementation of the code snippet in the original question to read the weights
// START Copied from TensorRT documentation - Do not include in your code
enum class DataType : int32_t
{
kFLOAT = 0,
kHALF = 1,
kINT8 = 2,
kINT32 = 3,
kBOOL = 4
};
class Weights
{
public:
DataType type;
const void *values;
int64_t count;
};
// END Copied from TensorRT documentation - Do not include in your code
bool read_weights(const char *infilename)
{
std::unordered_map<std::string, Weights> weightMap;
std::ifstream in(infilename, std::ios::binary);
if (!in)
{
std::cerr << "Error: Could not open input file '" << infilename << "'\n";
return false;
}
uint32_t line_count;
if (!in.read(reinterpret_cast<char *>(&line_count), sizeof(line_count)))
{
return false;
}
for (uint32_t l = 0; l < line_count; ++l)
{
uint32_t name_size;
if (!in.read(reinterpret_cast<char *>(&name_size), sizeof(name_size)))
{
return false;
}
std::string name(name_size, 0);
if (!in.read(name.data(), name_size))
{
return false;
}
uint32_t num_values;
if (!in.read(reinterpret_cast<char *>(&num_values), sizeof(num_values)))
{
return false;
}
// Normally I would use float* values = new float[num_values]; here which
// requires delete [] ptr; to free the memory later.
// I used malloc to match the original example since I don't know who is
// responsible to clean things up later, and TensorRT might use free(ptr)
// Makes no real difference as long as new/delete ro malloc/free are matched up.
float *values = reinterpret_cast<float *>(malloc(num_values * sizeof(*values)));
if (!in.read(reinterpret_cast<char *>(values), num_values * sizeof(*values)))
{
return false;
}
weightMap[name] = Weights { DataType::kFLOAT, values, num_values };
}
return true;
}
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.
Is there any way to add buffering to cin so that I can effectively use tellg and seekg on that istream? (I only need to go back about 6 characters.) Or is there perhaps some way to wrap the stream with a (perhaps custom) istream object that acts as a buffered pipe that would allow me to use tellg/seekg to revert the stream position a few characters? It might look like this:
BufferedIStream bis(cin);
streampos pos = bis.tellg();
MyObjectType t = getObjectType(bis);
bis.seekg(pos);
As a work-around, I'm currently reading cin to EOF into a string, and transferring that string to an istringstream, but this has numerous negative side-affects that I'd like to avoid.
The only other thing I can think to do is to overload all my scan/read functions on all my data classes with a private version (only used by the factory) where the header is assumed to already have been consumed, so that I can eliminate the need for tellg/seekg altogether. This would work fine, but would introduce a fair-amount of ugliness. In comparison, tellg/seekg is isolated to my factory and is just two lines of code. I hate to dump it.
You can create a filtering stream buffer, i.e., a class derived from std::streambuf. To support buffered reading you'd override underflow() to fill the next buffer of character once the input characters are consumed. To support limited seeking the previous buffer would not be discarded but rather partly retained. In addition you'd override seekoff().
Something like this should do the trick:
#include <iostream>
#include <streambuf>
#include <string>
#include <cstdlib>
#include <cstring>
class bufferbuf
: public std::streambuf {
enum { size = 2000, half = size / 2 };
char buffer[size];
std::streambuf* sbuf;
std::streamoff base;
public:
bufferbuf(std::streambuf* sbuf): sbuf(sbuf), base() {
auto read = sbuf->sgetn(this->buffer, size);
this->setg(this->buffer, this->buffer, this->buffer + read);
}
int underflow() {
if (this->gptr() == this->buffer + size) {
std::memmove(this->eback(), this->eback() + half, half);
base += half;
auto read = sbuf->sgetn(this->eback() + half, half);
this->setg(this->eback(), this->eback() + half, this->eback() + half + read);
}
return this->gptr() != this->egptr()
? traits_type::to_int_type(*this->gptr())
: traits_type::eof();
}
std::streampos seekoff(off_type offset,
std::ios_base::seekdir whence,
std::ios_base::openmode which) override {
if (this->gptr() - this->eback() < -offset
|| this->egptr() - this->gptr() < offset
|| whence != std::ios_base::cur
|| !(which & std::ios_base::in)) {
return pos_type(off_type(-1));
}
this->gbump(offset);
return pos_type(this->base + (this->gptr() - this->eback()));
}
std::streampos seekpos(pos_type pos, std::ios_base::openmode which) override {
if (off_type(pos) < this->base
|| this->base + (this->egptr() - this->eback()) < off_type(pos)
|| !(which & std::ios_base::in)) {
return pos_type(off_type(-1));
}
this->setg(this->eback(), this->eback() + (off_type(pos) - this->base), this->egptr());
return pos_type(base + (this->gptr() - this->eback()));
}
};
int main() {
bufferbuf buf(std::cin.rdbuf());
std::istream in(&buf);
// ...
std::string s0, s1;
bool relative(false);
if (relative) {
while (in >> s0
&& (in.seekg(-int(s0.size()), std::ios_base::cur), in >> s1)) {
std::cout << "read "
<< "s0='" << s0 << "' " << "s1='" << s1 << "'\n";
}
}
else {
for (std::streampos pos = in.tellg();
in >> s0 && (in.seekg(pos), in >> s1); pos = in.tellg()) {
std::cout << "read "
<< "s0='" << s0 << "' " << "s1='" << s1 << "'\n";
}
}
}
The code above works with a couple of simple test cases. It demonstrates uses of both relative and absolute positioning. Gennerally, I find it useless to seek in streams as generally every interesting lexing can be done with just one character look-ahead. As a result, I may have missed something in the context of position. However, I expect the above code to work proper.
This buffer should contain slots (three in this example) of equal length ( 20 in this example)
The buffer has to have contiguous memory so that it can be passed to a C function in non-const fashion.
const int slot_size = 20;
std::vector<char> vbuffer;
This function takes a string, copies to a temporary buffer of the required size then appeds it to vbuffer
void prepBuffer( const std::string& s)
{
std::vector<char> temp(slot_size);
std::copy(s.c_str(), s.c_str() + s.length() + 1, temp.begin());
vbuffer.insert(vbuffer.end(), temp.begin(), temp.end());
}
Testing the function
int main()
{
vbuffer.reserve(60);
prepBuffer( "Argentina");
prepBuffer( "Herzegovina");
prepBuffer( "Zambia");
cout << &vbuffer[0] << endl;
cout << &vbuffer[20] << endl;
cout << &vbuffer[40] << endl;
}
Question. There is a lot of string copying in my prepBuffer function. I am looking for a better way to fill up vbuffer with minimal copying
EDIT
The size of slots is determined elsewhere in the program. But it is not known at compile time.
EDIT
In line with my accepted answer below, I have settled on this version
void prepBuffer(const std::string& s)
{
assert(s.size() < slot_size );
vbuffer.insert(vbuffer.end(), s.begin(), s.end());
vbuffer.insert(vbuffer.end(), slot_size - s.size(), '\0' );
}
Suggestions are still welcome
How about this:
vbuffer.reserve(vbuffer.size() + 20);
vbuffer.insert(vbuffer.end(), s.begin(), s.end());
vbuffer.insert(vbuffer.end(), 20 - s.size(), '\0');
An additional check on the string length is recommended, along with a policy for handling over-long strings (e.g. assert(s.size() < 20);).
If you don't use std::string at all and avoid the temporary std::vector, you can easily do this without any extra dynamic allocation.
template <unsigned N>
void prepBuffer(char const (&s)[N])
{
std::copy(s, s + N, std::back_inserter(vbuffer));
vbuffer.resize(vbuffer.size() - N + 20);
}
Or, since the number of characters to be written is known ahead of time, you could just as easily use a nontemplate function:
void prepBuffer(char const* s)
{
unsigned n = vbuffer.size();
vbuffer.resize(n + 20);
while (*s && n != vbuffer.size())
{
vbuffer[n] = *s;
++n;
++s;
}
assert(*s == 0 && n != vbuffer.size());
// Alternatively, throw an exception or handle the error some other way
}
Another idea:
std::vector<std::array<char, 20> > prepped(3);
strncpy(prepped[0].begin(), "Argentina", 20);
strncpy(prepped[1].begin(), "Herzegovina", 20);
strncpy(prepped[2].begin(), "Zambia", 20);
You could write
typedef std::vector<std::array<char, 20> > prepped_t;
strncpy(..., ..., sizeof(prepped_t::value_type));
in case you wanted to be a bit more flexible when changing the size of the nested array
void prepBuffer( const char *s, std::size_t offset)
{
strncpy(&vbuffer[offset], s, 20);
}
Testing the function
int main()
{
vbuffer.resize(60);
prepBuffer( "Argentina", 0);
prepBuffer( "Herzegovina", 20);
prepBuffer( "Zambia", 40);
cout << &vbuffer[0] << endl;
cout << &vbuffer[20] << endl;
cout << &vbuffer[40] << endl;
}
That minimizes copying, at the cost of maintainability.
Here is nearly-optimal code that is still readable and maintainable.
std::string vbuffer;
void prepBuffer( const std::string& s)
{
vbuffer += s;
vbuffer.resize( ( (vbuffer.size() +19) / 20) * 20));
}
Testing the function
int main()
{
vbuffer.reserve(60);
prepBuffer( "Argentina");
prepBuffer( "Herzegovina");
prepBuffer( "Zambia");
cout << &vbuffer[0] << endl;
cout << &vbuffer[20] << endl;
cout << &vbuffer[40] << endl;
}
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;
}