我正在尝试使用
读取文件#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而已
答案 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)
所以你的问题是: fstat
或stat
在只读的非修改文件上报告的大小可以大于从文件中读取的大小 ?
首先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
分支。但如果它与真实文件大小一起发生,请仔细检查您的文件系统。