File I/O

  • Thread starter Papadopoulos Giannis
  • Start date
P

Papadopoulos Giannis

Up to today I am using the code below for File i/o:

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

int main(int argc, char *argv[]) {
FILE *in, *out;

if(argc==1)
{
in = stdin;
out = stdout;
}
else
{
if(argc==2)
{
in = stdin;
if( (out = fopen(argv[1], "w"))==NULL )
{
perror("Output file creation");
exit(EXIT_FAILURE);
}
}
else
{
if( (in = fopen(argv[1], "r"))==NULL )
{
perror("Input file opening");
exit(EXIT_FAILURE);
}
if( (out = fopen(argv[2], "w"))==NULL )
{
perror("Output file creation");
fclose(in);
exit(EXIT_FAILURE);
}
}
}

/* some code here */

return EXIT_SUCCESS;
}

Am I forgetting something? Is there any addition to make the program
more robust and efficient??


--
#include <stdio.h>
#define p(s) printf(#s" endian")
int main(void){int v=1;*(char*)&v?p(Little):p(Big);return 0;}

Giannis Papadopoulos
http://dop.users.uth.gr/
University of Thessaly
Computer & Communications Engineering dept.
 
T

those who know me have no need of my name

in comp.lang.c i read:
if( (out = fopen(argv[1], "w"))==NULL )
{
perror("Output file creation");

the c standard does not require that errno be set to a useful value when
fopen fails, so this might produce a nonsensical or irrelevant message.
this makes fprintf(stderr, "could not open %s for writing\n", argv[1]) more
useful, for totally generic code. if you are using a system where other
standards do require errno to be useful you should use the filename as the
argument, e.g., perror(argv[1]), so that it appears in the resulting message.
 
E

E. Robert Tisdale

Papadopoulos said:
Up to today I am using the code below for File i/o:

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

int main(int argc, char *argv[]) {
FILE *in, *out;

if(argc==1)
{
in = stdin;
out = stdout;
}
else
{
if(argc==2)
{
in = stdin;
if( (out = fopen(argv[1], "w"))==NULL )
{
perror("Output file creation");
exit(EXIT_FAILURE);
}
}
else
{
if( (in = fopen(argv[1], "r"))==NULL )
{
perror("Input file opening");
exit(EXIT_FAILURE);
}
if( (out = fopen(argv[2], "w"))==NULL )
{
perror("Output file creation");
fclose(in);
exit(EXIT_FAILURE);
}
}
}

/* some code here */

return EXIT_SUCCESS;
}

Am I forgetting something?
Is there any addition to make the program more robust and efficient?
> cat File.c
#include <stdlib.h>
#include <stdio.h>

int main(int argc, char *argv[]) {
int result = EXIT_SUCCESS;
FILE* const out = (2 < argc)? fopen(argv[2], "w"):
(1 < argc)? fopen(argv[1], "w"): stdout;
if (NULL != out) {
FILE* const in = (2 < argc)? fopen(argv[1], "r"): stdin;
if (NULL != in) {
/* some code here */
if (stdin != in)
fclose(in);
}
else {
perror("Input file opening");
result = EXIT_FAILURE;
}
if (stdout != out)
fclose(out);
}
else {
perror("Output file creation");
result = EXIT_FAILURE;
}
return result;
}
 
G

Gordon Burditt

those who know me have no need of my name said:
in comp.lang.c i read:
if( (out = fopen(argv[1], "w"))==NULL )
{
perror("Output file creation");

the c standard does not require that errno be set to a useful value when
fopen fails, so this might produce a nonsensical or irrelevant message.

Since the C standard does not require that errno be set to a *USELESS*
value when fopen fails, this might produce a sensical or relevant
message. Therefore, it's WORTH PRINTING IT ANYWAY.
this makes fprintf(stderr, "could not open %s for writing\n", argv[1]) more
useful, for totally generic code. if you are using a system where other
standards do require errno to be useful you should use the filename as the
argument, e.g., perror(argv[1]), so that it appears in the resulting message.

If you are using a system where errno might *OR MIGHT NOT* be useful,
you should use the filename or a string including the filename as
the argument, so that it appears in the resulting message. The
message should include the information:

(a) What was attempted (e.g. opening a file),
(b) What object (e.g. a specific filename) it was attempted on,
and (c) Why the system says it failed, *ACCURATE OR NOT*.

I have yet to see a debugging situation where *NO* information was
an improvement over some information including information that was
irrelevant, out of date, or just wrong. If the end users don't
understand it, they can still cut and paste the error message into
a bug report. If on a particular system it is known that fopen()
happens to accurately return "Permission denied" errors when they
happen, even if all other errors always get nonsensical reasons,
then printing "Not a Typewriter" tells me that it's not a permission
issue.

Gordon L. Burditt
 
B

Bill Cunningham

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

int main(int argc, char *argv[]) {
FILE *in, *out;

if(argc==1)
{
in = stdin;
out = stdout;
}
else
{
if(argc==2)
{
in = stdin;
if( (out = fopen(argv[1], "w"))==NULL )
{
perror("Output file creation");
exit(EXIT_FAILURE);
}
}
else
{
if( (in = fopen(argv[1], "r"))==NULL )
{
perror("Input file opening");
exit(EXIT_FAILURE);
}
if( (out = fopen(argv[2], "w"))==NULL )
{
perror("Output file creation");
fclose(in);
exit(EXIT_FAILURE);
}
}
}

/* some code here */

return EXIT_SUCCESS;
}
I'm looking at Giannis's code here and I see the one thing in C I dread the
most. I avoid even looking at it if I can. But one must learn main (int
argc, char *argv[]). Those main parameters look like garbage to me, when
would one use them. Argc must mean an argument to main, the array pointer to
a char pointer leaves me clueless. I understand main(void). What's the
purpose of the other parameters?

Bill
 
C

CBFalconer

Papadopoulos said:
Up to today I am using the code below for File i/o:

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

int main(int argc, char *argv[]) {
FILE *in, *out;

if(argc==1)
{
in = stdin;
out = stdout;
}
else
{
if(argc==2)
{
in = stdin;
if( (out = fopen(argv[1], "w"))==NULL )
{
perror("Output file creation");
exit(EXIT_FAILURE);
}
}
else
{
if( (in = fopen(argv[1], "r"))==NULL )
{
perror("Input file opening");
exit(EXIT_FAILURE);
}
if( (out = fopen(argv[2], "w"))==NULL )
{
perror("Output file creation");
fclose(in);
exit(EXIT_FAILURE);
}
}
}

/* some code here */

return EXIT_SUCCESS;
}

Am I forgetting something? Is there any addition to make the program
more robust and efficient??

Yes. Look at the following recast for a quick lesson in
formatting, with the objective of making missing cases obvious.
All I have done to your code is remove the odd extranious brace
and cut indentation down to a reasonable level. You should now be
able to see the erroneous assumption at a glance.

if (argc == 1) {
in = stdin; out = stdout;
}
else if (argc == 2) {
in = stdin;
if ( (out = fopen(argv[1], "w"))== NULL ) {
perror("Output file creation");
exit(EXIT_FAILURE);
}
}
else {
if ( (in = fopen(argv[1], "r")) == NULL ) {
perror("Input file opening");
exit(EXIT_FAILURE);
}
if ( (out = fopen(argv[2], "w")) == NULL ) {
perror("Output file creation");
fclose(in);
exit(EXIT_FAILURE);
}
}

When you have figured out the problem consider adjusting your
formatting standards to make such a mistake more obvious in the
future, and let that drive your eventual formatting religion. In
the above case you should soon see that the code can be made much
more compact and obvious at the same time.

However, the original was a good effort, in fact very respectable.
 
J

Jan Engelhardt

Those main parameters look like garbage to me, when
would one use them. Argc must mean an argument to main, the array pointer to
a char pointer leaves me clueless. I understand main(void). What's the
purpose of the other parameters?

Suppose what happens when you type

ls -l



Jan Engelhardt
--
 
P

Papadopoulos Giannis

E. Robert Tisdale said:
Papadopoulos Giannis wrote:


cat File.c
#include <stdlib.h>
#include <stdio.h>

int main(int argc, char *argv[]) {
int result = EXIT_SUCCESS;
FILE* const out = (2 < argc)? fopen(argv[2], "w"):
(1 < argc)? fopen(argv[1], "w"): stdout;
if (NULL != out) {
FILE* const in = (2 < argc)? fopen(argv[1], "r"): stdin;
if (NULL != in) {
/* some code here */
if (stdin != in)
fclose(in);
}
else {
perror("Input file opening");
result = EXIT_FAILURE;
}
if (stdout != out)
fclose(out);
}
else {
perror("Output file creation");
result = EXIT_FAILURE;
}
return result;
}

I said robust and efficient, not more cryptic ;).. After all, the above
code uses more conditional jumps than mine...

Lastly, for /* some code here */ what happens if it is 100+ lines??
Should everything be inside the second if?????? I don't think so...

--
#include <stdio.h>
#define p(s) printf(#s" endian")
int main(void){int v=1;*(char*)&v?p(Little):p(Big);return 0;}

Giannis Papadopoulos
http://dop.users.uth.gr/
University of Thessaly
Computer & Communications Engineering dept.
 
P

Papadopoulos Giannis

Bill said:
I'm looking at Giannis's code here and I see the one thing in C I dread the
most. I avoid even looking at it if I can. But one must learn main (int
argc, char *argv[]). Those main parameters look like garbage to me, when
would one use them. Argc must mean an argument to main, the array pointer to
a char pointer leaves me clueless. I understand main(void). What's the
purpose of the other parameters?

Bill

Bill, the argc parameter tells the number of command line arguments that
where passed to the program (including program name)... The char *argv[]
is an array to char* that holds the arguments...

So strcmp(argv[0], "myprog") is always 0 in our case and the last
element is argv[argc-1]...

That is, being your "myprog", if you execute

# myprog

then

argc = 1; and argv[] = { "myprog" };

If you exec

# myprog i am a c freak

then

argc = 6; and argv[] = { "myprog", "i", "am", "a", "c", "freak" };

See K&R (they explain it more than enough).

--
#include <stdio.h>
#define p(s) printf(#s" endian")
int main(void){int v=1;*(char*)&v?p(Little):p(Big);return 0;}

Giannis Papadopoulos
http://dop.users.uth.gr/
University of Thessaly
Computer & Communications Engineering dept.
 
P

Papadopoulos Giannis

CBFalconer said:
Papadopoulos Giannis wrote:
Yes. Look at the following recast for a quick lesson in
formatting, with the objective of making missing cases obvious.
All I have done to your code is remove the odd extranious brace
and cut indentation down to a reasonable level. You should now be
able to see the erroneous assumption at a glance.

if (argc == 1) {
in = stdin; out = stdout;
}
else if (argc == 2) {
in = stdin;
if ( (out = fopen(argv[1], "w"))== NULL ) {
perror("Output file creation");
exit(EXIT_FAILURE);
}
}
else {
if ( (in = fopen(argv[1], "r")) == NULL ) {
perror("Input file opening");
exit(EXIT_FAILURE);
}
if ( (out = fopen(argv[2], "w")) == NULL ) {
perror("Output file creation");
fclose(in);
exit(EXIT_FAILURE);
}
}

When you have figured out the problem consider adjusting your
formatting standards to make such a mistake more obvious in the
future, and let that drive your eventual formatting religion. In
the above case you should soon see that the code can be made much
more compact and obvious at the same time.

However, the original was a good effort, in fact very respectable.

Hmm, I thought what I have typed was more understandable, but now I get
your point.. Indeed, your code is more maintainable... tnx



--
#include <stdio.h>
#define p(s) printf(#s" endian")
int main(void){int v=1;*(char*)&v?p(Little):p(Big);return 0;}

Giannis Papadopoulos
http://dop.users.uth.gr/
University of Thessaly
Computer & Communications Engineering dept.
 
M

Mike Wahler

Re: Main [was File IO]

Since your question is about an entirely different topic
from the original one of this thread, imo you should have
started a new thread. Whatever... See below.
int main(int argc, char *argv[]) {
I'm looking at Giannis's code here and I see the one thing in C I dread the
most. I avoid even looking at it if I can. But one must learn main (int
argc, char *argv[]). Those main parameters look like garbage to me, when
would one use them. Argc must mean an argument to main, the array pointer to
a char pointer leaves me clueless. I understand main(void). What's the
purpose of the other parameters?

They're for accessing a program's (typically 'command line')
parameters.

==============================================================

ISO/IEC 9899:1999 (E)

[....]

5.1.2.2.1 Program startup

1 The function called at program startup is named main. The
implementation declares no prototype for this function. It
shall be defined with a return type of int and with no
parameters:

int main(void) { /* ... */ }

or with two parameters (referred to here as argc and argv,
though any names may be used, as they are local to the
function in which they are declared):

int main(int argc, char *argv[]) { /* ... */ }

or equivalent;(9) or in some other implementation-defined
manner.

2 If they are declared, the parameters to the main function
shall obey the following constraints:

-- The value of argc shall be nonnegative.

-- argv[argc] shall be a null pointer.


-- If the value of argc is greater than zero, the array
members argv[0] through argv[argc-1] inclusive shall
contain pointers to strings, which are given implemen-
tation-defined values by the host environment prior to
program startup. The intent is to supply to the program
information determined prior to program startup from
elsewhere in the hosted environment. If the host envi-
ronment is not capable of supplying strings with letters
in both uppercase and lowercase, the implementation shall
ensure that the strings are received in lowercase.

-- If the value of argc is greater than zero, the string
pointed to by argv[0] represents the program name;
argv[0][0] shall be the null character if the program name
is not available from the host environment. If the value
of argc is greater than one, the strings pointed to by
argv[1] through argv[argc-1] represent the program
parameters.

-- The parameters argc and argv and the strings pointed to
by the argv array shall be modifiable by the program,
and retain their last-stored values between program startup
and program termination.

[....]

(9) Thus, int can be replaced by a typedef name defined as int,
or the type of argv can be written as char ** argv, and so on.

[....]

==============================================================


Example use:

#include <stdio.h>

int main(int argc, char **argv)
{
const char * const messages[] =
{
"[not available]",
"[none given]",
""
};

const char * const *pmsg[] = {messages, argv};
int i = 0;


printf("argc == %d\n\n", argc);

printf("Program name:\n%s\n\n",
argc > 0 ? *pmsg[**argv != 0]
: messages[argc > 0]);

printf("Parameters:\n");

for(i = 1; i < argc; ++i)
printf("[%d] == %s\n", i, argv);

printf("%s\n", messages[(argc > 1) + (argc > 0)]);
return 0;
}

-Mike
 
P

Peter Nilsson

Papadopoulos Giannis said:
Bill said:
...But one must learn main (int argc, char *argv[]). Those main
parameters look like garbage to me, when would one use them. ...

Bill, the argc parameter tells the number of command line arguments that
where passed to the program (including program name)... The char *argv[]
is an array to char* that holds the arguments...

So strcmp(argv[0], "myprog") is always 0 in our case and the last
element is argv[argc-1]...

No and no.

The argc parameter must be non-negative, but could be 0. The only guarantee
you have is that argv[argc] == NULL.

Even if argc is non-zero, argv[0] may be practically anything, including an
empty string.
That is, being your "myprog", if you execute

# myprog

then

argc = 1; and argv[] = { "myprog" };

Not necessarily. The following are all possible too...

argc == 0
argc == 1 && argv[0][0] == 0
argc == 1 && strcmp("/usr/local/bin/myprog", argv[0]) == 0
argc == 1 && strcmp("myprog.exe", argv[0]) == 0
argc == 1 && strcmp("C:\\MYPROGS\\MYPROG.EXE", argv[0]) == 0
 
M

Mike Wahler

Papadopoulos Giannis said:
Bill said:
I'm looking at Giannis's code here and I see the one thing in C I dread the
most. I avoid even looking at it if I can. But one must learn main (int
argc, char *argv[]). Those main parameters look like garbage to me, when
would one use them. Argc must mean an argument to main, the array pointer to
a char pointer leaves me clueless. I understand main(void). What's the
purpose of the other parameters?

Bill

Bill, the argc parameter tells the number of command line arguments that
where passed to the program (including program name)... The char *argv[]
is an array to char* that holds the arguments...

So strcmp(argv[0], "myprog") is always 0 in our case and the last
element is argv[argc-1]...

That is, being your "myprog", if you execute

# myprog

then

argc = 1; and argv[] = { "myprog" };

Not necessarily. argc is allowed to be zero (but not negative),
in which case no program name or parameters are available.
Also, even if argc > 0, argc[0][0] is allowed to be zero if
program name is not available. See the ISO quote in my
other post this thread.

If you exec

# myprog i am a c freak

then

argc = 6; and argv[] = { "myprog", "i", "am", "a", "c", "freak" };

Not necessarily. The first element could be "\0";

-Mike
 
P

Papadopoulos Giannis

Peter said:
Even if argc is non-zero, argv[0] may be practically anything, including an
empty string.

Never occured to me... The more I grow the more I learn...

--
#include <stdio.h>
#define p(s) printf(#s" endian")
int main(void){int v=1;*(char*)&v?p(Little):p(Big);return 0;}

Giannis Papadopoulos
http://dop.users.uth.gr/
University of Thessaly
Computer & Communications Engineering dept.
 
P

Peter Nilsson

....
argc = 6; and argv[] = { "myprog", "i", "am", "a", "c", "freak" };

Not necessarily. The first element could be "\0";

True, but I think it's more likely to be "" than "\0", although I can't
really justify the statement in any objective fashion. <g>
 
M

Mark McIntyre

in comp.lang.c i read:
if( (out = fopen(argv[1], "w"))==NULL )
{
perror("Output file creation");

the c standard does not require that errno be set to a useful value when
fopen fails, so this might produce a nonsensical or irrelevant message.

Since the C standard does not require that errno be set to a *USELESS*
value when fopen fails, this might produce a sensical or relevant
message. [/QUOTE]

Yes, it might for instance print the value of the last error, which is
certainly sensical and relevant, for some definition of those words:

"the tape in DRA0 is dismounted"
"emergency cluster shutdown has been initiated"
"tts registers have been disabled on audio device 1"
"The protocol went off the end of the frame."
"This error should not occur during normal usage."
"the disk in drive %C is not formatted: formatting now..."

Therefore, it's WORTH PRINTING IT ANYWAY.

hmm..... :)

certainly its worth it, if you like frightening your users !
 
R

Robert Bachmann

Papadopoulos said:
Up to today I am using the code below for File i/o: [...]
perror("Output file creation");
This is just a matter of taste, but I prefer to have
such kind of error messages in the following format:

fprintf(stderr,"%s: %s: %s\n",argv[0],filename,strerror(errno));

So if there is an error I would see a message like
progname: outfile.dat: Could not find hard disk
instead of
Output file creation: Could not find hard disk
 
C

CBFalconer

.... snip rotten Trollsdale code ...
I said robust and efficient, not more cryptic ;).. After all,
the above code uses more conditional jumps than mine...

You apparently have not yet learned to ignore anything Trollsdale
writes. It will normally contain nothing of value. Do not trust
his quotations either, they have often been edited to suit him.
 
C

CBFalconer

Papadopoulos said:
CBFalconer said:
Yes. Look at the following recast for a quick lesson in
formatting, with the objective of making missing cases obvious.
All I have done to your code is remove the odd extranious brace
and cut indentation down to a reasonable level. You should now be
able to see the erroneous assumption at a glance.

if (argc == 1) {
in = stdin; out = stdout;
}
else if (argc == 2) {
in = stdin;
if ( (out = fopen(argv[1], "w"))== NULL ) {
perror("Output file creation");
exit(EXIT_FAILURE);
}
}
else {
if ( (in = fopen(argv[1], "r")) == NULL ) {
perror("Input file opening");
exit(EXIT_FAILURE);
}
if ( (out = fopen(argv[2], "w")) == NULL ) {
perror("Output file creation");
fclose(in);
exit(EXIT_FAILURE);
}
}

When you have figured out the problem consider adjusting your
formatting standards to make such a mistake more obvious in the
future, and let that drive your eventual formatting religion. In
the above case you should soon see that the code can be made much
more compact and obvious at the same time.

However, the original was a good effort, in fact very respectable.

Hmm, I thought what I have typed was more understandable, but now I get
your point.. Indeed, your code is more maintainable... tnx

After which, here is how I would actually rewrite your snippet:

/* succeeds or exits */
FILE *xfopen(const char *fn, const char *fopts)
{
FILE *f;

errno = 0;
if (!(f = fopen(fn, fopts))) {
/* Gussy this up if you like */
perror(fn);
exit(EXIT_FAILURE);
}
return f;
} /* xfopen */

.....

in = stdin; out = stdout; /* defaults */
if (argc > 1) in = xfopen(argv[1], "r");
if (argc > 2) out = xfopen(argv[2], "w");

and I have a reuseable component xfopen() available for other
uses. I believe this version does everything your original did,
and also caters to argc == 0.

A minor point, which is now easily controlled (and seen). If the
user enters the same name for argv[1] and argv[2] opening the
'out' file could destroy the prospective 'in' file. Handling the
'in' file first usually gives some added assurance, because most
systems will refuse to open for write a file that is already open
for read. I noticed this in the simplified code and interchanged
the xfopen calls just before sending this message.

These are the sort of things Micro$loth is famous for ignoring.
They are not alone.
 

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,769
Messages
2,569,582
Members
45,071
Latest member
MetabolicSolutionsKeto

Latest Threads

Top