Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the most efficient way to print a file in C to stdout?

Tags:

c

file

I'm stuck on this. Currently I'm using:

FILE *a = fopen("sample.txt", "r");
int n;
while ((n = fgetc(a)) != EOF) {
  putchar(n);
}

However this method seems to be a bit inefficient. Is there any better way? I tried using fgets:

char *s;
fgets(s, 600, a);
puts(s);

There's one thing I find wrong about this second method, which is that you would need a really large number for the second argument of fgets.

Thanks for all the suggestions. I found a way (someone on IRC told me this) using open(), read(), and write().

char *filename = "sample.txt";
char buf[8192];
int r = -1;
int in = open(filename, O_RDONLY), out = 0;
if (in == -1)
  return -1;
while (1) {
  r = read(in, buf, sizeof(buf));
  if (r == -1 || r == 0) { break; }
  r = write(out, buf, r);
  if (r == -1 || r == 0) { break; }
}
like image 854
Spreadsheet Avatar asked Jun 27 '10 00:06

Spreadsheet


3 Answers

The second code is broken. You need to allocate a buffer, e.g.:

char s[4096];
fgets(s, sizeof(s), a);

Of course, this doesn't solve your problem.

Read fix-size chunks from the input and write out whatever gets read in:

int n;
char s[65536];
while ((n = fread(s, 1, sizeof(s), a))) {
    fwrite(s, 1, n, stdout);
}

You might also want to check ferror(a) in case it stopped for some other reason than reaching EOF.

Notes

  1. I originally used a 4096 byte buffer because it is a fairly common page size for memory allocation and block size for the file system. However, the sweet-spot on my Linux system seems to be around the 64 kB mark, which surprised me. Perhaps CPU cache is a factor here, but I'm just guessing.
  2. For a cold cache, it makes almost no difference, since I/O paging will dominate; even one byte at a time runs at about the same speed.
like image 192
Marcelo Cantos Avatar answered Sep 24 '22 09:09

Marcelo Cantos


The most efficient method will depend greatly on the operating system. For example, in Linux, you can use sendfile:

struct stat buf;
int fd = open(filename, O_RDONLY);
fstat(fd, &buf);
sendfile(0, fd, NULL, buf.st_size);

This does the copy directly in the kernel, minimizing unnecessary memory-to-memory copies. Other platforms may have similar approaches, such as write()ing to stdout from a mmaped buffer.

like image 45
bdonlan Avatar answered Sep 21 '22 09:09

bdonlan


I believe the FILE returned by fopen is tipically (always?) buffered, so you first example is not so inefficient as you may think.

The second might perform a little better... if you correct the errors: remember to allocate the buffer, and remember that puts add a newline!.

Other option is to use binary reads (fread).

like image 45
leonbloy Avatar answered Sep 20 '22 09:09

leonbloy