Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

File read using POSIX API's

Consider the following piece of code for reading the contents of the file into a buffer

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define BLOCK_SIZE 4096

int main()
{
   int fd=-1;
   ssize_t bytes_read=-1;
   int i=0;
   char buff[50];
   //Arbitary size for the buffer?? How to optimise.
   //Dynamic allocation is a choice but what is the
   //right way to relate the file size to bufffer size.

   fd=open("./file-to-buff.txt",O_RDONLY);
   if(-1 == fd)
   {
      perror("Open Failed");
      return 1;
   }

   while((bytes_read=read(fd,buff,BLOCK_SIZE))>0)
   {
      printf("bytes_read=%d\n",bytes_read);
   }

   //Test to characters read from the file to buffer.The file contains "Hello"
   while(buff[i]!='\0')
   {
      printf("buff[%d]=%d\n",i,buff[i]);
      i++;
      //buff[5]=\n-How?
   }
   //buff[6]=`\0`-How?
   close(fd);
   return 0;
}

Code Description:

  • The input file contains a string "Hello"
  • This content needs to be copied into the buffer.
  • The objective is acheived by open and read POSIX API's.
  • The read API uses a pointer to a buffer of an*arbitary size* to copy the data in.

Questions:

  • Dynamic allocation is the method that must be used to optimize the size of the buffer.What is the right procedure to relate/derive the buffer size from the input file size?
  • I see at the end of the read operation the read has copied a new line character and a NULL character in addition to the characters "Hello". Please elaborate more on this behavior of read.

Sample Output

bytes_read=6

buff[0]=H

buff[1]=e

buff[2]=l

buff[3]=l

buff[4]=o

buff[5]=

PS: Input file is user created file not created by a program (using write API). Just to mention here, in case if it makes any difference.

like image 333
Vivek Maran Avatar asked Nov 10 '12 13:11

Vivek Maran


2 Answers

Since you want to read the whole file, the best way is to make the buffer as big as the file size. There's no point in resizing the buffer as you go. That just hurts performance without good reason.

You can get the file size in several ways. The quick-and-dirty way is to lseek() to the end of the file:

// Get size.
off_t size = lseek(fd, 0, SEEK_END); // You should check for an error return in real code
// Seek back to the beginning.
lseek(fd, 0, SEEK_SET);
// Allocate enough to hold the whole contents plus a '\0' char.
char *buff = malloc(size + 1);

The other way is to get the information using fstat():

struct stat fileStat;
fstat(fd, &fileStat); // Don't forget to check for an error return in real code
// Allocate enough to hold the whole contents plus a '\0' char.
char *buff = malloc(fileStat.st_size + 1);

To get all the needed types and function prototypes, make sure you include the needed header:

#include <sys/stat.h> // For fstat()
#include <unistd.h>   // For lseek()

Note that read() does not automatically terminate the data with \0. You need to do that manually, which is why we allocate an extra character (size+1) for the buffer. The reason why there's already a \0 character there in your case is pure random chance.

Of course, since buf is now a dynamically allocated array, don't forget to free it again when you don't need it anymore:

free(buff);

Be aware though, that allocating a buffer that's as large as the file you want to read into it can be dangerous. Imagine if (by mistake or on purpose, doesn't matter) the file is several GB big. For cases like this, it's good to have a maximum allowable size in place. If you don't want any such limitations, however, then you should switch to another method of reading from files: mmap(). With mmap(), you can map parts of a file to memory. That way, it doesn't matter how big the file is, since you can work only on parts of it at a time, keeping memory usage under control.

like image 197
Nikos C. Avatar answered Oct 06 '22 08:10

Nikos C.


1, you can get the file size with stat(filename, &stat), but define the buffer to page size is just fine

2, first, there is no NULL character after "Hello", it must be accident that the stack area you allocated was 0 before your code executed, please refer to APUE chapter 7.6. In fact you must initialize the local variable before using it.

I tried to generate the text file with vim, emacs and echo -n Hello > file-to-buff.txt, only vim adds a line break automatically

like image 22
TrueWill Avatar answered Oct 06 '22 06:10

TrueWill