Write to file

B

Bill

Hello,

I'm trying to output buffer content to a file. I either get an access
violation error, or crazy looking output in the file depending on
which method I use to write the file. Can anyone help out a newbie?


#include <stdio.h>
#include <ctype.h>
#include <string.h>

#define DISPLAY 117 /* Length of display line */
#define PAGE_LENGTH 20 /* Lines per page */

int main(int argc, char *argv[])
{
FILE *pfile;
FILE *outfile;
char *p;

unsigned char buffer[DISPLAY/4 - 1]; /* File input buffer */
int count = 0; /* Count of characters in buffer */
int lines = 0; /* Number of lines displayed */
int i = 0; /* Loop counter */

char string [100];
pfile = fopen ("my.txt" , "r");

while(!feof(pfile)) /* Continue until end of file */
{
if(count < sizeof buffer) /* If the buffer is not full */
/* Read a character */
buffer[count++] = (unsigned char)fgetc(pfile);
else
{
/* Now display buffer contents as characters */
for(count = 0; count < sizeof buffer; count++)
printf("%c", isprint(buffer[count]) ? buffer[count]:'.');
printf("\n"); /* End the line */
count = 0; /* Reset count */

if(!(++lines%PAGE_LENGTH)) /* End of page? */
if(getchar()=='E') /* Wait for Enter */
return 0; /* E pressed */
}
}

/* Display last line as characters */
for(i = 0; i < count; i++)
{
printf("%c",isprint(buffer) ? buffer:'.');
}

printf("\n");
fclose(pfile); /* Close the file */

//re-open file and write out the buffer contents
outfile = fopen ("myOutput.txt" , "w");

if(outfile==NULL)
{
return 0;
}

//causes an access violation error
for(count = 0; count < sizeof buffer; count++)
{
fputc(outfile, isprint(buffer[count]) ? buffer[count]:'.');
}

//causes crazy output in file
/*for(count = 0; count < sizeof buffer; count++)
{
(int) fwrite(buffer, 1, isprint(buffer[count]), outfile);
}*/

fclose(outfile);
getchar();
return 0;
}
 
B

Ben Bacarisse

Bill said:
Hello,

I'm trying to output buffer content to a file. I either get an access
violation error, or crazy looking output in the file depending on
which method I use to write the file. Can anyone help out a newbie?
Sure...

#include <stdio.h>
#include <ctype.h>
#include <string.h>

#define DISPLAY 117 /* Length of display line */
#define PAGE_LENGTH 20 /* Lines per page */

int main(int argc, char *argv[])
{
FILE *pfile;
FILE *outfile;
char *p;

unsigned char buffer[DISPLAY/4 - 1]; /* File input buffer */
int count = 0; /* Count of characters in buffer */
int lines = 0; /* Number of lines displayed */
int i = 0; /* Loop counter */

char string [100];
pfile = fopen ("my.txt" , "r");

You should check it worked. I know you know this!
while(!feof(pfile)) /* Continue until end of file */

This will cause you some surprises and won't do what you think. You
should check the return from the read operation (in you case) fgetc:

int c;
....
while ((c = fgetc(pfile)) != EOF) {
...

The feof function only returns a non-zero value *after* an input
operation has failed due to and end-of-file condition. It should be
used only to tell *why* things have gone wrong.
{
if(count < sizeof buffer) /* If the buffer is not full */
/* Read a character */
buffer[count++] = (unsigned char)fgetc(pfile);
else
{
/* Now display buffer contents as characters */
for(count = 0; count < sizeof buffer; count++)
printf("%c", isprint(buffer[count]) ? buffer[count]:'.');
printf("\n"); /* End the line */
count = 0; /* Reset count */

if(!(++lines%PAGE_LENGTH)) /* End of page? */
if(getchar()=='E') /* Wait for Enter */
return 0; /* E pressed */

There getchar calls will normally be working on a buffered input so it
is not a good way to "pause" your program. If I wanted to do this (and
for some reason I don't these days) I'd write a function to read a
whole line of input and decide what to do based on that.
}
}

/* Display last line as characters */
for(i = 0; i < count; i++)
{
printf("%c",isprint(buffer) ? buffer:'.');
}

printf("\n");
fclose(pfile); /* Close the file */


You should check if this worked also.
//re-open file and write out the buffer contents
outfile = fopen ("myOutput.txt" , "w");

if(outfile==NULL)
{
return 0;
}

//causes an access violation error
for(count = 0; count < sizeof buffer; count++)
{
fputc(outfile, isprint(buffer[count]) ? buffer[count]:'.');

You should check that this worked. Get in the habit of checking
everything that there is to check!
}

//causes crazy output in file
/*for(count = 0; count < sizeof buffer; count++)
{
(int) fwrite(buffer, 1, isprint(buffer[count]), outfile);
}*/

This will repeatedly try to print the first character in buffer as
many times as there are printable characters in the buffer!. Also, by
using it this way (asking to write no data sometimes) you make error
checking much harder. Stick with the fputc method. It is fine. For
the record you probably intended:

fwrite(&buffer[count], 1, isprint(buffer[count]), outfile);

If you want to print a whole buffer. Process it first:

for (count = 0; count < sizeof buffer; count++)
if (!isprint(buffer[count]))
buffer[count] = '.';

if (fwrite(buffer, 1, count, outfile) != count)
/* report a write error... */
 
B

Bill

Bill said:
I'm trying to output buffer content to a file. I either get an access
violation error, or crazy looking output in the file depending on
which method I use to write the file. Can anyone help out a newbie?
Sure...





#include <stdio.h>
#include <ctype.h>
#include <string.h>
#define DISPLAY 117 /* Length of display line */
#define PAGE_LENGTH 20 /* Lines per page */
int main(int argc, char *argv[])
{
FILE *pfile;
FILE *outfile;
char *p;
unsigned char buffer[DISPLAY/4 - 1]; /* File input buffer */
int count = 0; /* Count of characters in buffer */
int lines = 0; /* Number of lines displayed */
int i = 0; /* Loop counter */
char string [100];
pfile = fopen ("my.txt" , "r");

You should check it worked. I know you know this!
while(!feof(pfile)) /* Continue until end of file */

This will cause you some surprises and won't do what you think. You
should check the return from the read operation (in you case) fgetc:

int c;
....
while ((c = fgetc(pfile)) != EOF) {
...

The feof function only returns a non-zero value *after* an input
operation has failed due to and end-of-file condition. It should be
used only to tell *why* things have gone wrong.
{
if(count < sizeof buffer) /* If the buffer is not full */
/* Read a character */
buffer[count++] = (unsigned char)fgetc(pfile);
else
{
/* Now display buffer contents as characters */
for(count = 0; count < sizeof buffer; count++)
printf("%c", isprint(buffer[count]) ? buffer[count]:'.');
printf("\n"); /* End the line */
count = 0; /* Reset count */
if(!(++lines%PAGE_LENGTH)) /* End of page? */
if(getchar()=='E') /* Wait for Enter */
return 0; /* E pressed */

There getchar calls will normally be working on a buffered input so it
is not a good way to "pause" your program. If I wanted to do this (and
for some reason I don't these days) I'd write a function to read a
whole line of input and decide what to do based on that.
/* Display last line as characters */
for(i = 0; i < count; i++)
{
printf("%c",isprint(buffer) ? buffer:'.');
}

printf("\n");
fclose(pfile); /* Close the file */

You should check if this worked also.
//re-open file and write out the buffer contents
outfile = fopen ("myOutput.txt" , "w");
if(outfile==NULL)
{
return 0;
}
//causes an access violation error
for(count = 0; count < sizeof buffer; count++)
{
fputc(outfile, isprint(buffer[count]) ? buffer[count]:'.');

You should check that this worked. Get in the habit of checking
everything that there is to check!
//causes crazy output in file
/*for(count = 0; count < sizeof buffer; count++)
{
(int) fwrite(buffer, 1, isprint(buffer[count]), outfile);
}*/

This will repeatedly try to print the first character in buffer as
many times as there are printable characters in the buffer!. Also, by
using it this way (asking to write no data sometimes) you make error
checking much harder. Stick with the fputc method. It is fine. For
the record you probably intended:

fwrite(&buffer[count], 1, isprint(buffer[count]), outfile);

If you want to print a whole buffer. Process it first:

for (count = 0; count < sizeof buffer; count++)
if (!isprint(buffer[count]))
buffer[count] = '.';

if (fwrite(buffer, 1, count, outfile) != count)
/* report a write error... */


fclose(outfile);
getchar();
return 0;
}


Thanks alot. I'm making progress. No errors now, but the output in
the file is screwy. The code:


#include <stdio.h>
#include <ctype.h>
#include <string.h>

#define DISPLAY 117 /* Length of display line */
#define PAGE_LENGTH 20 /* Lines per page */

int main(int argc, char *argv[])
{
FILE *pfile; /* File pointer */
FILE *outfile;
char *p;
int c;

unsigned char buffer[DISPLAY/4 - 1]; /* File input buffer */
int count = 0; /* Count of characters in buffer */
int lines = 0; /* Number of lines displayed */
int i = 0; /* Loop counter */

char string [100];
pfile = fopen ("my.txt" , "r");


while((c = fgetc(pfile)) != EOF) /* Continue until end of
file */
{
if(count < sizeof buffer) /* If the buffer is not full */
/* Read a character */
buffer[count++] = (unsigned char)fgetc(pfile);
else
{
/* Now display buffer contents as characters */
for(count = 0; count < sizeof buffer; count++)
printf("%c", isprint(buffer[count]) ? buffer[count]:'.');
printf("\n"); /* End the line */
count = 0; /* Reset count */

if(!(++lines%PAGE_LENGTH)) /* End of page? */
if(getchar()=='E') /* Wait for Enter */
return 0; /* E pressed */
}
}

/* Display last line as characters */
for(i = 0; i < count; i++)
{
printf("%c",isprint(buffer) ? buffer:'.');
}

printf("\n");
fclose(pfile); /* Close the file */

//re-open file and write out the buffer contents
outfile = fopen ("myOutput.txt" , "w");

if(outfile==NULL)
{
return 0;
}


for(count = 0; count < sizeof buffer; count++)
{
if(!isprint(buffer[count]))
{
buffer[count] = '.';
}
else
{
fputc(buffer[count],outfile);
}
//fputc(outfile, isprint(buffer[count]) ? buffer[count]:'.');
}

/*for(count = 0; count < sizeof buffer; count++)
{
(int) fwrite(buffer, 1, isprint(buffer[count]), outfile);
}*/

fclose(outfile);
getchar();
return 0;
}

Input file contents:
Fred Flinstone 123-456-7890
Barney Rubble 123-789-4561

Output file:
B12y7p456r
 
B

Ben Bacarisse

Bill said:
Thanks alot. I'm making progress. No errors now, but the output in
the file is screwy. The code:

Please trip your replies (and always trim signature lines).
#include <stdio.h>
#include <ctype.h>
#include <string.h>

#define DISPLAY 117 /* Length of display line */
#define PAGE_LENGTH 20 /* Lines per page */

int main(int argc, char *argv[])
{
FILE *pfile; /* File pointer */
FILE *outfile;
char *p;
int c;

unsigned char buffer[DISPLAY/4 - 1]; /* File input buffer */
int count = 0; /* Count of characters in buffer */
int lines = 0; /* Number of lines displayed */
int i = 0; /* Loop counter */

char string [100];
pfile = fopen ("my.txt" , "r");


while((c = fgetc(pfile)) != EOF) /* Continue until end of
file */
{
if(count < sizeof buffer) /* If the buffer is not full */
/* Read a character */
buffer[count++] = (unsigned char)fgetc(pfile);

I intended you you use c here not read another character!
else
{
/* Now display buffer contents as characters */
for(count = 0; count < sizeof buffer; count++)
printf("%c", isprint(buffer[count]) ? buffer[count]:'.');
printf("\n"); /* End the line */
count = 0; /* Reset count */

Your input is long enough to trigger this condition. You reuse the buffer.
if(!(++lines%PAGE_LENGTH)) /* End of page? */
if(getchar()=='E') /* Wait for Enter */
return 0; /* E pressed */
}
}

/* Display last line as characters */
for(i = 0; i < count; i++)
{
printf("%c",isprint(buffer) ? buffer:'.');
}

printf("\n");
fclose(pfile); /* Close the file */

//re-open file and write out the buffer contents
outfile = fopen ("myOutput.txt" , "w");

if(outfile==NULL)
{
return 0;
}


for(count = 0; count < sizeof buffer; count++)
{
if(!isprint(buffer[count]))
{
buffer[count] = '.';
}
else
{
fputc(buffer[count],outfile);
}


You took a suggestion to modify the buffer and combined it with single
character printing to produce something rather odd. It is not wrong,
just odd. Why change buffer[count] when is it not a printing
character if you don't then print it?
//fputc(outfile, isprint(buffer[count]) ? buffer[count]:'.');
}

/*for(count = 0; count < sizeof buffer; count++)
{
(int) fwrite(buffer, 1, isprint(buffer[count]), outfile);
}*/

fclose(outfile);
getchar();
return 0;
}

Input file contents:
Fred Flinstone 123-456-7890
Barney Rubble 123-789-4561

Output file:
B12y7p456r

Not what I get, but still.

I should point out that although I have only commented on details, the
overall design of this program looks a bit wacky. If you intend to
process text files, why do you have a buffer of size 117/4-1? Why are
you happy to simply reuse the space when you read more than these 28
characters? If the input has 30 characters, your buffer will contain
numbers 29 and 30 followed by character numbers 3, 4 and so on. It
all looks very odd.

You should maybe say what you are actually trying to do.
 
B

Bill

Bill said:
Thanks alot. I'm making progress. No errors now, but the output in
the file is screwy. The code:

Please trip your replies (and always trim signature lines).




#include <stdio.h>
#include <ctype.h>
#include <string.h>
#define DISPLAY 117 /* Length of display line */
#define PAGE_LENGTH 20 /* Lines per page */
int main(int argc, char *argv[])
{
FILE *pfile; /* File pointer */
FILE *outfile;
char *p;
int c;
unsigned char buffer[DISPLAY/4 - 1]; /* File input buffer */
int count = 0; /* Count of characters in buffer */
int lines = 0; /* Number of lines displayed */
int i = 0; /* Loop counter */
char string [100];
pfile = fopen ("my.txt" , "r");
while((c = fgetc(pfile)) != EOF) /* Continue until end of
file */
{
if(count < sizeof buffer) /* If the buffer is not full */
/* Read a character */
buffer[count++] = (unsigned char)fgetc(pfile);

I intended you you use c here not read another character!
else
{
/* Now display buffer contents as characters */
for(count = 0; count < sizeof buffer; count++)
printf("%c", isprint(buffer[count]) ? buffer[count]:'.');
printf("\n"); /* End the line */
count = 0; /* Reset count */

Your input is long enough to trigger this condition. You reuse the buffer.




if(!(++lines%PAGE_LENGTH)) /* End of page? */
if(getchar()=='E') /* Wait for Enter */
return 0; /* E pressed */
}
}
/* Display last line as characters */
for(i = 0; i < count; i++)
{
printf("%c",isprint(buffer) ? buffer:'.');
}

printf("\n");
fclose(pfile); /* Close the file */
//re-open file and write out the buffer contents
outfile = fopen ("myOutput.txt" , "w");
if(outfile==NULL)
{
return 0;
}
for(count = 0; count < sizeof buffer; count++)
{
if(!isprint(buffer[count]))
{
buffer[count] = '.';
}
else
{
fputc(buffer[count],outfile);
}

You took a suggestion to modify the buffer and combined it with single
character printing to produce something rather odd. It is not wrong,
just odd. Why change buffer[count] when is it not a printing
character if you don't then print it?




//fputc(outfile, isprint(buffer[count]) ? buffer[count]:'.');
}
/*for(count = 0; count < sizeof buffer; count++)
{
(int) fwrite(buffer, 1, isprint(buffer[count]), outfile);
}*/
fclose(outfile);
getchar();
return 0;
}
Input file contents:
Fred Flinstone 123-456-7890
Barney Rubble 123-789-4561
Output file:
B12y7p456r

Not what I get, but still.

I should point out that although I have only commented on details, the
overall design of this program looks a bit wacky. If you intend to
process text files, why do you have a buffer of size 117/4-1? Why are
you happy to simply reuse the space when you read more than these 28
characters? If the input has 30 characters, your buffer will contain
numbers 29 and 30 followed by character numbers 3, 4 and so on. It
all looks very odd.

You should maybe say what you are actually trying to do.

--
Ben.- Hide quoted text -

- Show quoted text -- Hide quoted text -

- Show quoted text -- Hide quoted text -

- Show quoted text -


Yes. It has become wacky. What I'm trying to do is read the contents
of a file into a buffer, find a particular string in the buffer, make
an edit in the found string and write the same file back out.
 
B

Ben Bacarisse

Bill said:
Yes. It has become wacky. What I'm trying to do is read the contents
of a file into a buffer, find a particular string in the buffer, make
an edit in the found string and write the same file back out.

Ah, well... that could be done much more simply. Presumably this is some
class assignment or you would simply use one of the many fine tools
that can do this?

You did not take my advice to trim your replies (you quoted everything
including my signature block). That will have won you no favours from
others here and it discourages me from helping further.
 
B

Bill

You did not take my advice to trim your replies (you quoted everything
including my signature block). That will have won you no favours from
others here and it discourages me from helping further.

Sorry. Yes it is an assignment, but I'm trying to take it further
than what was assigned.
I really wish to learn the language.
 
B

Bryan

Thanks alot. I'm making progress. No errors now, but the output in
the file is screwy. The code:

#include <stdio.h>
#include <ctype.h>
#include <string.h>

#define DISPLAY 117 /* Length of display line */
#define PAGE_LENGTH 20 /* Lines per page */

int main(int argc, char *argv[])
{
FILE *pfile; /* File pointer */
FILE *outfile;
char *p;
int c;

unsigned char buffer[DISPLAY/4 - 1]; /* File input buffer */

Initialising this would be advisable, given what you're going to do
with it later.
int count = 0; /* Count of characters in buffer */
int lines = 0; /* Number of lines displayed */
int i = 0; /* Loop counter */

char string [100];
pfile = fopen ("my.txt" , "r");

while((c = fgetc(pfile)) != EOF) /* Continue until end of
file */
{
if(count < sizeof buffer) /* If the buffer is not full */
/* Read a character */
buffer[count++] = (unsigned char)fgetc(pfile);

This needs to be changed to :
buffer[count++] = (unsigned char)c;

As otherwise you are reading the first character in the while loop and
then ignoring it and reading a second character here. This is why you
get every other character in the output.

else
{
/* Now display buffer contents as characters */
for(count = 0; count < sizeof buffer; count++)
printf("%c", isprint(buffer[count]) ? buffer[count]:'.');
printf("\n"); /* End the line */
count = 0; /* Reset count */

This is what's causing your output file to look a bit odd. Once you've
read the input up to a full buffer, you just print it to screen and
then re-start at the beginning of the buffer. This data never (except
for the last couple of rows, by chance) goes to the datafile and so
you don't get everything that you're after. So you either need to
output it here or store it somehow.
if(!(++lines%PAGE_LENGTH)) /* End of page? */
if(getchar()=='E') /* Wait for Enter */
return 0; /* E pressed */
}
}

/* Display last line as characters */
for(i = 0; i < count; i++)
{
printf("%c",isprint(buffer) ? buffer:'.');
}

printf("\n");
fclose(pfile); /* Close the file */

//re-open file and write out the buffer contents
outfile = fopen ("myOutput.txt" , "w");

if(outfile==NULL)
{
return 0;
}

for(count = 0; count < sizeof buffer; count++)


You also don't know that all of buffer has been filled. It's not
initialised so it could well be causing undefince behaviour here. You
either need to keep a count of how many characters you've read or need
to initialise the array and/or populate and look for '\0' characters
at read time.

Given what you're trying to do I would suggest you take a look at the
fgets function.
{
if(!isprint(buffer[count]))
{
buffer[count] = '.';
}
else
{
fputc(buffer[count],outfile);
}


And this lot doesn't need to be in an else, it needs to happen every
time.

<snip rest of code>
 
B

Ben Bacarisse

Bill said:

Well you get much credit for *not* digging in and arguing that your
way of posting is the right way. Thank you.
Yes it is an assignment, but I'm trying to take it further
than what was assigned.
I really wish to learn the language.

A simple solution, which will concentrate on the language features
rather than on getting the searching algorithm right, is to:

(1) Find out how big the file is. There are two ways to do this in
standard C, but only one that is guaranteed to work. If I were your
tutor I would accept both, provided your program reports any failures
as such.

(2) Allocate space for the whole file and read it in.

(3) Search for the string to replace whilst writing out those parts
that are not to be replaced.

There are obvious limitations with this strategy, but it will help you
learn. A fully general search and replace program is quite complex
for a beginner.
 
B

Bill

There are obvious limitations with this strategy, but it will help you
learn. A fully general search and replace program is quite complex
for a beginner.

Thanks for the tips.
 
A

admin

Hello,

I'm trying to output buffer content to a file. I either get an access
violation error, or crazy looking output in the file depending on
which method I use to write the file. Can anyone help out a newbie?

#include <stdio.h>
#include <ctype.h>
#include <string.h>

#define DISPLAY 117 /* Length of display line */
#define PAGE_LENGTH 20 /* Lines per page */

int main(int argc, char *argv[])
{
FILE *pfile;
FILE *outfile;
char *p;

unsigned char buffer[DISPLAY/4 - 1]; /* File input buffer */
int count = 0; /* Count of characters in buffer */
int lines = 0; /* Number of lines displayed */
int i = 0; /* Loop counter */

char string [100];
pfile = fopen ("my.txt" , "r");

while(!feof(pfile)) /* Continue until end of file */
{
if(count < sizeof buffer) /* If the buffer is not full */
/* Read a character */
buffer[count++] = (unsigned char)fgetc(pfile);
else
{
/* Now display buffer contents as characters */
for(count = 0; count < sizeof buffer; count++)
printf("%c", isprint(buffer[count]) ? buffer[count]:'.');
printf("\n"); /* End the line */
count = 0; /* Reset count */

if(!(++lines%PAGE_LENGTH)) /* End of page? */
if(getchar()=='E') /* Wait for Enter */
return 0; /* E pressed */
}
}

/* Display last line as characters */
for(i = 0; i < count; i++)
{
printf("%c",isprint(buffer) ? buffer:'.');
}

printf("\n");
fclose(pfile); /* Close the file */

//re-open file and write out the buffer contents
outfile = fopen ("myOutput.txt" , "w");

if(outfile==NULL)
{
return 0;
}

//causes an access violation error
for(count = 0; count < sizeof buffer; count++)
{
fputc(outfile, isprint(buffer[count]) ? buffer[count]:'.');
}

//causes crazy output in file
/*for(count = 0; count < sizeof buffer; count++)
{
(int) fwrite(buffer, 1, isprint(buffer[count]), outfile);
}*/

fclose(outfile);
getchar();
return 0;



}- -

- -


Dear friend, we sell all kinds of laptops and guitars . our product
is a quantity best, the price is the lowest in the world, i think you
will be interested in our product . thanks a lot! Our Website:http://
www.prs-123.com/
Msn: (e-mail address removed)
mail: (e-mail address removed)
thanks for everyone good luck with everyone .
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Dear friend, we sell all kinds of laptops and guitars . our product
is a quantity best, the price is the lowest in the world, i think you
will be interested in our product . thanks a lot! Our Website:http://
www.prs-123.com/
Msn: (e-mail address removed)
mail: (e-mail address removed)
thanks for everyone good luck with everyone .
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Dear friend, we sell all kinds of laptops and guitars . our product
is a quantity best, the price is the lowest in the world, i think you
will be interested in our product . thanks a lot! Our Website:http://
www.prs-123.com/
Msn: (e-mail address removed)
mail: (e-mail address removed)
thanks for everyone good luck with everyone .
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Dear friend, we sell all kinds of laptops and guitars . our product
is a quantity best, the price is the lowest in the world, i think you
will be interested in our product . thanks a lot! Our Website:http://
www.prs-123.com/
Msn: (e-mail address removed)
mail: (e-mail address removed)
thanks for everyone good luck with everyone .
 
W

Walter Roberson

Ben Bacarisse said:
(1) Find out how big the file is. There are two ways to do this in
standard C, but only one that is guaranteed to work.

Perhaps I'm extracting too far out of context, and you were
only intending that to apply to very specific kinds of files,
or to a very specific meaning of "how big" -- but in the general
case, there is no guaranteed way to find file sizes in C.

If you read the file as a text file, then you don't know how much
(if any) space was used for line delimeters. And if it was really
a binary file and you tried to read it as text, there is the possibility
that some sequence of characters in the file (e.g., control-Z) will
be interpreted as signalling the end-of-file.

If you read the file as a binary file, then (if I recall correctly)
trailing nulls in the file are not certain to be preserved.
 
B

Ben Bacarisse

Perhaps I'm extracting too far out of context, and you were
only intending that to apply to very specific kinds of files,
or to a very specific meaning of "how big"

That was glossed over in my "this has limitations" remark. I wanted
to simply things as much as possible. It looks like the OP is
starting out in C.
-- but in the general
case, there is no guaranteed way to find file sizes in C.

If you read the file as a text file, then you don't know how much
(if any) space was used for line delimeters. And if it was really
a binary file and you tried to read it as text, there is the possibility
that some sequence of characters in the file (e.g., control-Z) will
be interpreted as signalling the end-of-file.

If it is not safe to treat the file as a binary stream, you only have
one option: read it while counting and then either rewind it or close
it and open it again. Of course, if the file changes (or becomes
unreadable for some reason) you have to report that, but it seems like
a reasonable solution for a beginner.
If you read the file as a binary file, then (if I recall correctly)
trailing nulls in the file are not certain to be preserved.

If the file may be treated as a binary file, then any standard C
program will have to read it and write it subject to whatever
limitations the standard imposes, so I don't really see that as a
problem (in this case). I doubt OP can have been expected to
investigate OS-specific extensions to handle files that are not fully
readable by standard C functions!

I would advise treating the file as a binary stream unless the
specification made that problematic (e.g. if one had to deal with
"lines" in some way). If this (opening as a binary file) is possible
then I would consider it reasonable for a student to either seek to
the end or to read to the end and use ftell to see how many characters
are likely to available after a rewind. Of course, all errors should
be reported, and a read shorter than expected should be dealt with
properly, but all of this is simpler (for a beginner) than doing a
general search and replace while doing buffered reads through a stream.

What is "reasonable" could change with even a slight change in
specification. I'd advice a different strategy if the search string
is constrained within "lines", or if the program should be able to
handle huge files, or non-rewindable streams, or...
 
B

Ben Bacarisse

Bill said:
Yes. It has become wacky. What I'm trying to do is read the contents
of a file into a buffer, find a particular string in the buffer, make
an edit in the found string and write the same file back out.

I suggested on way to go in another sub thread, but let me suggest
another which does not suffer from the ugliness of having to read in
the whole file.

You can read the file one character at a time. When that character
read matches the "next" character in your search string, just update a
count of how many matching characters you have seen. If, when you do
this, you get to the end of the target string, you know you have found
a match and you can print the replacement. When you don't find a
match you just reset your count of matched characters.

Looking back at your post, it is quite possible that this is the way
you were trying to do it. It is certainly a better design than my
other suggestion. By the way, there is nothing wrong with writing
both programs to compare them!

This strategy, will allow you to make your program into a filter so it
would be better *not* to open named input and output files. It should
read stdin and write stdout.
 
O

Old Wolf

//causes an access violation error
fputc(outfile, isprint(buffer[count]) ? buffer[count]:'.');

Hint: the prototype for fputc is:
int fputc(int c, FILE *stream);

If you didn't get a compiler warning at least on this
line, you should really turn up your warning level.

If gcc is your compiler then use the switches:
-Wall -Wextra -pedantic

along with either -std=ansi or -std=c99.
//causes crazy output in file
/*for(count = 0; count < sizeof buffer; count++)
{
(int) fwrite(buffer, 1, isprint(buffer[count]), outfile);
}*/

This will fill the file with ones and zeroes
(bytes, not bits!) I suspect you wanted a bit
more substance to the third argument to fwrite.

A couple of other style notes:
printf("%c", isprint(buffer[count]) ? buffer[count]:'.');

putchar(X) seems nicer to me than printf("%c", X).
In fact, you use this ternary expression in several
places in the code so I would like to see it contained
as its own function, e.g.

int ch_printable(int ch) { return isprint(ch) ? ch : '.'; }

and then in your other functions:
putchar( ch_printable( buffer[count] ) );

Another way to go would be to make your function:
int fputc_printable(int ch, FILE *stream);
pfile = fopen ("my.txt" , "r");
while(!feof(pfile))

You should check pfile != NULL immediately after the fopen.
(Others have already explained the problem with feof).
 
P

pete

Yes. It has become wacky. What I'm trying to do is read the contents
of a file into a buffer, find a particular string in the buffer, make
an edit in the found string and write the same file back out.

/* BEGIN one_way.c output */

my.txt open for reading.
my.txt closed.

Original file:
Fred Flinstone 123-456-7890
Barney Rubble 123-789-4561

my.txt open for writing.
my.txt closed.

New file:
Fred Flinstone KL5-456-7890
Barney Rubble KL5-789-4561

/* END one_way.c output */


/* BEGIN one_way.c */

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

#define STRING "123"
#define SUBSTITUE "KL5"

struct list_node {
struct list_node *next;
void *data;
};

unsigned long max_line_len(FILE *fp);
void list_free(struct list_node *node, void (*free_data)(void *));
int list_fputs(struct list_node *node, FILE *stream);
struct list_node *string_node(struct list_node **head,
struct list_node *tail,
char *data);
struct list_node *alter_list(struct list_node *head);
char *alter_line(char *line);

int main(void)
{
int rc;
FILE *fp;
char *line;
char *fn = "my.txt";
unsigned long max_length;
struct list_node *head, *tail;

puts("/* BEGIN one_way.c output */\n");
fp = fopen (fn, "r");
if (fp == NULL) {
puts("fp == NULL");
exit(EXIT_SUCCESS);
}
printf("%s open for reading.\n", fn);
max_length = max_line_len(fp);
if (max_length == -1) {
puts("ferror");
exit(EXIT_FAILURE);
}
line = malloc(max_length + 1);
if (line == NULL) {
printf("max_length is %lu\n", max_length);
exit(EXIT_FAILURE);
}
head = tail = NULL;
do {
rc = fscanf(fp, "%[^\n]", line);
if (!feof(fp)) {
getc(fp);
}
switch (rc) {
case 0:
*line = '\0';
case 1:
tail = string_node(&head, tail, line);
if (tail == NULL) {
puts("tail == NULL");
exit(EXIT_FAILURE);
}
default:
break;
}
} while (rc != EOF);
fclose(fp);
printf("%s closed.\n\n", fn);
puts("Original file:");
list_fputs(head, stdout);
if (alter_list(head) == NULL) {
list_free(head, free);
puts("alter_list(head) == NULL");
exit(EXIT_FAILURE);
}
fp = fopen (fn, "w");
if (fp == NULL) {
puts("fp == NULL2");
exit(EXIT_FAILURE);
}
printf("\n%s open for writing.\n", fn);
list_fputs(head, fp);
fclose(fp);
printf("%s closed.\n\n", fn);
puts("New file:");
list_fputs(head, stdout);
list_free(head, free);
puts("\n/* END one_way.c output */");
return 0;
}

struct list_node *alter_list(struct list_node *head)
{
struct list_node *node = head;
char *p;

while (node != NULL) {
p = alter_line(node -> data);
if (p != NULL) {
node -> data = p;
}
node = node -> next;
}
return head;
}

char *alter_line(char *line)
{
char *p;

p = strstr(line, STRING);
if (p != NULL) {
memcpy(p, SUBSTITUE, strlen(SUBSTITUE));
}
return line;
}

unsigned long max_line_len(FILE *fp)
{
int rc;
unsigned long count, max;

count = max = 0;
while ((rc = getc(fp)) != EOF) {
if (rc == '\n') {
if (count > max) {
max = count;
}
count = 0;
} else {
++count;
}
}
if (ferror(fp)) {
max = 0lu - 1;
}
rewind(fp);
return max;
}

void list_free(struct list_node *node, void (*free_data)(void *))
{
struct list_node *next_node;

while (node != NULL) {
next_node = node -> next;
free_data(node -> data);
free(node);
node = next_node;
}
}

int list_fputs(struct list_node *node, FILE *stream)
{
while (node != NULL) {
if (fputs(node -> data, stream) == EOF) {
return EOF;
}
if (putc('\n', stream) == EOF) {
return EOF;
}
node = node -> next;
}
return '\n';
}

struct list_node *string_node(struct list_node **head,
struct list_node *tail,
char *data)
{
struct list_node *node;

node = malloc(sizeof *node);
if (node != NULL) {
node -> next = NULL;
node -> data = malloc(strlen(data) + 1);
if (node -> data != NULL) {
strcpy(node -> data, data);
if (*head == NULL) {
*head = node;
} else {
tail -> next = node;
}
} else {
free(node);
node = NULL;
}
}
return node;
}

/* END one_way.c */
 
P

pete

/* BEGIN one_way.c output */

/* BEGIN another_way.c output */

my.txt open for reading.
my.txt closed.

Original file:
Fred Flinstone 123-456-7890
Barney Rubble 123-789-4561

my.txt open for writing.
my.txt closed.

New file:
Fred Flinstone KL5-456-7890
Barney Rubble KL5-789-4561

/* END another_way.c output */


/* BEGIN another_way.c */

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

#define STRING "123"
#define SUBSTITUE "KL5"

struct list_node {
struct list_node *next;
void *data;
};

int get_line(char **lineptr, size_t *n, FILE *stream);
void list_free(struct list_node *node, void (*free_data)(void *));
int list_fputs(struct list_node *node, FILE *stream);
struct list_node *string_node(struct list_node **head,
struct list_node *tail,
char *data);
struct list_node *alter_list(struct list_node *head);
char *alter_line(char *line);

int main(void)
{
int rc;
size_t size;
FILE *fp;
char *line;
char *fn = "my.txt";
struct list_node *head, *tail;

puts("/* BEGIN another_way.c output */\n");
fp = fopen (fn, "r");
if (fp == NULL) {
puts("fp == NULL");
exit(EXIT_SUCCESS);
}
printf("%s open for reading.\n", fn);
line = NULL;
size = 0;
head = tail = NULL;
for (;;) {
rc = get_line(&line, &size, fp);
if (rc > 0) {
tail = string_node(&head, tail, line);
if (tail == NULL) {
puts("tail = NULL");
exit(EXIT_FAILURE);
}
} else {
break;
}
}
free(line);
line = NULL;
size = 0;
fclose(fp);
printf("%s closed.\n\n", fn);
puts("Original file:");
list_fputs(head, stdout);
if (alter_list(head) == NULL) {
list_free(head, free);
puts("alter_list(head) == NULL");
exit(EXIT_FAILURE);
}
fp = fopen (fn, "w");
if (fp == NULL) {
puts("fp == NULL2");
exit(EXIT_FAILURE);
}
printf("\n%s open for writing.\n", fn);
list_fputs(head, fp);
fclose(fp);
printf("%s closed.\n\n", fn);
puts("New file:");
list_fputs(head, stdout);
list_free(head, free);
puts("\n/* END another_way.c output */");
return 0;
}

struct list_node *alter_list(struct list_node *head)
{
struct list_node *node = head;
char *p;

while (node != NULL) {
p = alter_line(node -> data);
if (p != NULL) {
node -> data = p;
}
node = node -> next;
}
return head;
}

char *alter_line(char *line)
{
char *p;

p = strstr(line, STRING);
if (p != NULL) {
memcpy(p, SUBSTITUE, strlen(SUBSTITUE));
}
return line;
}

void list_free(struct list_node *node, void (*free_data)(void *))
{
struct list_node *next_node;

while (node != NULL) {
next_node = node -> next;
free_data(node -> data);
free(node);
node = next_node;
}
}

int list_fputs(struct list_node *node, FILE *stream)
{
while (node != NULL) {
if (fputs(node -> data, stream) == EOF) {
return EOF;
}
if (putc('\n', stream) == EOF) {
return EOF;
}
node = node -> next;
}
return '\n';
}

struct list_node *string_node(struct list_node **head,
struct list_node *tail,
char *data)
{
struct list_node *node;

node = malloc(sizeof *node);
if (node != NULL) {
node -> next = NULL;
node -> data = malloc(strlen(data) + 1);
if (node -> data != NULL) {
strcpy(node -> data, data);
if (*head == NULL) {
*head = node;
} else {
tail -> next = node;
}
} else {
free(node);
node = NULL;
}
}
return node;
}

int get_line(char **lineptr, size_t *n, FILE *stream)
{
int rc;
void *p;
size_t count;

count = 0;
while ((rc = getc(stream)) != EOF) {
if (count != -1) {
++count;
}
if (count + 2 > *n) {
p = realloc(*lineptr, count + 2);
if (p == NULL) {
if (*n > count) {
if (rc != '\n') {
(*lineptr)[count] = '\0';
(*lineptr)[count - 1] = (char)rc;
} else {
(*lineptr)[count - 1] = '\0';
}
} else {
if (*n != 0) {
**lineptr = '\0';
}
ungetc(rc, stream);
}
count = 0;
break;
}
*lineptr = p;
*n = count + 2;
}
if (rc == '\n') {
(*lineptr)[count - 1] = '\0';
break;
}
(*lineptr)[count - 1] = (char)rc;
}
if (rc != EOF) {
rc = count > INT_MAX ? INT_MAX : count;
} else {
if (*n > count) {
(*lineptr)[count] = '\0';
}
}
return rc;
}

/* END another_way.c */
 
K

Keith Thompson

Old Wolf said:
//causes crazy output in file
/*for(count = 0; count < sizeof buffer; count++)
{
(int) fwrite(buffer, 1, isprint(buffer[count]), outfile);
}*/

This will fill the file with ones and zeroes
(bytes, not bits!) I suspect you wanted a bit
more substance to the third argument to fwrite.

Not necessarily. isprint() returns 0 if its argument is not a
printing character, but it can return any non-zero (i.e., true) value
if it is.

If you really want a 0 or 1 value for some reason, you can use
isprint(buffer[count]) ? 1 : 0
or
!!isprint(buffer[count])

And what the heck is the point of casting the result of fwrite() to
int before discarding it? fwrite() returns a size_t result. If you
want to ignore the result, no cast is needed (though you can cast to
void if you want to be explicit). But you should always check the
result anyway.

[snip]
 
O

Old Wolf

//causes crazy output in file
/*for(count = 0; count < sizeof buffer; count++)
{
(int) fwrite(buffer, 1, isprint(buffer[count]), outfile);
}*/

Not necessarily. isprint() returns 0 if its argument is not a
printing character, but it can return any non-zero (i.e., true) value
if it is.

So the above code could write any amount of
extra garbage into the file, as well as the
desired output.
And what the heck is the point of casting the result of fwrite() to
int before discarding it? fwrite() returns a size_t result. If you
want to ignore the result, no cast is needed (though you can cast to
void if you want to be explicit). But you should always check the
result anyway.

I suppose he's using a lint tool that warns about
unused return values. Although he does seem to
have a penchant for useless casts.
 
K

Keith Thompson

Old Wolf said:
//causes crazy output in file
/*for(count = 0; count < sizeof buffer; count++)
{
(int) fwrite(buffer, 1, isprint(buffer[count]), outfile);
}*/

Not necessarily. isprint() returns 0 if its argument is not a
printing character, but it can return any non-zero (i.e., true) value
if it is.

So the above code could write any amount of
extra garbage into the file, as well as the
desired output.

Whoops, I was paying attention to the value of the expression but not
to how it's being used. Yes, since the 3rd argument to fwrite is the
number of elements to write (and the 2nd is the size in bytes of each
element), it will *attempt* to write some arbirary amount of data to
the file. It will attempt to read that data from buffer, which very
likely isn't big enough, causing undefined behavior. That's probably
the cause of the "crazy output".

I was going to say that this will usually work, since isprint() in
practice will probably return either 0 or 1, but that's not the case.
In one implementation I just tried, it consistently returns 0 or
16384. I think this is a result of the table lookup method used
internally by the implementation <OT>glibc</OT>.

Even if isprint() always returned 0 or 1, passing 0 or 1 as the third
argument to fwrite is a rather odd technique. If you don't want to
write anything it probably makes more sense just not to call fwrite.

[snip]
 

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,059
Latest member
cryptoseoagencies

Latest Threads

Top