使用POSIX API读取文件

时间:2012-11-10 13:05:21

标签: c linux string file-io posix

考虑以下用于将文件内容读入缓冲区的代码

#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;
}

代码说明:

  • 输入文件包含字符串“Hello”
  • 此内容需要复制到缓冲区中。
  • 目标由openread POSIX API实现。
  • 读取API使用指向* 任意大小 *的缓冲区的指针来复制数据。

问题:

  • 动态分配是必须用于优化缓冲区大小的方法。从输入文件大小关联/派生缓冲区大小的正确过程是什么?
  • 我在read操作结束时看到,除了字符“Hello”之外,读取还复制了new line characterNULL字符。请详细说明这种阅读行为。

示例输出

  
    

bytes_read缓存= 6

         

的buff [0] = H

         

的buff [1] = E

         

的buff [2] = 1

         

的buff [3] = 1

         

的buff [4] = O

         

的buff [5] =

  

PS:输入文件是用户创建的文件,不是由程序创建的(使用write API)。在这里提一下,如果它有任何区别。

4 个答案:

答案 0 :(得分:6)

由于您想要读取整个文件,最好的方法是使缓冲区与文件大小一样大。在你去的时候调整缓冲区是没有意义的。这只会在没有充分理由的情况下伤害表现。

您可以通过多种方式获取文件大小。快速而肮脏的方式是lseek()到文件的末尾:

// 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);

另一种方法是使用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);

要获取所有需要的类型和函数原型,请确保包含所需的标题:

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

请注意,read()不会使用\0自动终止数据。您需要手动执行此操作,这就是我们为缓冲区分配额外字符(大小+ 1)的原因。在你的案例中已经存在\0字符的原因是纯随机机会。

当然,由于buf现在是一个动态分配的数组,所以当你不再需要时,不要忘记再次释放它:

free(buff);

请注意,分配一个与您想要读入的文件一样大的缓冲区可能会很危险。想象一下,如果(错误或故意,无所谓)文件是几GB大。对于这样的情况,最好允许最大允许尺寸。但是,如果您不想要任何此类限制,则应切换到另一种从文件中读取的方法:mmap()。使用mmap(),您可以将文件的某些部分映射到内存。这样,文件的大小并不重要,因为您一次只能处理部分文件,从而可以控制内存使用情况。

答案 1 :(得分:3)

1,您可以使用stat(filename,&amp; stat)获取文件大小,但是将页面大小定义为缓冲区

2,首先,在“Hello”之后没有NULL字符,在执行代码之前,你所分配的堆栈区域必须是0,请参考APUE第7.6章。实际上,在使用它之前必须初始化局部变量。

我尝试使用vim,emacs和echo -n生成文本文件Hello&gt; file-to-buff.txt,只有vim自动添加换行符

答案 2 :(得分:2)

您可以考虑动态分配缓冲区,方法是首先使用malloc创建固定大小的缓冲区,并在填充时加倍(使用realloc)大小。这将有很好的时间复杂性和空间权衡。

此刻你反复读入同一个缓冲区。您应该在每次读取后增加缓冲区中的点,否则您将使用文件的下一部分覆盖缓冲区内容。

您提供的代码为缓冲区分配50个字节,但是将大小传递4096作为read。这可能导致超过50个字节的任何文件的缓冲区溢出。

至于'\ n'和'\ 0'。换行符可能在文件中,'\ 0'就在缓冲区中。缓冲区在代码中的堆栈上分配,如果堆栈的那个部分尚未使用,它可能包含零,在程序加载时由操作系统放置在那里。

操作系统不会尝试终止从文件读取的数据,它可能是二进制数据,也可能是不理解的字符集。如果需要,终止字符串取决于你。

其他几点更多的是风格问题:

  • 您可以考虑使用for (i = 0; buff[i]; ++i)循环而不是一段时间来结束打印输出。这样,如果有人混淆索引变量i,你将不会受到影响。
  • 您可以在读完文件后提前关闭该文件,以避免文件长时间打开(如果发生某种错误,可能会忘记关闭它)。

答案 3 :(得分:1)

对于第二个问题,read不会自动添加字符'\0'。 如果您认为您的文件是文本文件,则必须在调用'\0'后添加read,以表明字符串的结尾。

在C中,字符串的结尾由此字符表示。如果read设置了4个字符,printf将会读取这4个字符,并会测试第5个字符:如果它不是'\0',则会继续打印直到下一个'\0'。 它也是缓冲区溢出的来源

对于'\n',它可能在输入文件中。