help with small example of file operations

L

Luca Forlizzi

Hello,

I would like to write a small example of I/O using standard library's
streams.
I would like the example to show how to test for possible errors
occurred during
file operations. Since I am not very experienced on the issue, I
would like to ask
if someone may give a look to my code and tell me if something is
wrong.
Of course the example works as I intended, in the implementation
I'am using now, when no error occurs during file operations, but I
don't have the possibility
to check it on many other implementations, and I don't know how to
make the file operations
fail in order to check the successful detection of the failure.
I hope no one is annoyed by the small comments in italian language.

#include <stdio.h>
#include <stdlib.h>

#define NOME_FILE_BIN_NUMERICO "streamBinario_formatoNumerico.out"
#define NOME_FILE_TEXT_NUMERICO "streamTesto_formatoNumerico.out"
#define NOME_FILE_BIN_TESTUALE "streamBinario_formatoTestuale.out"
#define NOME_FILE_TEXT_TESTUALE "streamTesto_formatoTestuale.out"

/* Funzione che stampa un messaggio di errore ed esce indicando
un fallimento all'ambiente operativo */
void fallisci(void) {
printf("Fallimento: Errore in un'operazione su file!\n");
exit(EXIT_FAILURE);
}

int main(void) {

int i;
FILE *stream_bin_numerico, *stream_text_numerico,
*stream_bin_testuale, *stream_text_testuale;

struct {
float f;
unsigned char c1, c2;
} dati[] = {
{ 12.34f, 5, '5' },
{ 130.0f, 10, 'a' },
{ 108.0f, 108, 130 },
};

/* Apre i file */

/* apre il primo in modo poco conciso */
stream_bin_numerico = fopen( NOME_FILE_BIN_NUMERICO, "wb");
if (stream_bin_numerico == NULL) fallisci();

/* apertura un po' piu' concisa */
if ( (stream_text_numerico = fopen( NOME_FILE_TEXT_NUMERICO, "w"))
== NULL) fallisci();

/* ...ancora piu' conciso! */
if ( !(stream_bin_testuale = fopen( NOME_FILE_BIN_TESTUALE, "wb")) )
fallisci();
if ( !(stream_text_testuale = fopen( NOME_FILE_TEXT_TESTUALE,
"w")) ) fallisci();

/* scrive nei file */
for (i=0; i< (sizeof dati)/(sizeof dati[0]); i++) {

/* output numerico */
if ( fwrite( &dati.f, sizeof dati.f, 1,
stream_bin_numerico ) < 1) fallisci();
if ( fputc( dati.c1, stream_bin_numerico ) == EOF ) fallisci();
if ( fputc( dati.c2, stream_bin_numerico ) < 0 ) fallisci();

if ( fwrite( &dati.f, sizeof dati.f, 1,
stream_text_numerico ) < 1) fallisci();
if ( fputc( dati.c1, stream_text_numerico ) == EOF )
fallisci();
if ( fputc( dati.c2, stream_text_numerico ) < 0 ) fallisci();


/* output testuale */
if ( fprintf( stream_bin_testuale, "%f %c %c\n", dati.f,
dati.c1, dati.c2 ) < 0) fallisci();
if ( fprintf( stream_text_testuale, "%f %c %c\n", dati.f,
dati.c1, dati.c2 ) < 0) fallisci();

}

/* Chiude i file, controlla anche il successo dell'operazione */
if ( fclose( stream_bin_numerico ) == EOF ) fallisci();
if ( fclose( stream_text_numerico ) != 0 ) fallisci();
if ( fclose( stream_bin_testuale ) ) fallisci();
if ( fclose( stream_text_testuale ) ) fallisci();


return 0;
}
 
A

Angel

Hello,

I would like to write a small example of I/O using standard library's
streams.
I would like the example to show how to test for possible errors
occurred during
file operations. Since I am not very experienced on the issue, I
would like to ask
if someone may give a look to my code and tell me if something is
wrong.
Just one thing I can find, here you declare i as an integer:

But here you compare it to a value that is of type size_t. int is signed
and size_t is unsigned. Moreover, on 64-bit systems int may be 32 bit
and size_t 64 bit. Because of that, if you have a large number of small
objects it is possible that this loop will never end because the value
of ((sizeof dati)/(sizeof dati[0])) may be larger than INT_MAX. It's
better to make i of type size_t.
for (i=0; i< (sizeof dati)/(sizeof dati[0]); i++) {
 
B

Billy Mays

Hello,

I would like to write a small example of I/O using standard library's
streams.
I would like the example to show how to test for possible errors
occurred during
file operations. Since I am not very experienced on the issue, I
would like to ask
if someone may give a look to my code and tell me if something is
wrong.
Of course the example works as I intended, in the implementation
I'am using now, when no error occurs during file operations, but I
don't have the possibility
to check it on many other implementations, and I don't know how to
make the file operations
fail in order to check the successful detection of the failure.
I hope no one is annoyed by the small comments in italian language.

#include<stdio.h>
#include<stdlib.h>

#define NOME_FILE_BIN_NUMERICO "streamBinario_formatoNumerico.out"
#define NOME_FILE_TEXT_NUMERICO "streamTesto_formatoNumerico.out"
#define NOME_FILE_BIN_TESTUALE "streamBinario_formatoTestuale.out"
#define NOME_FILE_TEXT_TESTUALE "streamTesto_formatoTestuale.out"

/* Funzione che stampa un messaggio di errore ed esce indicando
un fallimento all'ambiente operativo */
void fallisci(void) {
printf("Fallimento: Errore in un'operazione su file!\n");
exit(EXIT_FAILURE);
}

int main(void) {

int i;
FILE *stream_bin_numerico, *stream_text_numerico,
*stream_bin_testuale, *stream_text_testuale;

struct {
float f;
unsigned char c1, c2;
} dati[] = {
{ 12.34f, 5, '5' },
{ 130.0f, 10, 'a' },
{ 108.0f, 108, 130 },
};

/* Apre i file */

/* apre il primo in modo poco conciso */
stream_bin_numerico = fopen( NOME_FILE_BIN_NUMERICO, "wb");
if (stream_bin_numerico == NULL) fallisci();

/* apertura un po' piu' concisa */
if ( (stream_text_numerico = fopen( NOME_FILE_TEXT_NUMERICO, "w"))
== NULL) fallisci();

/* ...ancora piu' conciso! */
if ( !(stream_bin_testuale = fopen( NOME_FILE_BIN_TESTUALE, "wb")) )
fallisci();
if ( !(stream_text_testuale = fopen( NOME_FILE_TEXT_TESTUALE,
"w")) ) fallisci();

/* scrive nei file */
for (i=0; i< (sizeof dati)/(sizeof dati[0]); i++) {

/* output numerico */
if ( fwrite(&dati.f, sizeof dati.f, 1,
stream_bin_numerico )< 1) fallisci();
if ( fputc( dati.c1, stream_bin_numerico ) == EOF ) fallisci();
if ( fputc( dati.c2, stream_bin_numerico )< 0 ) fallisci();

if ( fwrite(&dati.f, sizeof dati.f, 1,
stream_text_numerico )< 1) fallisci();
if ( fputc( dati.c1, stream_text_numerico ) == EOF )
fallisci();
if ( fputc( dati.c2, stream_text_numerico )< 0 ) fallisci();


/* output testuale */
if ( fprintf( stream_bin_testuale, "%f %c %c\n", dati.f,
dati.c1, dati.c2 )< 0) fallisci();
if ( fprintf( stream_text_testuale, "%f %c %c\n", dati.f,
dati.c1, dati.c2 )< 0) fallisci();

}

/* Chiude i file, controlla anche il successo dell'operazione */
if ( fclose( stream_bin_numerico ) == EOF ) fallisci();
if ( fclose( stream_text_numerico ) != 0 ) fallisci();
if ( fclose( stream_bin_testuale ) ) fallisci();
if ( fclose( stream_text_testuale ) ) fallisci();


return 0;
}



I tried compiling it with gcc -Wall -Werror -ansi -pedantic with no
complaints, and lint only had minor complaints about unsigned char to
int upcasts.

From my cursory review it looks good.
 
M

Mark Bluemel

Hello,

I would like to write a small example of I/O using standard library's
streams.
I would like the example to show how to test for possible errors
occurred during
file operations. Since I am not very experienced on the issue, I
would like to ask
if someone may give a look to my code and tell me if something is
wrong.
Of course the example works as I intended, in the implementation
I'am using now, when no error occurs during file operations, but I
don't have the possibility
to check it on many other implementations, and I don't know how to
make the file operations
fail in order to check the successful detection of the failure.

Some (fairly) easy options:-

* Run the test in a write-protected directory.
* Put a pause in the test and exhaust the space in the directory
with another program before trying to write to your file(s)
I hope no one is annoyed by the small comments in italian language.

#include<stdio.h>
#include<stdlib.h>

#define NOME_FILE_BIN_NUMERICO "streamBinario_formatoNumerico.out"
#define NOME_FILE_TEXT_NUMERICO "streamTesto_formatoNumerico.out"
#define NOME_FILE_BIN_TESTUALE "streamBinario_formatoTestuale.out"
#define NOME_FILE_TEXT_TESTUALE "streamTesto_formatoTestuale.out"

/* Funzione che stampa un messaggio di errore ed esce indicando
un fallimento all'ambiente operativo */
void fallisci(void) {
printf("Fallimento: Errore in un'operazione su file!\n");
exit(EXIT_FAILURE);
}

This doesn't tell you what operation failed or how. In a large program
this is a major disadvantage...

Why not add a parameter to indicate which operation failed - perhaps use
the C preprocessor macros __FILE__ and __LINE__ - and add a call to
perror() to show what went wrong...

Here's a simple example:-

$ cat fred.c
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

void fail(char *file, int line) {
printf("Failed %s(%d)\n",file,line);
perror(NULL);
exit(EXIT_FAILURE);
}

int main(void) {
FILE *f = fopen("banana.txt","r");
if (f == NULL) {
fail(__FILE__,__LINE__);
}
}

Here's how it looks when I run it (banana.txt doesn't exist).

$ ./fred
Failed fred.c(14)
No such file or directory
 
S

Stefan Ram

Mark Bluemel said:
void fail(char *file, int line) {
printf("Failed %s(%d)\n",file,line);
perror(NULL);
exit(EXIT_FAILURE);
}

When should one call »exit(EXIT_FAILURE)« and when should
one call »abort()« instead?

When a program exits upon an error, it might not be possible
to embed it as a subprogram or to extend it into a larger
program, because it might not be appropriate that the
failure of a subprogram already terminates the whole program.

A call to »fclose« was not necessary in your program,
because this is already handled by »exit« IIRC. But a
failure to close could thus be hidden from detection.
(Ok, but how should one interpret/handle a failure to
close a file with read access anyways?)
This also adds to the reasons that would make it more
difficult to embed it into a larger program.

Your program can be rewriten relatively easily into a
program that does not explicitly call »exit« anymore.

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

void fail( char const * const text )
{ fprintf( stderr, "Fallito il %s.\n", text );
perror( 0 ); }

int main1( FILE * const f )
{ /* use f here */
puts( "OK." ); return EXIT_SUCCESS; }

int main( void )
{ int result = EXIT_FAILURE;
FILE * const f = fopen( "alpha.txt", "r" );
if( !f )
fail( "tentativo di aprire il file \"alpha.txt\""
" per l'accesso in lettura" );
else
{ result = main1( f );
if( fclose( f ))
{ fail( "tentativo di chiudere il file \"alpha.txt\""
" con accesso in scrittura" );
/* result = EXIT_FAILURE; */ }}
puts( "Uscendo.");
return result; }

Fallito il tentativo di aprire il file "alpha.txt" per l'accesso in lettura.
No such file or directory
Uscendo.

The above program raises the »philosophical« question:
If a program to read and - say - print a file, did its
job and then fails to close the file read from, did the
program succeed or fail? After all, it already did its job.

I believe that there is no locale for the italian language
defined by ISO/IEC 9899:1999 (E), although specific
environments might provide such a locale. In this case,
a call such as

setlocale( LC_ALL, "it" );

might have the effect of perror working in italian, but
this might not be portable anymore.
 
S

Stefan Ram

Mark Bluemel said:
Some (fairly) easy options:-
* Run the test in a write-protected directory.
* Put a pause in the test and exhaust the space in the directory
with another program before trying to write to your file(s)

The OP could substitute the calls to the file functions by
calls to custom functions, which then can be made to return
a failure code or just forward to the real file functions.
 
J

James Kuyper

On 06/01/2011 12:41 PM, Stefan Ram wrote:
....
When should one call �exit(EXIT_FAILURE)� and when should
one call �abort()� instead?

When correct handling of the error condition allows the use of exit(),
but not abort(). Neither function provides error handling that I would
consider to be acceptable in my programs, but YMMV. My own code normally
reports not only the occurrence of error, but also the location where it
was detected, and the run-time context of the failure. It does this not
only at the point where the problem is detected, but also in the calling
function, all the way back to main(), which then returns with a value,
causing an implicit call to exit(); my code never explicitly calls it.

Calling exit() results in all functions registered with at_exit() to be
executed in the reverse of the order in which they were registered. It
flushes all output stream buffers, closes all open streams, and removes
all files created by tmpfile(). abort() does none of those things, but
it does raise SIGABRT; if a signal handler has been set up for that
signal, it will be executed.

If you care about none of those things, it makes no difference which you
use.
A call to �fclose� was not necessary in your program,
because this is already handled by �exit� IIRC.

I consider it lazy design to rely upon this fact. I prefer for files to
be explicitly closed, as soon as possible after they are no longer
needed, and for the closure to be explicit rather than an implicit
side-effect of exit(). YMMV.

....
I believe that there is no locale for the italian language
defined by ISO/IEC 9899:1999 (E), although specific
environments might provide such a locale.

Correct, other standards have something to say about that, but the C
standard only mandates the "C" and "" locales.
 
J

Jorgen Grahn

On 06/01/2011 12:41 PM, Stefan Ram wrote: ....

I consider it lazy design to rely upon this fact. I prefer for files to
be explicitly closed, as soon as possible after they are no longer
needed, and for the closure to be explicit rather than an implicit
side-effect of exit(). YMMV.

And I note that you're talking about a file opened for /reading/.
If it was opened for /writing/, it's not open for discussion -- you
need to close it explicitly and deal with the errors.

/Jorgen
 
J

James Kuyper

And I note that you're talking about a file opened for /reading/.
If it was opened for /writing/, it's not open for discussion -- you
need to close it explicitly and deal with the errors.

I had not intended my comments to be specific to files opened for
reading. It's not strictly necessary to close files opened for write
explicitly. As long as exit() is called, whether you do so explicitly or
implicitly by returning a value from main(), all output stream buffers
will be flushed, and the streams themselves closed.

But you're right about the error handling. Only by calling fclose()
explicitly can your program report any I/O errors detected upon closure.
However, many people feel this isn't worth bothering with; that's
covered by my "YMMV".
 
J

Jorgen Grahn

I had not intended my comments to be specific to files opened for
reading. It's not strictly necessary to close files opened for write
explicitly. As long as exit() is called, whether you do so explicitly or
implicitly by returning a value from main(), all output stream buffers
will be flushed, and the streams themselves closed.
Agreed.

But you're right about the error handling. Only by calling fclose()
explicitly can your program report any I/O errors detected upon closure.
However, many people feel this isn't worth bothering with; that's
covered by my "YMMV".

OK, it /is/ open for discussion. But I doubt that it's frequently
reasonable to ignore write errors -- your program will say everything
is OK, but the user will end up with a corrupt file. Silent data
corruption is one of the worst things that can happen IMHO.

/Jorgen
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,764
Messages
2,569,565
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top