Problem with fseek - c++

Here is my code.
if(fseek(file,position,SEEK_SET)!=0)
{
throw std::runtime_error("can't seek to specified position");
}
I used to assume that even if position is greater than num of characters in file this code would work correct (i.e. throw error), but it wouldn't. So I want to know how can I handle seeking failure when trying to seek out of file range?

Well, you can always check for file length before doing the fseek.
void safe_seek(FILE* f, off_t offset) {
fseek(f, 0, SEEK_END);
off_t file_length = ftell(f);
if (file_length < offset) {
// throw!
}
fseek(f, offset, SEEK_SET);
}
Be aware though that this is not thread safe.

if( fseek(file,position,SEEK_SET)!=0 || ftell(file) != position )
{
throw std::runtime_error("can't seek to specified position");
}

According to man: http://linuxmanpages.com/man3/fseek.3.php, fseek returns non-zero value in case of an error, and the only errors that may appear are:
EBADF The stream specified is not a seekable stream.
EINVAL The whence argument to fseek() was not SEEK_SET, SEEK_END, or SEEK_CUR.
Falling beyond the end-of-file is probably considered not an error for lseek. However, calling feof immediately after may indicate the out-of-file condition.

It's not an error to seek past the end of the file. If you write to that offset, the file will be extended with null bytes.

Related

eof bit not set even if offset is beyond file size

I have a fstream pointer fileP_.
I open a file with:
fileP_.open(filePath_.c_str(), std::ios::in|std::ios::binary);
I have a Read() function with it's definition as:
int Read(size_t offset, char *buffer, size_t *size)
So here I read #size in #buffer starting from offset #offset of a file.
My code of Read() is somewhat like this:
int rc = 0
fileP_.seekg(offset);
fileP_.read(buffer, *size);
if (!fileP_.gcount()) {
if (fileP_.eof())
*size = rc;
else if (fileP_.fail())
rc = -EIO;
....
The code works fine until offset < filesize, but if I give offset > filesize gcount() gives 0(which is expected) but I get -EIO, and I expect that if offset > filesize size = rc = 0.
Am I missing anything in the above code?
Thanks!
If you seekg > filesize the operation fails, the failbit is set and read does not work... (eof has not been set)
If one operation fails, failbit is activated and all the following operation will be no-op until state bits are cleared. In this case, if seekg fails, istream::read will not read anything and, in particular, will not set eofbit.
On the other hand, eofbit is not activated when the position in the file is "at the end". Actually, it is activated when the stream detects the end of file, that is, when it try to gets the next char and an EOF is returned.
In general, in C++ it is not a good idea to control the end of input with eofbit. It is better to test if the operation has been successfull. When the operation fails, then test if the problem is the end of file by using eofbit.

Size error on read file

RESOLVED
I'm trying to make a simple file loader.
I aim to get the text from a shader file (plain text file) into a char* that I will compile later.
I've tried this function:
char* load_shader(char* pURL)
{
FILE *shaderFile;
char* pShader;
// File opening
fopen_s( &shaderFile, pURL, "r" );
if ( shaderFile == NULL )
return "FILE_ER";
// File size
fseek (shaderFile , 0 , SEEK_END);
int lSize = ftell (shaderFile);
rewind (shaderFile);
// Allocating size to store the content
pShader = (char*) malloc (sizeof(char) * lSize);
if (pShader == NULL)
{
fputs ("Memory error", stderr);
return "MEM_ER";
}
// copy the file into the buffer:
int result = fread (pShader, sizeof(char), lSize, shaderFile);
if (result != lSize)
{
// size of file 106/113
cout << "size of file " << result << "/" << lSize << endl;
fputs ("Reading error", stderr);
return "READ_ER";
}
// Terminate
fclose (shaderFile);
return 0;
}
But as you can see in the code I have a strange size difference at the end of the process which makes my function crash.
I must say I'm quite a beginner in C so I might have missed some subtilities regarding the memory allocation, types, pointers...
How can I solve this size issue?
*EDIT 1:
First, I shouldn't return 0 at the end but pShader; that seemed to be what crashed the program.
Then, I change the type of reult to size_t, and added a end character to pShader, adding pShdaer[result] = '/0'; after its declaration so I can display it correctly.
Finally, as #JamesKanze suggested, I turned fopen_s into fopen as the previous was not usefull in my case.
First, for this sort of raw access, you're probably better off
using the system level functions: CreateFile or open,
ReadFile or read and CloseHandle or close, with
GetFileSize or stat to get the size. Using FILE* or
std::filebuf will only introduce an additional level of
buffering and processing, for no gain in your case.
As to what you are seeing: there is no guarantee that an ftell
will return anything exploitable as a numeric value; it could
very well be just a magic cookie. On most current systems, it
is a byte offset into the physical file, but on any non-Unix
system, the offset into the physical file will not map directly
to the logical file you are reading unless you open the file in
binary mode. If you use "rb" to open the file, you'll
probably see the same values. (Theoretically, you could get
extra 0's at the end of the file, but practically, the OS's
where that happened are either extinct, or only used on legacy
mainframes.)
EDIT:
Since the answer stating this has been deleted: you should loop
on the fread until it returns 0 (setting errno to 0 before
each call, and checking it after the return to see whether the
function returned because of an error or because it reached the
end of file). Having said this: if you're on one of the usual
Windows or Unix systems, and the file is local to the machine,
and not too big, fread will read it all in one go. The
difference in size you are seeing (given the numerical values
you posted) is almost certainly due to the fact that the two
byte Windows line endings are being mapped to a single '\n'
character. To avoid this, you must open in binary mode;
alternatively, if you really are dealing with text (and want
this mapping), you can just ignore the extra bytes in your
buffer, setting the '\0' terminator after the last byte
actually read.

"fastfwd" file that can be pipe/socket/fifo

My function gets a FILE* to read from, and it needs to read starting from some non-negative offset.
I could use fseek(file, offset, SEEK_SET), but it fails on stdin, for instance.
How can I determine if fseek works? If it doesn't, I could read and discard offset bytes.
And is there a way to read (and discard) from FILE without allocating read buffer?
You can test if fseek works on that stream, by calling fseek( file, offset, SEEK_SET) and on error, checking that errno == EBADF which is returned to say "The stream specified is not a seekable stream".
I think you need to read and discard, with a buffer, but if it can just be pagesize bytes and you keep a count of bytes read, reading till you did the equivalent of a seek. If it were a memory mappable file, then you can read without reading, but then the seek would have worked.
The return value of fseek() tells you if it worked or not:
Return Value
...Upon successful completion, fgetpos(), fseek(), fsetpos() return 0, ...Otherwise, -1 is returned and errno is set to indicate the error.
So attempt to fseek() from the file and check the return result, and handle your failure case accordingly. Ex:
ret = fseek(stdin, 0, SEEK_SET);
if(ret < 0)
printf("failed because: %s\n", strerror(errno));
will give you something like:
failed because: Illegal seek
So that failed because you can't seek stdin, where as:
FILE * fp = fopen("word.txt", "r");
ret = fseek(fp, 0, SEEK_SET);
if(ret < 0)
printf("failed because: %s\n", strerror(errno));
Wouldn't print anything because you got back 0 indicating success (assuming of course that "word.txt" exists, is readable, was opened successfully, etc).
I don't understand this part of your question:
is there a way to read (and discard) from FILE without allocating read buffer
You can just fseek() to the point you want to read, or you can read into an array to a buffer and then overwrite the results. The answer depends on your goals, but using things like fread() or read() will require a non-null pointer to store data into.

Reading writing from offset

bool CReadWrite::write(unsigned long long offset, void* pvsrc, unsigned long long nbytes)
{ int WriteResult;
pFile = fopen("D:\\myfile.bin","wb");
if (!pFile){
puts("Can't open file");
return false;
}
//offset = fseek(pFile,offset,
WriteResult = fwrite (pvsrc, 1, nbytes, pFile);
if (WriteResult == nbytes){
puts("Wrote to file");
fclose(pFile);
return true;
}
else{
puts("Unable to write to File.");
fclose(pFile);
return false;
}
}
This is my class function so far. I'm basically opening a file, checking to see if it did indeed open if not get out. Writes the file, checks to see if the file see if the file was indeed written to returns true. else return false. As you can tell by my parameters, I'm looking to create an offset where I can give a particular offset i.e. 10, and start from 10 and then from there write. I know for sure I need to use fseek but I can't assume that I'm at the beginning of the file or anywhere in the file. Im pretty sure i need to use SEEK_SET but I may be wrong. Any thoughts on implemented the above desires? Thanks.
If you're using fopen without the append setting (as you are, "wb" creates an empty file), you can assume you're at the beginning.
Regardless, SEEK_SET sets the position to the given offset from the beginning.
If the file doesn't have the offset that you want to seek to (as it is in your case), then the question is what are you required to do? If just pad - then write offset padding bytes, and then your content, otherwise maybe you wanted to use "a" and not "w". "w" truncates the existing content of the file, while "a" opens for append and sets position to the end of the existing content.
More details here.

Why is fread reaching the EOF early?

I am writing a C library that reads a file into memory. It skips the first 54 bytes of the file (header) and then reads the remainder as data. I use fseek to determine the length of the file, and then use fread to read in the file.
The loop runs once and then ends because the EOF is reached (no errors). At the end, bytesRead = 10624, ftell(stream) = 28726, and the buffer contains 28726 values. I expect fread to read 30,000 bytes and the file position to be 30054 when EOF is reached.
C is not my native language so I suspect I've got a dumb beginner mistake somewhere.
Code is as follows:
const size_t headerLen = 54;
FILE * stream;
errno_t ferrno = fopen_s( &stream, filename.c_str(), "r" );
if(ferrno!=0) {
return -1;
}
fseek( stream, 0L, SEEK_END );
size_t bytesTotal = (size_t)(ftell( stream )) - headerLen; //number of data bytes to read
size_t bytesRead = 0;
BYTE* localBuffer = new BYTE[bytesTotal];
fseek(stream,headerLen,SEEK_SET);
while(!feof(stream) && !ferror(stream)) {
size_t result = fread(localBuffer+bytesRead,sizeof(BYTE),bytesTotal-bytesRead,stream);
bytesRead+=result;
}
Depending on the reference you use, it's quite apparent that adding a "b" to the mode flag is the answer. Seeking nominations for the bonehead-badge. :-)
This reference talks about it in the second paragraph, second sentence (though not in their table).
MSDN doesn't discuss the binary flag until halfway down the page.
OpenGroup mentions the existance of the "b" tag, but states that it "shall have no effect".
perhaps it's a binary mode issue. Try opening the file with "r+b" as the mode.
EDIT: as noted in a comment "rb" is likely a better match to your original intent since "r+b" will open it for read/write and "rb" is read-only.
Also worth noting that simply including binmode.obj into your link command will do this for you for all file opens.
A solution, based on the previous answers:
size_t bytesRead = 0;
BYTE* localBuffer = new BYTE[bytesTotal];
fseek(stream,headerLen,SEEK_SET);
while(!feof(stream) && !ferror(stream)) {
size_t result = fread(localBuffer+bytesRead,sizeof(BYTE),bytesTotal-
bytesRead,stream);
bytesRead+=result;
}