How to get consistent responses from fstream? - c++

When I read in information via fstream, it has ocurred twice in two different programs, that the input given to my program isn't stable, even if a given file doesn't change.
In my most recent program, which is concerned with audio-reading. I'm doing a simple check on the first four letters in the file. These letters are supposed to be RIFF, which they also are - I checked.
So, in order to check the format of a given binary file, I buffer the first four letters and see if they are equal to 'RIFF'.
char buffer[4];
std::ifstream in(fn,std::ios::binary);
in.read(buffer,4);
if(buffer!="RIFF"){//Always wrong atm
std::cout << "INVALID WAV FILE: " << buffer << std::endl;
}
When I first made the program, I recall this working properly. Now though, I get an error via my own cout:
INVALID WAV FILE: RIFFýfK
Does anyone have any idea as to what has gone wrong? Perhaps a way to make fstream more consistent?

You're reading 4 characters but not adding a zero terminator, furthermore your comparison is wrong since you're not comparing strings equality, you should rather do:
char buffer[5];
std::ifstream in(fn, std::ios::binary);
in.read(buffer, 4);
buffer[4] = '\0'; // Add a zero-terminator at the end
if (strcmp(buffer,"RIFF")) { // If buffer isn't {'R','I','F','F','\0'}..
std::cout << "INVALID WAV FILE: " << buffer << std::endl;
}

Related

why I cant read a file to an integer vector?

well! I have a text file including some integer values and non-integers like character strings and white spaces so I want only to read integers values so I used a vector of integers but when I read the file the opining is ok but it seems the first input fails thus breaks the loop!!!
here is my main example:
ifstream in("file.txt");
if(in.fail())
cout << "opening failed!" << endl;
//opening is fine!
int value;
vector<int> v;
while(in >> value) // the problem here; it fails why?
{
cout << "ok"; // not printed
v.push_back(value);
}
cout << v.size() << endl; // 0??!!
this is the content of file.txt:
32 43 24 32
15 23
57
77 81
if I make a vector of chars it's ok but I want only to use one of integers
*** I already used a code like this and worked fine but now I don't know what happened??!!! it's really annoting
any help, comment, tip is welcome and appreciated
This line:
while(in >> value)
says while I can read integers...
But in the post this may not be true - you are not handling this case.
Either read stuff that is not integers and handle it. Or just read strings and then decide what to do.
In addition
cout << "ok"; // not printed
is because it is buffered.
Do this
cout << "ok" << flush; // printed
excuse me first for annoying you with nonsense question. finally I managed to discover the error:
in my main folder of project I unintentionally created a winrar file input.rar then I didn't remove it but rename it to input.txt it's ok I opened it manually and removed some unreadable characters. then I put inside it the content above of integers then my c++ application succeeds in opening it but can't read it.
*now I removed it input.txt which was input.rar and created a new document text input.txt and now everything is good!!!
thank you for your collaboration. and this post may help someone else.
* don't create rar file or other formats then rename them to be text files and try to read them via your c++ fstream because it'll fail in fact it'll produce an error-prone which looks impossible to solve

std::ifstream::read() reading less than requested and setting failbit for no obvious reason

I was trying to read an entire file into a buffer using std::ifstream which failed for no obvious reason, so I constructed a minimal code example that demonstrates the problem:
std::vector<char> vec;
vec.resize(1000);
std::ifstream file("G:/Pictures/Webcam/Snapshot_20110209.jpg");
file.exceptions(std::ifstream::badbit | std::ifstream::failbit | std::ifstream::eofbit);
std::cout << file.good() << std::endl;
try {
file.read(vec.data(), 100);
} catch (std::ios_base::failure f) {
std::cout << f.what() << " Characters extracted: " << file.gcount() << std::endl;
} catch (...) {
std::cout << "Some other error" << std::endl;
}
std::cout << "Done" << std::endl;
file.close();
The file I'm trying to read is 48kb of size, so reading 100 bytes shouldn't be the problem. The buffer in 1000 bytes big, so that should also be ok. Now, what happens is that the stream only reads 61 bytes and then sets the failbit. The output generated is the following:
1
ios_base::failbit set: iostream stream error Characters extracted: 61
Done
So for some reason the failbit is set after 61 bytes. If I read less than 61 bytes, it works. If I try to read more, it also fails at 61. I also tried other files of similar size, same problem. Some completely different files of different size showed the same behaviour, but after 166 bytes.
Now, if I use Qt's QFile class for reading the data, everything works fine and I can read the full file. The code looks like this then:
QFile file(path);
std::vector<char> buffer;
buffer.resize(file.size());
if (!file.open(QIODevice::ReadOnly)) return;
file.read(buffer.data(), file.size());
file.close();
I know, now you'd say here I'm only reading as much as the file's size is, but that is actually more than 61 bytes. Also reading a fixed 100 is no problem.
std::ifstream file("G:/Pictures/Webcam/Snapshot_20110209.jpg");
Whoops! You're opening the file in text mode.
Depending on your platform, any number of nasty binary characters could make the stream think the data flow has ended, since different platforms use different "sentinel characters" to signal this (e.g. Ctrl+Z or 0x1A on Windows — is there a 0x1A at byte 62?1).
Here:
std::ifstream file("G:/Pictures/Webcam/Snapshot_20110209.jpg", std::ios::binary);
1 I have a JPEG file with 0x05 at that location; a quick glance at an EXIF format description makes me think we are both looking at the TIFF data field describing the horizontal resolution encoding, as 0x1A01 and 0x0500 are common options.

Can't get ios::beg to go back to the beginning of the file

It always seems to be the things that should be no problem that cause problems for me. I don't get it. :/
So I'm trying to make sure that I understand how to manipulate text files. I've got two files, "infile.txt" and "outfile.txt". "infile.txt" has six numbers in it and nothing else. Here is the code I used to manipulate the files.
#include<fstream>
using std::ifstream;
using std::ofstream;
using std::fstream;
using std::endl;
using std::ios;
int main()
{
ifstream inStream;
ofstream outStream;//create streams
inStream.open("infile.txt", ios::in | ios::out);
outStream.open("outfile.txt");//attach files
int first, second, third;
inStream >> first >> second >> third;
outStream << "The sum of the first 3 nums is " << (first+second+third) << endl;
//make two operations on the 6 numbers
inStream >> first >> second >> third;
outStream << "The sum of the second 3 nums is " << (first+second+third) << endl;
inStream.seekg(0); //4 different ways to force the program to go back to the beginning of the file
//2. inStream.seekg(0, ios::beg);
//3. inStream.seekg(0, inStream.beg);
//4. inStream.close(); inStream.open("infile.txt");
//I have tried all four of these lines and only #4 works.
//There has got to be a more natural option than just
//closing and reopening the file. Right?
inStream >> first >> second >> third;
outStream << "And again, the sum of the first 3 nums is " << (first+second+third) << endl;
inStream.close();
outStream.close();
return 0;
}
Maybe I don't understand quite how the stream works, but I've seen a few sources that said that seekg(0) should move the index back to the start of the file. Instead, this is what I get out of it.
The sum of the first 3 nums is 8
The sum of the second 3 nums is 14
And again, the sum of the first 3 nums is 14
It went back, but not nearly in the way I would have hoped. Any idea why this happened? Why did my first three attempts fail?
As Bo Persson states, it may be because your input has
encountered end of file; it shouldn't, because in C++, a text
file is defined as being terminated by a '\n', but practically
speaking, if you're working under Windows, a lot of ways of
generating a file will omit this final '\n'—although it
is formally required, practical considerations will mean that
you'll make sure that it works even if the final '\n' is
missing. And I can't think of any other reason off hand why the
seekg's wouldn't work. inStream.seekg( 0 ) is, of course,
undefined behavior, but in practice, it will work pretty much
everywhere. inStream.seekg( 0, ios::beg ) is guaranteed to
work if inStream.good(), and is, IMHO, preferable to the
first form. (The single argument form of seekg is normally
only used with the results of a tellg as an argument.) And of
course, it only works if the actual input source supports
seeking: it won't work if you're reading from a keyboard or
a pipe (but presumably, "infile.txt" is neither).
In general, you should check the status of inStream after each
read, before using the results. But if the only problem is that
the file doesn't end with '\n', it's probable that the status
will be OK (!fail()) after the final read, even if you've
encountered end of file. In which case, you'll need clear()
anyway.
Note that the above comments are valid for C++-03 and precedent.
C++11 has changed the specification of the single argument form
of seekg, and requires it to reset eofbit before anything
else. (Why is this change only for the single argument form of
seekg, and not the two argument form? Oversight?)
The second input reaches end-of-file for the stream. That state sticks until you call inStream.clear() to clear its state (in addition to the seek).
With a C++11 compliant compiler, option 4 should also work as close and reopen will now clear the previous state. Older compilers might not do that.
Try:
inStream.seekg(0, ios_base::beg);

Reading file byte by byte with ifstream::get

I wrote this binary reader after a tutorial on the internet. (I'm trying to find the link...)
The code reads the file byte by byte and the first 4 bytes are together the magic word. (Let's say MAGI!) My code looks like this:
std::ifstream in(fileName, std::ios::in | std::ios::binary);
char *magic = new char[4];
while( !in.eof() ){
// read the first 4 bytes
for (int i=0; i<4; i++){
in.get(magic[i]);
}
// compare it with the magic word "MAGI"
if (strcmp(magic, "MAGI") != 0){
std::cerr << "Something is wrong with the magic word: "
<< magic << ", couldn't read the file further! "
<< std::endl;
exit(1);
}
// read the rest ...
}
Now here comes the problem, when I open my file, I get this error output:
Something is wrong with the magic word: MAGI?, couldn't read the file further! So there is always one (mostly random) character after the word MAGI, like in this example the character ?!
I do think that it has something to do with how a string in C++ is stored and compared with each other. Am I right and how can I avoid this?
PS: this implementation is included in another program and works totally fine ... weird.
strcmp assumes that both strings are nul-terminated (end with a nul-character). When you want to compare strings which are not terminated, like in this case, you need to use strncmp and tell it how many characters to compare (4 in this case).
if (strncmp(magic, "MAGI", 4) != 0){
When you try to use strcmp to compare not null-terminated char arrays, it can't tell how long the arrays are (you can't tell the length of an array in C/C++ just by looking at the array itself - you need to know the length it was allocated with. The standard library is not exempt from this limitation). So it reads any data which happens to be stored in memory after the char array until it hits a 0-byte.
By the way: Note the comment to your question by Lightness Races in Orbit, which is unrelated to the issue you are having now, but which hints a different bug which might cause you some problems later on.

Why does my program produce different results on Windows and Linux, about file reading with ifstream?

I have a program shown as follows. For it I have several questions:
1). Why does it produce different results on different platforms? I'll paste the screen-shots later.
2). I'm using a fail() method to check if the "file.read()" failed. Is this correct? I use fail() method because this web page says this:
The function returns true if either the failbit or the badbit is set. At least one of these flags is set when some error other than reaching the End-Of-File occurs during an input operation.
But later I read this page about istream::read() here. It says the eofbit and failbit would always be set at the same time.. Does this mean that a normal EOF situation would also result in that fail() returns true? This seems to conflict with "other than reaching the End-Of-File occurs"..
Could anyone help me clarify how I am supposed to use these methods? Should I use bad() instead?
My program
#include <iostream>
#include <fstream>
using namespace std;
#ifdef WIN32
char * path="C:\\Workspace\\test_file.txt";
#else
char * path="/home/robin/Desktop/temp/test_file.txt";
#endif
int main(int argc, char * argv[])
{
ifstream file;
file.open(path);
if (file.fail())
{
cout << "File open failed!" << endl;
return -1; // If the file open fails, quit!
}
// Calculate the total length of the file so I can allocate a buffer
file.seekg(0, std::ios::end);
size_t fileLen = file.tellg();
cout << "File length: " << fileLen << endl;
file.seekg(0, std::ios::beg);
// Now allocate the buffer
char * fileBuf = new (std::nothrow) char[fileLen+1];
if (NULL == fileBuf)
return -1;
::memset((void *)fileBuf, 0, fileLen+1); // Zero the buffer
// Read the file into the buffer
file.read(fileBuf, fileLen);
cout << "eof: " << file.eof() << endl
<< "fail: " << file.fail() << endl
<< "bad: " << file.bad() << endl;
if (file.fail())
{
cout << "File read failed!" << endl;
delete [] fileBuf;
return -1;
}
// Close the file
file.close();
// Release the buffer
delete [] fileBuf;
return 0;
}
The test_file.txt content(shown with "vim -b". It's a very simple file):
Result on Windows(Visual Studio 2008 SP1):
Result on Linux(gcc 4.1.2):
Does this mean that a normal EOF situation would also result in that fail() returns true? This seems to conflict with "other than reaching the End-Of-File occurs".
I recommend using a reference that isn't full of mistakes.
http://en.cppreference.com/w/cpp/io/basic_ios/fail says:
Returns true if an error has occurred on the associated stream. Specifically, returns true if badbit or failbit is set in rdstate().
And the C++ standard says:
Returns: true if failbit or badbit is set in rdstate().
There's no "other than end-of-file" thing. An operation that tries to read past the end of the file, will cause failbit to set as well. The eofbit only serves to distinguish that specific failure reason from others (and that is not as useful as one might think at first).
I'm using a fail() method to check if the "file.read()" failed. Is this correct?
You should simply test with conversion to bool.
if(file) { // file is not in an error state
It's synonymous with !fail(), but it's more usable, because you can use it to test directly the result of a read operation without extra parenthesis (things like !(stream >> x).fail() get awkward):
if(file.read(fileBuf, fileLen)) { // read succeeded
You will notice that all read operations on streams return the stream itself, which is what allows you to do this.
Why does it produce different results on different platforms?
The difference you're seeing between Windows and Linux is because the file is open in text mode: newline characters will be converted silently by the implementation. This means that the combination "\r\n" (used in Windows for newlines) will be converted to a single '\n' character in Windows, making the file have only 8 characters. Note how vim shows a ^M at the end of the first line: that's the '\r' part. In Linux a newline is just '\n'.
You should open the file in binary mode if you want to preserve the original as is:
file.open(path, std::ios_base::in | std::ios_base::binary);
I guess, the problem here with the different execution is the DOS(Window) vs. UNIX text file convention.
In DOS, a line ends with <CR><LF>, and this is read/written together as '\n'. Thus, in Windows your file is at the end, but in UNIX not, since there is one character left.