I/O with fread and fwrite - c

Below is my simple cat problem, which reads a file and print it on terminal. When I set the BUFISZE macro to 10, it works fine. If I set BUFSIZE to 100, it prints part of the file. If I set BUFIZE to 1024, it prints nothing. Could you anyone please explain what is going on?
#include <stdio.h>
#include <string.h>
#define BUFSIZE 10
int main(int argc, char **argv){
char buf[BUFSIZE];
FILE *fp;
if( (fp = fopen(*++argv, "r")) == NULL){
printf("cannot open %s\n", *argv);
return 1;
}
while( fread(buf, BUFSIZE, 1, fp) == 1 )
if (fwrite(buf,strlen(buf), 1, stdout) != 1 ){
printf("write error.\n");
return 2;
}
printf("\n");
return 0;
}

Don't use strlen here; you are not dealing with null-terminated strings here. You read fixed blocksizes. You should write the same amount of chars that you have read.
fread returns the number of elements of the given size that were successfully read. Use this information in your call to fread. For this to work, you must treat the data as BUFSIZE bytes, not as one block of BUFSIZE bytes. (If that sounds esoteric: Swap your second and third parameters in fread and fwrite. These functions cannot return a number greater than their third parameter, the element count.)
So:
char buf[BUFSIZE];
size_t n;
do {
n = fread(buf, 1, BUFSIZE, stdin);
if (n > 0) fwrite(buf, 1, n, stdout);
} while (n == BUFSIZE);

Related

Using C popen(): read() work, but fread() doesn't

After popen(), fread() always returns 0. Using read() with fileno(fp) works. What's going on?
Here is the code.
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
FILE *fp = popen("echo hello", "r");
if (!fp) {
perror("echo hello");
} else {
char buffer[128];
for (;;) {
int n;
if (argc < 2) {
n = fread(buffer, sizeof buffer, 1, fp);
} else {
n = read(fileno(fp), buffer, sizeof buffer);
}
printf("read %d bytes\n", n);
if (n <= 0) break;
fwrite(buffer, n, 1, stdout);
}
pclose(fp);
}
}
The code uses fread() if there is no command line argument, read() otherwise.
Output:
$ ./test
read 0 bytes
$ ./test x
read 6 bytes
hello
read 0 bytes
You told fread() to read 1 item 100 bytes long. fread() returns the number of complete items that were read. Since the stream only has 6 bytes in it, it can't read any items before reaching EOF, so it returns 0.
Swap the order of the size and nitem arguments, then it will treat each byte as a separate item, and return the number of bytes that it read.
n = fread(buffer, 1, sizeof buffer, fp);

Cannot Copy a Text File through fwrite and fread in C [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
Here is the Code:
#include <stdio.h>
#include <stdlib.h>
int main() {
FILE *f, *fp;
char buff[512];
int buff_size;
int bytes;
fp = fopen("File.txt", "rb");
if (fp == NULL) {
printf("Cannot Open Source File!\n");
exit(1);
}
f = fopen("append.txt", "ab+");
if (f == NULL) {
printf("Cannot Open Target File!\n");
fclose(fp);
exit(1);
}
buff_size = sizeof(buff);
while (bytes = fread(&buff, buff_size, 1, fp) > 0) {
if (bytes > 0)
fwrite(&buff, buff_size, 1, f);
else
break;
printf("Appending...\n\n");
}
rewind(f);
while (bytes = fread(&buff, buff_size, 1, f) > 0)
if (bytes > 0)
printf("%s", buff);
fclose(fp);
fclose(f);
}
So, it happens to be that this doesn't output anything and when I check the file "append.txt" it also does not contain anything.
Note that the Source File "File.txt" is not empty.
Can anyone tell me what is wrong with it?
EDIT:
I fixed the problem by replacing buff_size with strlen(buff) as this:
bytes = fread(&buff, strlen(buff), 1, f) > 0 and the same in fwrite() and second fread().
Can someone explain why this worked?
char buff[512];
int buff_size;
// [...]
bytes = fread(&buff, buff_size, 1, fp)
This will attempt to read one block of 512 bytes. The return value is the number of blocks read, so it won't be bytes. But leaving this aside, if your file is shorter than 512 bytes, this won't read anything.
What you want is read 512 times 1 byte, then you will get the byte count back, so swap places for buff_size and 1.
Side notes:
if you do your checks in the loop condition correctly like:
while ((bytes = fread(buff, 1, buff_size, fp)) > 0 )
the extra check for if (bytes > 0) is redundant.
when writing, you only want to write the amount of bytes you actually read:
fwrite(buff, 1, bytes, f);
For sizes, always use size_t -- int could very well be wrong:
size_t buff_size;
size_t bytes;
printing your buff with printf("%s", ) is undefined behavior because you don't add a '\0' byte after the data read by fread(). A C string must end with '\0'. When the data read by fread() doesn't contain a '\0' by accident, printf() will read and use uninitialized data and possibly even read beyond the bounds of your buff.
Felix Palmen listed a number of problems in your code, your fix is completely wrong as buff does not even have a null terminator.
Here is a better version:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
FILE *f, *fp;
char buff[512];
size_t bytes;
fp = fopen("File.txt", "rb");
if (fp == NULL) {
fprintf(stderr, "Cannot open source file!\n");
exit(1);
}
f = fopen("append.txt", "ab+");
if (f == NULL) {
fprintf(stderr, "Cannot open target file!\n");
fclose(fp);
exit(1);
}
while ((bytes = fread(buff, 1, sizeof buff, fp)) != 0) {
if (fwrite(buff, 1, bytes, 1, f) != bytes) {
fprintf(stderr, "Error writing to the target file\n");
break;
}
printf("Appending...\n\n");
}
rewind(f);
while ((bytes = fread(buff, 1, sizeof buff, f)) != 0) {
printf("%.*s", (int)bytes, buff);
}
fclose(fp);
fclose(f);
return 0;
}
bytes = fread(&buff, buff_size, 1, fp) > 0
Take a look at the C Precedence Chart. The operator > is above =, so the return value of fread will be compared to zero, and then the result of that comparison will be stored in bytes. You intended to write this
(bytes = fread(&buff, buff_size, 1, fp)) > 0
Here's my solution to your problem, given fixed file names. Were it my own code, it would take the file names as arguments.
#include <stdio.h>
int main(void)
{
const char src_file[] = "File.txt";
FILE *fp = fopen(src_file, "rb");
if (fp == NULL)
{
fprintf(stderr, "Cannot Open Source File '%s'!\n", src_file);
return(1);
}
const char tgt_file[] = "append.txt";
FILE *f = fopen(tgt_file, "ab+");
if (f == NULL)
{
fprintf(stderr, "Cannot Open Target File '%s'!\n", tgt_file);
fclose(fp);
return(1);
}
char buff[512];
int bytes;
while ((bytes = fread(buff, sizeof(char), sizeof(buff), fp)) > 0)
{
fwrite(buff, sizeof(char), bytes, f);
printf("Appending...\n\n");
}
rewind(f);
while ((bytes = fread(buff, sizeof(char), sizeof(buff), f)) > 0)
printf("%.*s", bytes, buff);
fclose(fp);
fclose(f);
return 0;
}
There are a variety of fixes, most of them articulated in comments somewhere along the line.
I made the file names into arrays so that the name could be used in both fopen() and in the error messages, which are printed to stderr, not stdout. This is helpful to other users in generalized code. If the file names come from command line arguments, it is trivial.
The calls to fread() were fixed so that the number of bytes is reported, rather than the number of 512-byte blocks (which will be 0 or 1). This involved reversing the order of the size/count arguments to fread(). The buffer was passed rather than the address of the buffer, too.
The number of bytes read was captured correctly.
The number of bytes read was used to control the size of the fwrite().
The number of bytes read was used to control the number of bytes printed by printf().
I don't like the special rule in C99 and later that allows main() — but only main() — to return 0 by default. AFAIAC, it's a function defined as returning an int; it should return an int. However, there are others who disagree.

C read file and print buffer

I am learning C and I have been trying to read a file and print what I just read. I open the file and need to call another function to read and return the sentence that was just read.
My function will return 1 if everything went fine or 0 otherwise.
I have been trying to make it work for a while but I really dont get why I cant manage to give line its value. In the main, it always prints (null).
The structure of the project has to stay the same, and I absolutely have to use open and read. Not fopen, or anything else...
If someone can explain it to me that would be awesome.
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define BUFF_SIZE 50
int read_buff_size(int const fd, char **line)
{
char buf[BUFF_SIZE];
int a;
a = read(fd, buf, BUFF_SIZE);
buf[a] = '\0';
*line = strdup(buf);
return (1);
}
int main(int ac, char **av)
{
char *line;
int fd;
if (ac != 2)
{
printf("error");
return (0);
}
else
{
if((fd = open(av[1], O_RDONLY)) == -1)
{
printf("error");
return (0);
}
else
{
if (read_buff_size(fd, &line))
printf("%s\n", line);
}
close(fd);
}
}
Here:
char buf[BUFF_SIZE];
int a;
a = read(fd, buf, BUFF_SIZE);
buf[a] = '\0';
if there are more characters than BUFF_SIZE available to be read, then you will fill your array entirely, and buf[a] will be past the end of your array. You should either increase the size of buf by one character:
char buf[BUFF_SIZE + 1];
or, more logically given your macro name, read one fewer characters:
a = read(fd, buf, BUFF_SIZE - 1);
You should also check the returns from strdup() and read() for errors, as they can both fail.
read(fd, buf, BUFF_SIZE); //UB if string is same or longer as BUFF_SIZE
u need +1 byte to store 0, so use BUFF_SIZE - 1 on reading or +1 on array allocation...also you should check all returned values and if something failed - return 0
Keep it simple and take a look at:
https://github.com/mantovani/apue/blob/c47b4b1539d098c153edde8ff6400b8272acb709/mycat/mycat.c
(Archive form straight from the source: http://www.kohala.com/start/apue.tar.Z)
#define BUFFSIZE 8192
int main(void){
int n;
char buf[BUFFSIZE];
while ( (n = read(STDIN_FILENO, buf, BUFFSIZE)) > 0)
if (write(STDOUT_FILENO, buf, n) != n)
err_sys("write error");
if (n < 0)
err_sys("read error");
exit(0);
}
No need to use the heap (strdup). Just write your buffer to STDOUT_FILENO (=1) for as long as read returns a value that's greater than 0. If you end with read returning 0, the whole file has been read.

Posix call 'read' does not read entire file

While programming with files I stumbled upon some strange difference between the C library 'fread' function and the POSIX call 'read'; 'read' only reads a few bytes of a file while 'fread' reads the whole file.
This code only reads 1024 + 331 bytes, and then 'read' returns 0:
char buf[1024];
int id = open("file.ext", 0);
int len;
while((len = read(id, buf, 1024)) > 0)
println(len);
while this code reads the whole file as expected, around 11kb:
char buf[1024];
FILE* fp = fopen("file.ext", "rb");
int len;
while((len = fread(buf, 1, 1024, fp)) > 0)
println(len);
Can you tell why 'read' doesn't read the whole file?
EDIT2: I am sorry, I am using windows with MinGW, and reading a binary file
EDIT: A complete example:
#include <io.h>
#include <stdio.h>
int main() {
char buf[1024];
int len;
// loop 1
int id = open("file.ext", 0);
while((len = read(id, buf, 1024)) > 0) {
printf("%d\n", len);
}
close(id);
println("--------");
// loop 2
FILE* fp = fopen("file.ext", "rb");
while((len = fread(buf, 1, 1024, fp)) > 0) {
printf("%d\n", len);
}
fclose(fp);
while(1) {}
return 0;
}
The output:
1024
331
--------
1024
1024
1024
1024
1024
1024
1024
1024
1024
1024
981
You're opening the file the first time in text mode and the second time in binary mode. You need to open it both times in binary mode. If it's not in binary mode, the first control-z (hex value 1A) signals the "end of file".
Add the following includes (getting rid of <io.h>):
#include <unistd.h>
#include <fcntl.h>
The call open like this:
int id = open("spiderman.torrent", O_RDONLY|O_BINARY);
Here's an example of control-z ending the file:
#include <stdio.h>
void writeit() {
FILE *f = fopen("test.txt", "wb");
fprintf(f, "hello world\r\n");
fputc(0x1A, f);
fprintf(f, "goodbye universe\r\n");
fclose(f);
}
void readit() {
int c;
FILE *f = fopen("test.txt", "r");
while ((c = fgetc(f)) != EOF)
putchar(c);
fclose(f);
}
int main() {
writeit();
readit();
return 0;
}
The above only prints "hello world" and not "goodbye universe".
The question was updated...
The fread() loop is weird:
while ((len = fread(buf, 1, 1024, fp) > 0))
println(len);
Look at the parentheses — they're equivalent to:
while ((len = (fread(buf, 1, 1024, fp) > 0) ))
Now, fread() will return the number of bytes read, but the value assigned to len will be 0 or 1, so the printing from println() should repeat 1 a few times and then stop.
Is that you're actual code, or did you make a typing error in creating the question?
Compile and run this program (I called it rd compiled from rd.c):
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#define FILENAME "file.ext"
static void println(int val)
{
printf("%d\n", val);
}
int main(void)
{
char buf[1024];
int len;
int id = open(FILENAME, 0);
while ((len = read(id, buf, 1024)) > 0)
println(len);
close(id);
FILE *fp = fopen(FILENAME, "rb");
while ((len = fread(buf, 1, 1024, fp)) > 0)
println(len);
fclose(fp);
struct stat sb;
stat(FILENAME, &sb);
printf("Size: %d\n", (int)sb.st_size);
return 0;
}
Example output:
$ ls -l file.ext
-rw-r--r-- 1 jleffler staff 7305 Apr 6 08:08 file.ext
$ ./rd
1024
1024
1024
1024
1024
1024
1024
137
1024
1024
1024
1024
1024
1024
1024
137
Size: 7305
$

lseek() returning 0 when followed by new open()

I have the following bit of code (it's "example" code, so nothing fancy):
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
char buffer[9];
int fp = open("test.txt", O_RDONLY);
if (fp != -1) // If file opened successfully
{
off_t offset = lseek(fp, 2, SEEK_SET); // Seek from start of file
ssize_t count = read(fp, buffer, strlen(buffer));
if (count > 0) // No errors (-1) and at least one byte (not 0) was read
{
printf("Read test.txt %d characters from start: %s\n", offset, buffer);
}
close(fp);
}
int fp2 = open("test.txt", O_WRONLY);
if (fp2 != -1)
{
off_t offset = lseek(fp2, 2, SEEK_CUR); // Seek fraom current position (0) - same result as above in this case
ssize_t count = write(fp2, buffer, strlen(buffer));
if (count == strlen(buffer)) // We successfully wrote all the bytes
{
printf("Wrote to test.txt %d characters from current (0): %s\n", offset, buffer);
}
close(fp2);
}
}
This code does not return the first printout (reading) as it is, and the second printout reads: "Wrote test.txt 0 characters from current (0): " indicating that it did not seek anywhere in the file and that buffer is empty.
The odd thing is, if I comment out everything from fp2 = open("test.txt", O_WRONLY);, the first printout returns what I expected. As soon as I include the second open statement (even with nothing else) it won't write it. Does it somehow re-order the open statements or something else?
The line
ssize_t count = read(fp, buffer, strlen(buffer));
is wrong, you're taking the strlen of an uninitialized buffer. You likely want the size of the buffer like so:
ssize_t count = read(fp, buffer, sizeof buffer);
You should make sure buffer really contain a nul terminated string as well when you print it as one.
if (fp != -1) // If file opened successfully
{
off_t offset = lseek(fp, 2, SEEK_SET); // Seek from start of file
ssize_t count = read(fp, buffer, sizeof buffer - 1);
if (count > 0) // No errors (-1) and at least one byte (not 0) was read
{
buffer[count] = 0;
Are you perfectly sure you are cleaning out the file every time you run?
As written, the first time you run this, you'll only see the second printout, and the second time you might see the first one.

Resources