问题
Update: Please guid me through a clear answer and small example of usage
I am reading data from a binary file in which the first number indicates the number of inputs or values to read, the second tells the length of the first input then the input itself, after that the length of the second input then the input itself and so on.
So wrote the following code:
std::ifstream infile(filename, std::ios_base::binary);
unsigned int NumberOfInputs;
infile.read((char *) &NumberOfInputs, sizeof(unsigned int));
for (unsigned int i = 0; i < NumberOfInputs; i++) {
unsigned int Input_Lengh;
infile.read((char *) &Input_Lengh, sizeof(unsigned int));
string data;
while (Input_Lengh) {
char letter;
infile.read((char *) &letter, sizeof(char));
data += letter;
Input_Lengh--;
}
}
But what if the file was corrupted and the user told me that the number of inputs is 10 and I only read 2 (Because I got to EOF for example) How may I detect this?
回答1:
When using std::istream::read() you can test both the eofbit and failbit flags. Both of them are set when end of file is reached. In your case, after every read operation, I would recommend testing if these flags are set. If so, just interrupt your file read sequence.
For example:
if ( (infile.rdstate() & std::ifstream::failbit ) != 0 )
{
/* error handling here*/
}
Alternatively, you can use gcount() std::istream method to obtain the number of characters successfully read.
For example:
if (infile.gcount() < N_EXPECTED_CHARS)
{
/* error handling here*/
}
回答2:
From the doc. of std::istream::read():
Characters are extracted and stored until any of the following conditions occurs:
- count characters were extracted and stored
- end of file condition occurs on the input sequence (in which case,
setstate(failbit|eofbit)
is called). The number of successfully extracted characters can be queried using gcount().
I made a small MCVE to demonstrate how to check the state of input stream and the usage of std::istream::gcount()
:
#include <iostream>
#include <fstream>
#include <vector>
int main(int argc, char **argv)
{
std::ifstream infile(argv[1], std::ios_base::binary);
unsigned n;
if (!infile.read((char*)&n, sizeof n)) {
std::cerr << "ERROR! Failed to read number of inputs.\n";
return 1;
}
for (unsigned i = 0; i < n; ++i) {
unsigned len;
if (!infile.read((char*)&len, sizeof len)) {
std::cerr << "ERROR! Failed to read length of input " << (i + 1) << ".\n";
return 1;
}
std::vector<char> data(len);
if (!infile.read(&data[0], len)) {
std::cerr << "ERROR! Failed to read data of input " << (i + 1) << ".\n"
<< "Expected " << len << " bytes but got " << infile.gcount() << ".\n";
return 1;
}
}
std::cout << "File '" << argv[1] << "' successfully read.\n";
}
Test:
$ g++ -std=c++17 -O2 -Wall -pedantic -pthread main.cpp
$ echo "Good:" ; \
echo -ne '\x01\x00\x00\x00\x04\x00\x00\x00''abcd' > test.dat ; \
./a.out test.dat
Good:
File 'test.dat' successfully read.
$ echo "Bad n:" ; \
echo -ne '\x02\x00\x00\x00\x04\x00\x00\x00''abcd' > test.dat ; \
./a.out test.dat
Bad n:
ERROR! Failed to read length of input 2.
$ echo "Bad len:"; \
echo -ne '\x01\x00\x00\x00\x04\x00\x00\x00''abc' > test.dat ; \
./a.out test.dat
Bad len:
ERROR! Failed to read data of input 1.
Expected 4 bytes but got 3.
Live Demo on coliru
Notes:
- The size of
unsigned
depends on the compiler / target architecture. It may be less than 4 bytes. - The endianess of
unsigned
is platform dependent.
In this sample, little-endian is assumed but there are still platforms with big-endianess.
(At least, in the past there were platforms with even more exotic endianess.)
To fix both issues, the expected number of bytes can be read and composed to the integral using bit arithmetic operators.
Related Q/As:
- SO: C++ istream: Is gcount() always set after a read() even if it fails?
- SO: Ifstream reading error
来源:https://stackoverflow.com/questions/63355962/c-detect-if-input-doesnt-satisfy-conditions