Don't understand cplusplus.com example for istream::read - c++

On cplusplus.com an example is given:
// read a file into memory
#include <iostream> // std::cout
#include <fstream> // std::ifstream
int main () {
std::ifstream is ("test.txt", std::ifstream::binary);
if (is) {
// get length of file:
is.seekg (0, is.end);
int length = is.tellg();
is.seekg (0, is.beg);
char * buffer = new char [length];
std::cout << "Reading " << length << " characters... ";
// read data as a block:
is.read (buffer,length);
if (is)
std::cout << "all characters read successfully.";
else
std::cout << "error: only " << is.gcount() << " could be read";
is.close();
// ...buffer contains the entire file...
delete[] buffer;
}
return 0;
}
Can someone please explain why the last if (is) can determine if all characters have been read? It's the same if statement we're already in and the way I interpreted it (probably too simplistically and false at that) we only check if is exists, but wasn't this already established?

std::ifstream has a conversion operator to bool, which returns whether or not badbit or failbit is set on the stream.
It is essentially shorthand for if (!is.fail()) {/*...*/}.

std::ifstream defines operator bool() const, which implicitly converts the stream to a boolean.
From cplusplus.com on operator bool():
Returns whether an error flag is set (either failbit or badbit).
Notice that this function does not return the same as member good, but
the opposite of member fail.
http://www.cplusplus.com/reference/ios/ios/operator_bool/

Related

c++ fstream::read only returning 1st char

Preface: I am a inexperienced coder so its probably an obvious error. Also like all of this code is stolen and slapped together so I claim no ownership of this code.
System: I am using windows 10 64 bit. I write my code in Notepad++ and compile with MinGW G++.
What I'm trying to do: I am trying to read an entire file (BMP format) into a variable and return a pointer to that variable as the return of a function.
What's happening: The variable is only storing the first char of the file.
char* raw_data(std::string filename){
//100% non-stolen
std::ifstream is (filename, std::ifstream::binary);
if (is) {
// get length of file:
is.seekg (0, is.end);
int length = is.tellg();
is.seekg (0, is.beg);
std::cout << is.tellg() << "\n";
char * buffer = new char [length];
std::cout << "Reading " << length << " characters... \n";
// read data as a block:
is.read (buffer,length);
std::cout << "\n\n" << *buffer << "\n\n";
if (is)
{std::cout << "all characters read successfully.";}
else
{std::cout << "error: only " << is.gcount() << " could be read";}
is.close();
// ...buffer contains the entire file...
//101% non-stolen
return {buffer};
}
return {};
}
The code calling the function is
char * image_data = new char [image_size];
image_data = raw_data("Bitmap.bmp");
This compiles fine and the EXE outputs
0
Reading 2665949 characters...
B
all characters read successfully.
The file Bitmap.bmp starts:
BM¶ƒ 6 ( € ‰ €ƒ Δ Δ ¨δό¨δό¨δό¨
As you can see, the variable buffer only stores the first char of Bitmap.bmp (if I change the 1st char it also changes)
Any help would be appreciated.
Thank you for your time.
std::cout << "\n\n" << *buffer << "\n\n";
Buffer is a char*, so by dereferencing it you get a single char, which in your case is B. If you want to output the whole data that you read just don't dereference the pointer, in C/C++ char* has special treatment when outputing with std::cout,printf and such.
std::cout << "\n\n" << buffer << "\n\n";
Keep in mind that by convention, C-strings in char* should be null-terminated, yours is not and the caller of your function has no effective way to check how long it is, that information is lost as functions like strlen expect the Cstring to be null-terminated too. You should look at std::vector<char> or std::string for holding such data, as they will hold the information about the size, and clean after themselves.

Check istreambuf_iterator fail

We can read a whole file into a string:
std::ifstream ifs(path);
assert(ifs.good());
std::string text(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>())
Once this code returned an empty string. How can I check that there were no errors during the reading?
UPD:
I've learned that if a file is being written (or just have been overwritten), then when I read the file, std::filesystem::file_size may return 0, and ifstream returns true from operator bool (on Windows). So the file is not available for some time, but I get no errors and I can't distinguish this case from a case of real empty file. So I have to read a file in a loop while its size is 0 for some time.
The easiest way to check whether the stream has errors is to use operator bool after every operation on streams.
#include <iostream>
#include <fstream>
#include <string>
int main()
{
std::string file_name{"data.txt"};
std::ifstream ifs(file_name);
if ( !ifs) // <---
std::cout << "Error: unable to open " << file_name << ".\n";
std::string text{ std::istreambuf_iterator<char>(ifs),
std::istreambuf_iterator<char>() };
// ^ ^
if ( ifs ) // <--
std::cout << text << '\n';
else
std::cout << "An error occured while reading the file.\n";
}
Note that OP's snippet suffers from the Most Vexing Parse, which can be fixed using the list-initialization of the string.

Weird behavior with ifstreams and rdbuf()

I've noticed that using .rdbuf() on an ifstream seems to change it somehow. The following code should show the problem.
#include <fstream>
#include <iostream>
using namespace std;
int main(int argc, const char * argv[]) {
ifstream ifs("Sample.csv");
cout << "Reading buffer: " << endl;
cout << ifs.rdbuf(); // Outputs buffer as expected
cout << "Reading buffer again: " << endl;
cout << ifs.rdbuf(); // Returns nothing
return 0;
}
The reason this is bothering me is that I'm currently trying to copy the contents of one text file into another using ofstream ofs; ofs << ifs.rdbuf(). This works fine but makes reading from ifs using getline(ifs, str) fail, effectively "breaking" the stream.
This isn't particularly "weird"; it's the same stream behaviour you see every day. rdbuf isn't like std::stringstream::str() and it isn't magic — it's a pointer to the buffer, that your cout is then reading from just as you would read from the original stream yourself:
std::stringstream ss("1");
int x;
if (ss >> x)
cout << x;
if (ss >> x) // doesn't work a second time; "1" is already extracted
cout << x;
As your stream is a file stream, you can seek it back to the beginning to start from scratch (which will inherently do the same to its underlying buffer).
ifs.rdbuf() returns a pointer to the ifs's corresponding stream buffer object. Sending it to std::cout via << overload pulls information from the stream until the end of the buffer is reached (eof). Calling .rdbuf() again returns "nothing" because there's nothing to read at the end of the buffer. The buffer seek position be explicitly reset to zero by calling ifs.seekg (0);.

writing after reading using fstream

I am under the impression fstream objects in c++ can be used to both read and write, using the same stream.
I have successfully been able to first write to a stream and then read from it. If I however try to write to it again the file is not affected.
Here is a code example that successfully compiles on windows using MinGw:
int main()
{
std::string path="file.txt";
std::fstream fs(path.c_str());
int buffSize=100;
int bytesRead=0;
char* buffer=new char[buffSize];
fs.write("hello", 5);
fs.seekp(0, std::ios::beg);
fs.read(buffer, buffSize);
bytesRead=fs.gcount();
for(int i=0;i<bytesRead;i++) {std::cout << buffer[i];}
std::cout << "\n";
fs.clear();
fs.seekp(1, std::ios::beg);
fs.write("E", 1);
std::cout << "fail: " << fs.fail() << "\n";
delete[] buffer;
}
The initial content of "file.txt" was only:
AAAAAAA
And the program outputs:
helloAA
fail: 0
Looking at the file in a text editor after running the program shows that the final content is:
helloAA
The final writing of the "E" has not taken effect, why is this and how do I fix it?
EDIT:
I tried using fs.clear() before writing again as user 0x499602D2 suggested. Also added a line printing out whether the failbit or badbit has been set or not and updated the program output. The final file content stays the same however, the problem remains.
(more verbose answer from what I posted in comments on the question)
You need to call flush() on output stream objects (derived from ostream) in order for the data to actually be written on the output stream. More information on flush() is available on this c++ reference page.
This work in GCC 4.9.0 and VS2013.
Notes:
seekg is for move the read pointer
seekp is for move the write pointer
In the sample code in line fs.seekp(0, std::ios::beg); need to be seekg. There is no problem because the read pointer has not been move (there is no read until there).
Code:
#include <algorithm>
#include <iostream>
#include <fstream>
using namespace std;
int main(int argc, char* argv[]) {
std::string path = "H:\\save.txt";
int buffSize = 100;
int bytesRead = 0;
char* buffer = new char[buffSize];
std::fstream fs(path.c_str());
fs.write("hello", 5);
fs.flush(); // flushing to disk file
fs.seekg(0, std::ios_base::beg); // moving the read pointer
fs.read(buffer, buffSize);
bytesRead = fs.gcount();
for (int i = 0; i < bytesRead; i++) {
std::cout << buffer[i];
}
std::cout << "\n";
fs.clear();
fs.seekp(1, std::ios::beg);
fs.write("E", 1);
fs.flush(); // flushing to disk file
std::cout << "fail: " << fs.fail() << "\n";
delete[] buffer;
return 0;
}
string data="";
string Newdata="New Data";
std::fstream output_file(fileName, ios::in| ios::out);
output_file >> data; //read Data
output_file.seekg( 0, ios::beg );//set point to zero
output_file<<Newdata<<"\n"; //write new Data
output_file.close();
once you read a file using fstream, tellg < read pointer > and tellp < write pointer > points to -1.
to be able to write again using fstream, just call fstream.clear() and it will reset read and write pointer to where it was before reading.
none of the solution posted above work but fstream.clear() works.

How can I assign a std::string from a char * [duplicate]

This question already has answers here:
How do I read an entire file into a std::string in C++?
(23 answers)
Closed 8 years ago.
This may be trivial, but I'm new to C++ and an getting confused here.
I have the method:
bool load_database_file( const std::string& filename, std::string& contents ) {
std::ifstream is (filename, std::ifstream::binary);
if (is) {
// get length of file:
is.seekg (0, is.end);
int length = (int)is.tellg();
is.seekg (0, is.beg);
char * buffer = new char [length];
std::cout << "Reading " << length << " characters... ";
// read data as a block:
is.read (buffer, length);
if (is)
std::cout << "all characters read successfully.";
else
std::cout << "error: only " << is.gcount() << " could be read";
is.close();
// ...buffer contains the entire file...
std::string str(buffer);
contents = str;
delete[] buffer;
}
return true ;
}
where I would like to read a file and assign it's contents to contents so that it can be read by the calling function.
My issue is that after this function is run, I see that only the first character of buffer was copied to contents.
How can I copy/convert the entire contents of buffer (a char *) to contents (a std::string).
std::string str(buffer);
Should be:
std::string str(buffer, buffer+length);
Otherwise, how can the constructor know how many bytes to allocate/copy?
By the way, your code is needlessly clumsy. Why not read directly into the string's buffer rather than using a separate buffer that you have to allocate and free just to hold the data before you allocate another buffer?