stream io in c

R

Ron Ford

A question came up in a syntax which is a common C extension, and I wanted
to answer how to solve it in C, as I believe that the solution is less than
ten lines and one that I could step through with gdb, my untoward debugger.

%- Writing $00-$FF via Fortran
%-
%- I am in need for an important project to write out simple
%- unsigned-char values in the range $00 through $FF,
%- without any extraneous data being added:
%-
%- open: Open the file for "raw 8-bit binary output"
%- Sequential writes; nothing fancy.
%- write: Write out individual unsigned 8-bit values with no
%- extraneous data whatsoever being involved
%- (a single value results in a single value going to output
%- file)
%- close: Close the file with no extraneous data being appended.
%- Size: File size is only the number of actual values written!

I think the solution in c could be as easy as instantiating a loop from
zero to 255, putchar'ing in the body of the loop, then redirecting output
to a file. If the creation of the file comes from the source, I suspect it
would have to be in 'rb' mode.

But I don't know. I'm hoping to use 8-bit devices, so I better get a good
handle on it. Thanks and cheers,
 
B

Ben Bacarisse

Ron Ford said:
A question came up in a syntax which is a common C extension, and I wanted
to answer how to solve it in C, as I believe that the solution is less than
ten lines and one that I could step through with gdb, my untoward debugger.

%- Writing $00-$FF via Fortran
%-
%- I am in need for an important project to write out simple
%- unsigned-char values in the range $00 through $FF,
%- without any extraneous data being added:
%-
%- open: Open the file for "raw 8-bit binary output"
%- Sequential writes; nothing fancy.
%- write: Write out individual unsigned 8-bit values with no
%- extraneous data whatsoever being involved
%- (a single value results in a single value going to output
%- file)
%- close: Close the file with no extraneous data being appended.
%- Size: File size is only the number of actual values written!

I think the solution in c could be as easy as instantiating a loop from
zero to 255, putchar'ing in the body of the loop, then redirecting output
to a file.

The output stream should be binary. On a system where there is no
distinction between text and binary streams you don't really need to
bother, but I would do a:

if (freopen(NULL, stdout, "wb") != NULL) {
/* OK, of we go... */
}

to be sure. Of course, if you are writing to a device/file, just open
with mode "wb".
If the creation of the file comes from the source, I suspect it
would have to be in 'rb' mode.

Parse error. Can you re-phrase?
 
R

Ron Ford

I think the solution in c could be as easy as instantiating a loop from
zero to 255, putchar'ing in the body of the loop, then redirecting output
to a file. If the creation of the file comes from the source, I suspect it
would have to be in 'rb' mode.

I've tried some variations with this.

int i;
for (i = 0; i <= UCHAR_MAX; i++) putchar (i);

When stdout is redirected to a file, I get a file of size 257 bytes. I
wanted to see if that would also be the case if I created the file from
source, but I've got type mismatches here:

#include <stdio.h>
int main(void)
{
FILE *fp;
char name[]="text58.txt";

fp=&name;
int c;

if ((fp = fopen(fp, "rb")) == NULL)
{
printf("can't open %s\n", fp);
return 1;
}
else
{
for (c = 0; c <= 255; c ++) {
putc(c, fp);
}
fclose(fp);
}
return 0;
}
// gcc -o chars mkchars1.c

This compiles but gcc warns of incompatible pointer types. As of now, it
tells me it can't open. I can't find an example in K&R where they hard-code
a filename like this, so I'm a little stuck.:-(
 
B

Ben Bacarisse

Ron Ford said:
I think the solution in c could be as easy as instantiating a loop from
zero to 255, putchar'ing in the body of the loop, then redirecting output
to a file. If the creation of the file comes from the source, I suspect it
would have to be in 'rb' mode.

I've tried some variations with this.

int i;
for (i = 0; i <= UCHAR_MAX; i++) putchar (i);

When stdout is redirected to a file, I get a file of size 257 bytes. I
wanted to see if that would also be the case if I created the file from
source, but I've got type mismatches here:

#include <stdio.h>
int main(void)
{
FILE *fp;
char name[]="text58.txt";

fp=&name;

No need to set fp here. Just use name in the fopen call. This
assignment does no make sense anyway, since the types don't match.
int c;

if ((fp = fopen(fp, "rb")) == NULL)

Use fopen(name, "wb") here. You are opening for reading but plan to
write to this stream.
{
printf("can't open %s\n", fp);

printf("can't open %s\n", name);
return 1;
}
else
{
for (c = 0; c <= 255; c ++) {
putc(c, fp);
}
fclose(fp);
}
return 0;
}
// gcc -o chars mkchars1.c

This compiles but gcc warns of incompatible pointer types. As of now, it
tells me it can't open.

This is because you try to open it for reading and, presumably, the
file does not yet exist.
 
K

Keith Thompson

Ron Ford said:
I've tried some variations with this.

int i;
for (i = 0; i <= UCHAR_MAX; i++) putchar (i);

When stdout is redirected to a file, I get a file of size 257 bytes. I
wanted to see if that would also be the case if I created the file from
source, but I've got type mismatches here:

Please indent your code. Run it through "indent -kr" if necessary
(and if you have the "indent" program).
#include <stdio.h>
int main(void)
{
FILE *fp;
char name[]="text58.txt";

Ok, so far, so good.
fp=&name;

What?

fp is a FILE*, something you use for things like fopen. You're
assigning the address of a character array to it. What exactly is
"fp=&name;" intended to accomplish?

Just delete that line.
int c;

if ((fp = fopen(fp, "rb")) == NULL)

And here you're trying to use fp both to hold the result of fopen and
to hold the name of the file.

A FILE* and the name of a file are two entirely different things. You
pass the name of a file to fopen, and it gives you a FILE*. (A FILE*
is a pointer to some blob of information; you don't need to know the
details, which will vary from system to system.)

You already have a variable that holds the name of the file. Use it.

... fp = fopen(name, "rb") ...
{
printf("can't open %s\n", fp);

Mis-using fp again. You want name. And consider printing the error
message to stderr rather than stdout (that's not a high priority).
return 1;

}
else
{
for (c = 0; c <= 255; c ++) {
putc(c, fp);

You opened fp as an input file. Now you're trying to write to it.

If you wanted to write to the file, you should have used mode "wb"
rather than "rb".
 
C

CBFalconer

Ron said:
.... snip ...

I've tried some variations with this.

int i;
for (i = 0; i <= UCHAR_MAX; i++) putchar (i);

When stdout is redirected to a file, I get a file of size 257
bytes. I wanted to see if that would also be the case if I
created the file from source, but I've got type mismatches here:

The system you are using affects this. Some will expand '\n' to cr
and lf, others just to lf, others to just cr, or even a line with a
length flag. Some will insist on adding an EOF marker, some wont.
If your system is ASCII you wrote a '\n' with the 11th character.
You wrote a '\0' with the first.

What you ARE guaranteed is that you get back what you wrote
(assuming proper file modes in the open argument).
 
K

Keith Thompson

CBFalconer said:
The system you are using affects this. Some will expand '\n' to cr
and lf, others just to lf, others to just cr, or even a line with a
length flag. Some will insist on adding an EOF marker, some wont.
If your system is ASCII you wrote a '\n' with the 11th character.
You wrote a '\0' with the first.

What you ARE guaranteed is that you get back what you wrote
(assuming proper file modes in the open argument).

I believe that's guaranteed only for binary file (except that they may
be padded at the end with an arbitrary number of zero bytes). There's
no such guarantee for text files.

For example, on a system that uses character 26 as an end-of-file
marker for text files, if you write character 26 in the middle of an
output file, you'll only see half the file when you try to read it.
 
R

Ron Ford

Ron Ford said:
I think the solution in c could be as easy as instantiating a loop from
zero to 255, putchar'ing in the body of the loop, then redirecting
output
to a file. If the creation of the file comes from the source, I suspect
it would have to be in 'rb' mode.

I've tried some variations with this.

int i;
for (i = 0; i <= UCHAR_MAX; i++) putchar (i);

When stdout is redirected to a file, I get a file of size 257 bytes. I
wanted to see if that would also be the case if I created the file from
source, but I've got type mismatches here:

#include <stdio.h>
int main(void)
{
FILE *fp;
char name[]="text58.txt";

fp=&name;

Huh? fp has type FILE *, but &name has type char (*)[11]. How can it
possibly make sense to you to assign the one to the other?

It would seem that I was trying to shoe-horn a char* into a file*. I
believed that the & operator hands the LHS the address of the object, in
this case, the name of a file.

Serious
question. What on earth do you think this assignment means? Why did you
type it? (Answering that question might be the most important way you can
learn from this thread.)

LHS is a pointer. Since I could not create a sensical thingamajig of type
FILE--everything I tried was a syntax error--I tried chars. There's no
guarantees on pointers like this; why would there be a prohibition?
 
S

santosh

[ ... ]
#include <stdio.h>
int main(void)
{
FILE *fp;
char name[]="text58.txt";

fp=&name;

Huh? fp has type FILE *, but &name has type char (*)[11]. How can it
possibly make sense to you to assign the one to the other?

It would seem that I was trying to shoe-horn a char* into a file*.

You mean a FILE*?

In any case the FILE type holds critical data that is needed to operate
on the associated stream, like address of buffers, status flags etc.
You simply cannot stuff a FILE* object with the address of any object
other than a FILE, and hope get anything right. This is in fact an
illustration of a general rule in C, viz., object pointers of type T*
can only hold NULL or addresses of objects of type T. An exception is
the void* which can hold NULL or values of any object pointer type.
I believed that the & operator hands the LHS the address of the
object, in this case, the name of a file.

The & operator indeed computes the address of it's operand, but in your
case the address generated is the address of the array name, not the
address of it's first element. The values would be identical, but the
type differs.
LHS is a pointer. Since I could not create a sensical thingamajig of
type FILE

That's what the fopen function is for.
--everything I tried was a syntax error--I tried chars. There's
no guarantees on pointers like this; why would there be a prohibition?

Because there are implementations were the sizes and representations of
various pointer types are different and a value of type A* need not
make any sense when interpreted as a value of type B*.

<snip>
 
R

Ron Ford

Ron Ford said:
I've tried some variations with this.

int i;
for (i = 0; i <= UCHAR_MAX; i++) putchar (i);

When stdout is redirected to a file, I get a file of size 257 bytes. I
wanted to see if that would also be the case if I created the file from
source, but I've got type mismatches here:

Please indent your code. Run it through "indent -kr" if necessary
(and if you have the "indent" program).
#include <stdio.h>
int main(void)
{
FILE *fp;
char name[]="text58.txt";

Ok, so far, so good.
fp=&name;

What?

fp is a FILE*, something you use for things like fopen. You're
assigning the address of a character array to it. What exactly is
"fp=&name;" intended to accomplish?

Just delete that line.
int c;

if ((fp = fopen(fp, "rb")) == NULL)

And here you're trying to use fp both to hold the result of fopen and
to hold the name of the file.

A FILE* and the name of a file are two entirely different things. You
pass the name of a file to fopen, and it gives you a FILE*. (A FILE*
is a pointer to some blob of information; you don't need to know the
details, which will vary from system to system.)

You already have a variable that holds the name of the file. Use it.

... fp = fopen(name, "rb") ...
{
printf("can't open %s\n", fp);

Mis-using fp again. You want name. And consider printing the error
message to stderr rather than stdout (that's not a high priority).
return 1;

}
else
{
for (c = 0; c <= 255; c ++) {
putc(c, fp);

You opened fp as an input file. Now you're trying to write to it.

If you wanted to write to the file, you should have used mode "wb"
rather than "rb".
}
fclose(fp);
}
return 0;
}
// gcc -o chars mkchars1.c

This compiles but gcc warns of incompatible pointer types. As of now, it
tells me it can't open. I can't find an example in K&R where they hard-code
a filename like this, so I'm a little stuck.:-(

Thanks, Keith, I seem to be doing much better:


#include <stdio.h>
int main(void)
{
FILE *fp;
char name[]="text58.txt";


int c;

if ((fp = fopen(name, "wb")) == NULL)
{
printf("can't open %s\n", fp);
return 1;
}
else
{
for (c = 0; c <= 255; c ++) {
putc(c, fp);
}
fclose(fp);
}
return 0;
}
// gcc -o chars mkchars2.c

It seems to compile and behave:

C:\MinGW\source> gcc -o chars mkchars2.c

C:\MinGW\source>chars

C:\MinGW\source>dir
Volume in drive C has no label.
Volume Serial Number is 486B-CFF3

Directory of C:\MinGW\source

[...]
07/31/2008 02:58 AM 309 mkchars1.c
07/30/2008 06:31 PM 257 text55.txt
07/30/2008 06:56 PM 2,592 mkchars1.o
07/30/2008 06:56 PM 19,581 new.exe
07/31/2008 03:06 AM 312 mkchars2.c
07/31/2008 03:06 AM 256 text58.txt
07/31/2008 03:06 AM 17,203 chars.exe
27 File(s) 108,824 bytes
3 Dir(s) 492,433,408 bytes free

I think this shows a difference between a way to get a file with 257 bytes
(> .txt) and one without an ultimate -1.

I'll tune up the minor points when I can follow through. Best regards,
 
R

Richard Bos

Ron Ford said:
Ron Ford said:
FILE *fp;
char name[]="text58.txt";

fp=&name;
question. What on earth do you think this assignment means? Why did you
type it? (Answering that question might be the most important way you can
learn from this thread.)

LHS is a pointer. Since I could not create a sensical thingamajig of type
FILE--everything I tried was a syntax error--I tried chars. There's no
guarantees on pointers like this; why would there be a prohibition?

That's a nice example of magical thinking. "This looks, in one trivial
detail, like that; therefore, this should work, in all essentials, like
that." No. A pointer to FILE and a pointer to array of char are quite
different, and what's much more important, a FILE and an array of char
are very different. If you want to be a programmer, you must learn to
make sure that you _know_, rather than guess.

Richard
 
S

santosh

Ron Ford wrote:

Thanks, Keith, I seem to be doing much better:


#include <stdio.h>
int main(void)
{
FILE *fp;
char name[]="text58.txt";

If you are using tabs to indent your code, consider switching to spaces,
since tabs are often stripped out by Usenet software.
int c;

if ((fp = fopen(name, "wb")) == NULL)
{
printf("can't open %s\n", fp);

The 's' type specifier expects a char* argument that must point at a
string. You have supplied it a FILE* argument, a certain route to
undefined behaviour.
return 1;

And one is not a portable return value. The portable values are 0,
EXIT_SUCCESS and EXIT_FAILURE.

Surely you must have noted these elementary things as they are often
mentioned in this group, besides being in it's FAQ.
}
else
{
for (c = 0; c <= 255; c ++) {
putc(c, fp);

Since you are writing raw byte values, you might consider changing the
misleading filename extension.

Also the maximum value of an unsigned byte in C is given by UCHAR_MAX.
It is not necessarily 256. And note that 'c' is declared as an int, not
unsigned char.
}
fclose(fp);
}
return 0;
}
// gcc -o chars mkchars2.c

This command is not a conforming implementation of ISO C. For that you
need:

gcc -ansi -pedantic /* For conformance to C90 */
gcc -std=c99 -pedantic /* For incomplete but good conformance to C99
*/

Also add the -Wall and -W flags for extra diagnostics which are always a
help.

<snip>
 
R

Ron Ford

This command is not a conforming implementation of ISO C. For that you
need:

gcc -ansi -pedantic /* For conformance to C90 */
gcc -std=c99 -pedantic /* For incomplete but good conformance to C99
*/

Also add the -Wall and -W flags for extra diagnostics which are always a
help.

//mkchars.c:



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

int main(void)
{
FILE *fp;
char name[]="text62.txt";


int c;

if ((fp = fopen(name, "wb")) == NULL)
{
printf("can't open %s\n", fp);
return EXIT_FAILURE;
}
else
{
for (c = 0; c <= 255; c ++) {
putc(c, fp);
}
fclose(fp);
}
return 0;
}
// gcc -o chars -std=c99 -pedantic mkchars3.c
 
S

santosh

Ron said:
This command is not a conforming implementation of ISO C. For that
you need:

gcc -ansi -pedantic /* For conformance to C90 */
gcc -std=c99 -pedantic /* For incomplete but good conformance to
C99
*/

Also add the -Wall and -W flags for extra diagnostics which are
always a help.

//mkchars.c:



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

int main(void)
{
FILE *fp;
char name[]="text62.txt";


int c;

if ((fp = fopen(name, "wb")) == NULL)
{
printf("can't open %s\n", fp);

Can you spot the mistake in this printf call?
return EXIT_FAILURE;
}
else
{
for (c = 0; c <= 255; c ++) {
putc(c, fp);
}
fclose(fp);
}
return 0;
}
// gcc -o chars -std=c99 -pedantic mkchars3.c

If you had added the -Wall and -W flags to gcc, it would have warned you
of your botch-up with printf.
 
R

Richard

Ben Bacarisse said:
I suspect a joke, but I'm not getting it. Can you labour the point
for the rest of us?

I will hazard he meant:

,----
| for (int i = 0; i < UCHAR_MAX; i++) putchar (i);
`----

as in C99.

Although frankly I think it#s horrible layout. Hard to debug. Hard to
see the body.

,----
| for (int i = 0; i < UCHAR_MAX; i++)
| putchar (i);
`----

....
 
B

Ben Bacarisse

Richard said:
I will hazard he meant:

,----
| for (int i = 0; i < UCHAR_MAX; i++) putchar (i);
`----

as in C99.

The bit I am not getting is changing test from the correct one to one
that does not do what the OP wants. If the intent was to change the
scope of i, why change the test as well?
 
C

CBFalconer

Nick said:
.... snip ...


for (i = 0; i < UCHAR_MAX; i++) putchar (i);

No, the original is correct. He wants to write out the entire
range available to the char type.
 
K

Keith Thompson

Ron Ford said:
This command is not a conforming implementation of ISO C. For that you
need:

gcc -ansi -pedantic /* For conformance to C90 */
gcc -std=c99 -pedantic /* For incomplete but good conformance to C99
*/

Also add the -Wall and -W flags for extra diagnostics which are always a
help.

//mkchars.c:



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

int main(void)
{
FILE *fp;
char name[]="text62.txt";


int c;

if ((fp = fopen(name, "wb")) == NULL)
{
printf("can't open %s\n", fp);
return EXIT_FAILURE;
}
else
{
for (c = 0; c <= 255; c ++) {
putc(c, fp);
}
fclose(fp);
}
return 0;
}
// gcc -o chars -std=c99 -pedantic mkchars3.c

Compared to the previous version you posted, you added
"#include <stdlib.h>", you changed the file name from "text58.txt" to
"text62.txt", and you changed "return 1;" to "return EXIT_FAILURE;".

Oh, and you didn't mention what changes you had made; I had to save
both versions and compare them myself.

You ignored the vast majority of santosh's excellent advice.
Specifically:

Your indentation is still non-existent.

You're still trying to print a FILE* value using "%s".

You're still using a misleading file name.
 

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,766
Messages
2,569,569
Members
45,042
Latest member
icassiem

Latest Threads

Top