I have written the function, don't know it that's correct but how do I return true and false using if condition?
Function bool save_book(Book, char)**
Input Parameters: A pointer to a Book and a string representing a file name.
Return Value: Return true if the book's contents were stored to a file successfully. Otherwise false.
Note This function should create or open a file based on the file name and store each Line of the book in it.
Below is my code
bool save_book(Book *b, char* fileName){
FILE *filePointer = fopen(fileName,"w");
for (int i = 0; i < pBook->lineCount; i++)
{
fprintf(filePointer, "%s", b->lines[i]);
}
fclose(filePointer);
return true;
}
this is the struct I am using:
typedef struct _Book
{
int characterCount;
int lineCount;
int maxLineCount;
char **lines;
}Book;
You have to check every individual I/O operation.
To make it fun, they all return different things on error. fopen will return NULL. fprintf will return a negative number. fclose will return EOF.
Here it is annotated with a short list of what might go wrong at each step.
bool save_book(Book *b, char* fileName) {
// Maybe the directory doesn't exist.
// Maybe you don't have permission.
// Maybe there's a disallowed character.
// Maybe the disk is full.
// Maybe it's a network drive and there's a network error.
// Maybe the drive got unmounted.
FILE *filePointer = fopen(fileName,"w");
if( filePointer == NULL ) {
return false;
}
for (int i = 0; i < b->lineCount; i++)
{
// Maybe the disk is full.
// Maybe it's a network drive and there's a network error.
// Maybe the drive got unmounted.
if( fprintf(filePointer, "%s", b->lines[i]) < 0 ) {
// Even though the filePointer variable will be automatically freed
// the underlying file handle will not be automatically closed.
// There's a limit to how many open file handles one can have open.
// No need to check for error, we know something has already gone wrong.
fclose(filePointer);
return false;
}
}
// Maybe the disk is full.
// Maybe it's a network drive and there's a network error.
// Maybe the drive got unmounted.
if( fclose(filePointer) != 0 ) {
return false;
}
return true;
}
In reality you probably don't need to check fprintf, checking fclose should catch the same errors. But if you're writing a very large and expensive file you might want to know if you ran out of disk space sooner rather than later.
You can also optionally print the error. Each of those functions will set the global errno on failure. You can turn this into a human readable string with strerror.
if( filePointer == NULL ) {
fprintf(stderr, "Error while opening '%s' for writing: %s", fileName, strerror(errno));
return false;
}
Note that rather than checking for an exact error code, I tend to check for that which is not the success code. Rather than if( fclose(filePointer) == EOF ) I've checked for if( fclose(filePointer) != 0 ), the lack of a success code. This is a defense programming practice just in case the error is severe enough that it can't even return its correct error code (extremely unlikely in standard library code) or I didn't read the spec quite right.
Related
I'm trying to understand when the stdio function clearerr() should be used.
For example, if I fread() or fwrite() on a valid FILE* and get a short count and ferror is true, what can I do?
From what I've read so far, fread() and fwrite() are robust and will block and/or retry (if there are locks and/or interrupts which could happen in lower level functions) so there never seems any point in using clearerr() because fread or fwrite errors will be so catastrophic there is no point in trying to recover.
Additionally, ferror() only tells me that there is an error, not what the error is.
#define SZ 1024
FILE* fp = fopen( "foo", "r" );
if ( fp ) {
char b[SZ];
int ch_count = fread( b, sizeof(char), SZ, fp );
if ( ch_count != SZ && ferror( fp ) ) {
// how would clearerr() be used. I don't know?
// ....
// should I drop through here to fclose? (when I've got an ferror)
}
fclose( fp );
}
There is at least one real world use case for clearerr: when you want to mimic tail -f on a file that is not opened in exclusive mode. That means that another (or many other) process(es) write at the end of a file, and one process repeatedly reads even after having reached the end of file in order to look whether new data has arrived. In that case, could would look like:
for (;;) {
if (NULL == fgets(line, sizeof(line), fd)) {
sleep(n);
clearerr(fd); // reset EOF condition
}
else {
fputs(line, fdout);
}
}
Functions that set the error status of a FILE (as reported by ferror) do not clear it even if later called successfully. Likewise if you encounter the end of file while reading, it will not be cleared automatically even if the file later has more data available.
Basically this means that if you are using ferror to check for an error state and you have some way of recovering from it, the ferror will keep indicating an error until you use clearerr.
In your example, if you just use the return value of fread as the condition for terminating the read (i.e., EOF and any type of error are considered final), there is no need to clearerr: just fall through to fclose (and perhaps use ferror to determine whether to print an error message).
On the other hand, if the FILE is in fact a stream on which read can later succeed, and you detect (or assume) that specific condition and retry, you should clearerr before retrying or you will keep seeing the old error condition on future attempts.
Likewise, as pointed out in comments, clearerr also clears the end of file state, so this also applies when using feof to check for the end of file. (Note, however, that you generally shouldn't use !feof(file) as the loop condition when reading.)
clearerr() clears the error and EOF flags from a stream.
Say FILE were like this:
typedef struct {
int fd;
char *buf;
int error;
int eof;
} FILE;
FILE *file;
This would set file->error and file->eof to 0.
Some reasons for doing this include file I/O, such as when a file gives EOF, but then another program (or another thread, etc.) appends to it. If you clear the error after doing this, you can have your program act as sort of a tail -f-substitute.
clearerr() clears both the error and end-of-file flags.
A pedantic use of clearerr():
// Return -1 on end-of-file
// Return -2 on rare file error
// Else return the unsigned char value
int my_get_quandry() {
// At this point, the file end-of-file flag may be set.
// At this point, the file file error flag may be set.
// They may both be set.
// Attempt to read another
int ch = fgetc();
if (ch != EOF) {
return ch;
}
// Now was the EOF due to a end-of file or error?
// feof() is true if end-of-file just occurred OR if end-of-file was set earlier
// ferror() is true if error just occurred OR if error was set earlier
// If only one feof() or ferror() is true, we know why EOF just occurred,
// Yet if both set, we do not know.
...?
}
Use clearerr()
// Return -1 on end-of-file
// Return -2 on rare file error
// Else return the unsigned char value
int my_get_crystal() {
clearerr(stdin);
// Attempt to read another
int ch = fgetc();
if (ch != EOF) {
return ch;
}
// Now EOF due to at most one reason
if (feof(stdin)) return -1;
if (ferror(stdin)) return -2;
// if code reaches this point, it is due to the odd-ball platform of `char` having the
// same range as `int`. But let us leave that platform for another day.
return ch;
}
I have an issue with C programming and would like a huge help from you all.
I'm supposed to write a console application that is going to manage human resources. The code below is supposed to check if the userlist.txt file exists and if so it reads from it. The code compiles and links successfully but I get a segmentation fault when I execute it.
bool userListAvailable() {
bool userListExist = false;
struct user_details userlist[number_of_employee];
FILE *userListFile=fopen("userlist.txt", "w");
if (!(userListFile == NULL)) {
fread(userlist, sizeof (userlist), 1, userListFile);
for (int i = 0; i < sizeof (userlist); ++i) {
if (strstr(userlist[i].email, "#") != NULL) {
userListExist = true;
break;
}
}
}
fclose(userListFile);
return userListExist;
}
Any idea on what I'm doing wrong? I tried several other almost similar questions , but it was in vain.
In addition to the discussion at the top
for (int i = 0; i < sizeof(userlist); ++i) {
Is most likely wrong, as you want to read number_of_employee rather than the number of bytes in your buffer.
Most likely you'd get a segfault at userlist[i] as sizeof(userlist) is probably a larger number than number_of_employee
If you want to check if the userlist.txt file exists, You can check it using Open.
Open will return the new file descriptor, or -1 if an error occurred (in which case, errno is set appropriately). Here is the man http://man7.org/linux/man-pages/man2/open.2.html
Then if the FD (file descriptor) than open returned is different that -1, it means that the file exist and that you succesfully opened it, so you can read it using the FD.
The following is my code for a method that copies a file from a path to a file to a directory provided as the destination. The copy works perfectly fine, however my chmod call assigns the wrong permissions to the copied file in the destination. If the permission in the source is 644, the copied file has a permission of 170 or 120.
I have been attempting to debug this for hours and it's driving me slightly crazy so any help is greatly appreciated.
void copy_file(char* src, char* dest) {
char a;
//extract file name through a duplicate ptr
char* fname = strdup(src);
char* dname = basename(fname);
//open read and write streams
FILE* read;
FILE* write;
read = fopen(src, "r");
chdir(dest);
write = fopen(dname, "w");
//error checking
if (read == NULL) //|| (write == NULL))
{
perror("Read Error: ");
exit(0);
}
else if (write == NULL)
{
perror("Write Error: ");
exit(0);
}
//write from src to dest char by char
while (1){
a = fgetc(read);
if (a == EOF)
{
break;
}
fputc(a, write);
}
//close files
fclose(read);
fclose(write);
// this is where I attempt to assign source file permissions
//and it goes horribly wrong
struct stat src_st;
if(stat(src, &src_st)){
perror("stat: ");
}
chmod(dname, src_st.st_mode);
printf("%o\n", src_st.st_mode & 0777);
}
You fopen(src, "r"), then you chdir(dest). This means that when you later call stat(src, &src_st), there is no reason to think that stat will access the same file as fopen did, or indeed that stat will access any file at all.
If stat fails, you proceed to call chmod anyway, so you pass whatever random junk was in src_st.st_mode to chmod.
You should use fstat(fileno(read), &src_st) before calling fclose(src), instead of calling stat(src, &src_st).
The basic problem is you have to check your system calls like fopen, chdir, and stat immediately.
For example, first thing I tried was copy_file( "test.data", "test2.data" ) not realizing it expected a destination directory.
char* fname = strdup(src);
char* dname = basename(fname);
dname is now test.data, same as the source.
read = fopen(src, "r"); // succeeds
chdir(dest); // fails
write = fopen(dname, "w"); // blows away test.data, the source
You do eventually check read and write, but after the damage has been done.
Blowing away your source file is really bad. It's important that your code deals with failed system calls. If you don't, it will sail along causing confusion and destruction.
Most system calls in C return 0 for success. This is an anti-pattern where the return value is an error flag, so false is failure, and anything else indicates what kind of error (though stat doesn't use that, it uses errno).
When it fails, stat returns -1 which is true. So this is the wrong way around.
struct stat src_st;
if(stat(src, &src_st)){
perror("stat: ");
}
Instead, you have to check for non-zero.
struct stat src_st;
if(stat(src, &src_st) != 0 ){
// Note that I don't use perror, it doesn't provide enough information.
fprintf(stderr, "Could not stat %s: %s\n", src, strerror(errno));
exit(1);
}
As you can guess this gets tedious in the extreme, and you're going to forget, or do it slightly different each time. You'll want to write wrappers around those functions to do the error handling for you.
FILE *fopen_checked( const char *file, const char *mode ) {
FILE *fp = fopen(file, mode);
if( file == NULL ) {
fprintf(stderr, "Could not open '%s' for '%s': %s", file, mode, strerror(errno));
exit(1);
}
return fp;
}
It's not the best error handling, but it will at least ensure your code appropriately halts and catches fire.
A note about chdir: if you can avoid it don't use it. chdir affects the global state of the program, the current working directory, and globals add complexity to everything. It's very, very easy for a function to change directory and not change back, as yours does. Now your process is in a weird state.
For example, if one did copy_file( "somefile", "foo" ) this leaves the program in foo/. If they then did copy_file( "otherfile", "foo" ) they'd be trying to copy foo/otherfile to foo/foo/otherfile.
And, as #robmayoff pointed out, your stat fails because the process is now in a different directory. So even the function doing the chdir is confused by it.
Ensuring that your functions always chdir back to the original directory in a language like C is very difficult and greatly complicates error handling. Instead, stay in your original directory and use functions like basename to join paths together.
Finally, avoid mixing your file operations. Use filenames or use file descriptors, but try not to use both. That means if you're using fopen, use fstat and fchmod. You might have to use fileno to get a file descriptor out of the FILE pointer.
This avoids having to carry around and keep in sync two pieces of data, the file descriptor and the filename. It also avoids issues with chdir or the file being renamed or even deleted, the file descriptor will still work so long as it remains open.
This is also a problem:
char a;
...
while (1){
a = fgetc(read);
if (a == EOF)
{
break;
}
fputc(a, write);
}
fgetc() returns int, not char. Per the C Standard, 7.21.7.1 The fgetc function:
7.21.7.1 The fgetc function
Synopsis
#include <stdio.h>
int fgetc(FILE *stream);
Assuming sizeof( int ) > sizeof( char ), char values are signed, 2s-complement integers, and EOF is an int defined to be -1 (all very common values), reading a file with char a = fgetc( stream ); will fail upon reading a valid 0xFF character value. And if your implementation's default char value is unsigned char, char a = fgetc( stream ); will never produce a value that matches EOF.
I'm a bit new to C, but basically I have a problem where I need to read '-1' from a file. Sadly this means I run into a premature ending of the file, because the EOF constant is also -1 in my compiler.
What sort of work arounds would there be for this? Is there another function I can use to read it that will change the EOF to something I can work with?
Thanks in advance.
The code since people are asking for it
int read() {
int returnVal; // The value which we return
// Open the file if it isn't already opened
if (file == NULL) {
file = fopen(filename, "r");
}
// Read the number from the file
fscanf(file, "%i", &returnVal);
// Return this number
return returnVal;
}
This number is then later compared to EOF.
Okay this is probably bad practice, but I changed the code to the following
int readValue() {
int returnVal; // The value which we return
// Open the file if it isn't already opened
if (file == NULL) {
file = fopen(filename, "r");
}
// Read the number from the file
fscanf(file, "%i", &returnVal);
if (feof(file)) {
fclose(file);
return -1000;
}
// Return this number
return returnVal;
}
Because I knew I would never read any such number from my file (they range from about [-300, 300]. Thanks for all your help guys!
The return value of fscanf is NOT the value that was read, but rather it is the number of items successfully read, or EOF if an error occurred.
The problem is that your read function doesn't distinguish between a successful read and an error condition. You should change it to accept a int * as a parameter that scanf writes into, and the function should return something like 0 on a successful read and -1 on error. You can use the return value of scanf as the basis of what your function returns.
Also, there's a system call named read, so you should really name it something else. And don't forget to fclose(file) at the end of the function, otherwise you're leaking file descriptors.
I want to read a file in a specific format, so I use fscanf_s and a while loop. But as soon as fscanf_s is processed, the program crashes with an access violation (0xC0000005).
Here's the code:
FILE *fp;
errno_t err = fopen_s(&fp, "C:\\data.txt", "r");
if (err != 0)
return 0;
int minSpeed = 0;
int maxSpeed = 0;
char axis = '#';
while(!feof(fp))
{
int result = fscanf_s(fp, "%c;%d-%d\n", &axis, &minSpeed, &maxSpeed);
if (result != 3)
continue;
}
fclose(fp);
The content of the file is line based, for example:
-;10000-20000
X;500-1000
S;2000-2400
Can somebody help me?
Apparently, fscanf_s() needs a size parameter after the address of the variable
fscanf_s(fp, "%c;%d-%d\n", &axis, 1, &minSpeed, &maxSpeed);
/* extra 1 for the size of the ^^^ axis array */
But I suggest you do not use the *_s functions: they are worse than the plainly named functions --- they require the same checks and make you feel safe when you aren't. I suggest you don't use them because of false sense of security and the fact they are not available on many implementations making your programs work only in a limited subset of possible machines.
Use plain fscanf()
fscanf(fp, "%c;%d-%d\n", &axis, &minSpeed, &maxSpeed);
/* fscanf(fp, "%1c;%d-%d\n", &axis, &minSpeed, &maxSpeed); */
/* default 1 ^^^ same as for fscanf_s */
And your use of feof() is wrong.
The fscanf() returns EOF when there is an error (end-of-file or matching failure or read error ...).
You can use feof() to determine why fscanf() failed, not to check whether it would fail on the next time it is called.
/* pseudo-code */
while (1) {
chk = fscanf();
if (chk == EOF) break;
if (chk < NUMBER_OF_EXPECTED_CONVERSIONS) {
/* ... conversion failures */
} else {
/* ... all ok */
}
}
if (feof()) /* failed because end-of-file reached */;
if (ferror()) /* failed because of stream error */;
If you believe the file (data.txt) exists, your application is probably not running with a current directory set to where the file is. This would cause fopen_s() to fail.