使用read函数读取大于4GB的文件在C语言中会出现问题

9

我需要编写C代码来读取大文件。以下是代码:

int read_from_file_open(char *filename,long size)
{
    long read1=0;
    int result=1;
    int fd;
    int check=0;
    long *buffer=(long*) malloc(size * sizeof(int));
    fd = open(filename, O_RDONLY|O_LARGEFILE);
    if (fd == -1)
    {
       printf("\nFile Open Unsuccessful\n");
       exit (0);;
    }
    long chunk=0;
    lseek(fd,0,SEEK_SET);
    printf("\nCurrent Position%d\n",lseek(fd,size,SEEK_SET));
    while ( chunk < size )
    {
        printf ("the size of chunk read is  %d\n",chunk);
        if ( read(fd,buffer,1048576) == -1 )
        {
            result=0;
        }
        if (result == 0)
        {
            printf("\nRead Unsuccessful\n");
            close(fd);
            return(result);
        }

        chunk=chunk+1048576;
        lseek(fd,chunk,SEEK_SET);
        free(buffer);
    }

    printf("\nRead Successful\n");

    close(fd);
    return(result);
}

我在这里遇到的问题是,只要传递的参数(size参数)小于264000000字节,它似乎能够读取。每个周期都会增加chunk变量的大小。
当我传递264000000字节或更多时,读取失败,即:根据检查所用的内容,read返回-1。
有人能指出为什么会发生这种情况吗?我是在正常模式下使用cc编译的,而不是使用DD64。

可能是 https://dev59.com/IFDTa4cB1Zd3GeqPMdEh 的重复问题。 - CyberDem0n
1
我看到你的代码有一些严重的问题。首先,你分配了一个缓冲区来读取,使用size作为大小。然后,你读取缓冲区中的信息,使用固定大小而不考虑读入缓冲区的分配大小。想想如果size小于250k会发生什么。其次,由于文件是新打开的,所以你不需要寻找开头。第三,你然后寻找size,但是size是文件中的记录数还是字节数?在分配缓冲区时,你使用了不同的size - Some programmer dude
第四,您在循环内释放缓冲区,但没有分配新的缓冲区。这意味着在第二次迭代中,它将读取未分配的内存。第五,调用read将自动推进文件中的位置,您不需要每次都进行搜索。第六,在读取时,您检查错误,但不检查文件结束。read在文件结束时返回0 - Some programmer dude
2
最后,当读取大文件时,你不应该使用 read。使用例如 mmap 的解决方案可能更为有效,特别是当你按顺序读取文件时。 - Some programmer dude
1
同意使用mmap来读取大文件大小的建议。此外,考虑增加您的分页或交换内存大小,以避免内存分配错误。 - askmish
3个回答

13
首先,为什么你需要在循环中使用lseek()read()将通过读取的字节数推进文件中的光标。
至于主题:long和chunk分别具有最大值2147483647,任何大于该值的数字实际上都会变成负数。
你应该使用off_t来声明chunk:off_t chunk,并将size声明为size_t。这是lseek()失败的主要原因。
另外,正如其他人已经注意到的那样,你不想在循环内部free()你的缓冲区。
还要注意,你将覆盖已经读取的数据。此外,read()不一定会读取与你要求的字节数相同的字节数,因此最好按实际读取的字节数推进chunk,而不是按要读取的字节数。
综合考虑,正确的代码可能看起来像这样:
// Edited: note comments after the code
#ifndef O_LARGEFILE
#define O_LARGEFILE 0
#endif

int read_from_file_open(char *filename,size_t size)
{
int fd;
long *buffer=(long*) malloc(size * sizeof(long));
fd = open(filename, O_RDONLY|O_LARGEFILE);
   if (fd == -1)
    {
       printf("\nFile Open Unsuccessful\n");
       exit (0);;
    }
off_t chunk=0;
lseek(fd,0,SEEK_SET);
printf("\nCurrent Position%d\n",lseek(fd,size,SEEK_SET));
while ( chunk < size )
  {
   printf ("the size of chunk read is  %d\n",chunk);
   size_t readnow;
   readnow=read(fd,((char *)buffer)+chunk,1048576);
   if (readnow < 0 )
     {
        printf("\nRead Unsuccessful\n");
        free (buffer);
        close (fd);
        return 0;
     }

   chunk=chunk+readnow;
  }

printf("\nRead Successful\n");

free(buffer);
close(fd);
return 1;

}

我还顺便删除了结果变量和所有相关逻辑,因为我认为它可以简化。

编辑:我注意到一些系统(尤其是BSD)没有O_LARGEFILE,因为在这些系统中不需要它。因此,我在开头添加了一个#ifdef,使代码更具可移植性。


2

使用 lseek 函数可能难以支持大文件大小。尝试使用 lseek64

当您使用 lseek64 函数时,请检查链接以查看所需定义的关联宏。


0
如果是32位机器,读取大于4GB的文件可能会出现问题。因此,如果您正在使用gcc编译器,请尝试使用宏-D_LARGEFILE_SOURCE=1-D_FILE_OFFSET_BITS=64
请还要查看link
如果您使用其他编译器,请检查是否有类似的编译器选项。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接