cpp文件读取错误与stat和read()

时间:2015-04-28 04:57:17

标签: c++ linux io posix

  1. 我不经常遇到此错误,无法重现。
  2. 正在阅读的文件是只读文件,无法删除或修改。
  3. 代码并不完全相同,因为它是我写的更大的一部分,但这是引起问题的代码的重要部分。
  4. 此代码用于解释目的,而不是因为第1点
  5. 而重现问题

    我正在尝试使用

    读取文件
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <memory>
    #include <exception>
    #include <iostream>
    #include <glog/logging.h>
    using namespace std;
    int main() {
        string fileName="blah";
        struct stat fileStat;
        int status = ::stat(fileName.c_str(), &fileStat);
        if (status != 0) {
         LOG(ERROR) << "Error stating the file";
        }
        size_t fileSize = fileStat.st_size;
        // fileSize is 79626240. I am trying to read block starting from 
        // 67108864 bytes, so there will be 1251736
        size_t fileBlockSize = 16 * 1024 * 1024;
        size_t numBlocks = fileSize / fileBlockSize;    
        size_t offset = numBlocks;
        size_t actualSize = fileSize - offset * fileBlockSize;
        if (actualSize == 0) {
          LOG(INFO) << "You read the entire file";
          return 1;
        }
    
        int fd = ::open(fileName.c_str(), O_RDONLY);
        if (fd < 0) {
          throw std::runtime_error("Error opening the file");
        } else if (offset > 0 && lseek(fd, offset, SEEK_SET) < 0) {
         throw std::runtime_error("Error seeking the file");
        }
    
        uint64_t readBlockSize = 256 * 1024;    
        char *data = new char[readBlockSize + 1];
        uint64_t totalRead = 0;
        while (totalRead < actualSize) {
          ssize_t numRead = ::read(fd, data, readBlockSize);
          // Use the data you read upto numRead
          if (numRead == 0) {
           LOG(ERROR) << "Reached end of file";
           break;
          } else if (numRead < 0) {
           throw std::runtime_error("read unsuccessful");
          } 
          totalRead += numRead;
        }
        if (totalRead != actualSize) { 
          LOG(ERROR) << "Error reading the file";
        }
    }
    

    如果您想象我将文件切成16块大小的块,然后读取最后一块。我正在一个较小尺寸的循环中读取块,但是在我读完整个块之前我得到了EOF。是否会发生stat报告的大小大于文件中数据的大小?

    我看到的输出:

    Reached end of file
    Error reading the file
    

    我不需要替代解决方案,我可以做其他事情,比如lseek到END但是我想知道为什么会发生这种情况?

    PS这不是因为磁盘上的块数。我正在使用st_size而已

2 个答案:

答案 0 :(得分:2)

您必须使用stat对文件进行处理,最好使用fstat来避免TOCTOU竞争条件。

int fileDescriptor = -1;
struct stat fileStat;
std::vector<char> fileContent;
std::string filename("test.txt");

fileDescriptor = open(filename.c_str(),O_RDONLY);
// Do error check of fileDescriptor
fstat(fileDescriptor,&fileStat);
// Do error check of fstat
fileContent.resize(fileStat.st_size);
::read(fileDescriptor,fileContent.data(),fileStat.st_size);

close(fileDescriptor);

另外,考虑read可能返回一个小于fileStat.st_size的值,你必须读取剩余的字节(在文件I / O中相当困难,但对于套接字很常见),代码只是一个小例子。

修改

我已复制您的代码并修改为加载本地9MB文件,在使用g++ -g -std=c++11 -lglog main.cpp编译后,我在第51行设置了断点

  

if(totalRead!= actualSize)

这是我的调试会话的结果:

  

(gdb)b main.cpp:51断点1位于0x4013fc:文件main.cpp,第51行。

     

(gdb)r启动程序:/home/jpalma/Documents/functionTest/a.out

     

[使用libthread_db启用线程调试]使用主机libthread_db   库“/lib64/libthread_db.so.1”。

     

在main.cpp的断点1,main():51

     

51 if(totalRead!=&gt; actualSize){

     

(gdb)p totalRead

     

$ 1 = 9000032

     

(gdb)p actualSize

     

$ 2 = 9000032

所以基本上你的程序对我来说完美无瑕。也许您的文件系统存在问题或与此无关的问题。

我正在使用ext4作为文件系统。

ll从我正在阅读的文件9000032 abr 29 16:10 WebDev.pdf报告此大小,因此您可以看到它实际上是正确的。我的页面大小是

$ getconf PAGESIZE
4096

答案 1 :(得分:0)

所以你的问题是: fstatstat在只读的非修改文件上报告的大小可以大于从文件中读取的大小

首先read上的一些元素返回值(来自手册页): 成功时,返回读取的字节数(零表示文件结束),文件位置按此编号前进。如果此数字小于请求的字节数,则不是错误...出错时,返回-1,并且正确设置errno

因此返回值最大为请求的大小,可以小于,0表示文件结束指示,-1表示错误指示。

手册页还说,读取的字节数少于请求的大小可能会发生,例如因为现在实际可用的字节数较少(可能是因为我们接近文件结尾,或者因为我们正在读取管道,或从终端),或因为read()被信号中断。

所以即使我再也看不到,文档中没有任何内容可以保证即使阅读文件,您也可以获得所需的数据,除非文件已经到达。

但明确说明,返回值为0表示您处于文件末尾。当您使用0 == read(...)测试文件末尾时,一切都很好。

对于你的问题:stat报告的大小可以与文件中可以读取的字节数不同,答案是,除非文件系统坏了,或者有物理读错误。这是size成员的定义: st_size字段以字节为单位给出文件的大小(如果是常规文件或符号链接)(来自stat手册页)

但我真的无法理解你的代码。首先我看到:

size_t fileSize = fileStat.st_size;
// fileSize is 79626240. I am trying to read block starting from 
// 67108864 bytes, so there will be 1251736
size_t fileBlockSize = 16 * 1024 * 1024;
size_t numBlocks = fileSize / fileBlockSize;    
size_t offset = numBlocks;
size_t actualSize = fileSize - offset * fileBlockSize;

当文件长度为actualSize时,1251736现在为79626240

之后,没有actualSize发生了变化:

uint64_t totalRead = 0;
while (totalRead < actualSize) {
  ssize_t numRead = ::read(fd, data, readBlockSize);
  ...
  totalRead += numRead;
}
if (totalRead != actualSize) { 
  LOG(ERROR) << "Error reading the file";
}

尽管actualSize的名称实际文件大小,但您可以进入Error reading the file分支。但如果它与真实文件大小一起发生,请仔细检查您的文件系统。