C++ Reading file backwards from the end of the file

不羁的心 提交于 2019-12-01 18:50:22

The following is my solution to the question. When open the file, I use ios::ate to set the file position to the end of the file and use seekg method to read back. I am not sure whether there is a more efficient way to solve this question.

void readFile(char *fileName){
    char c;
    std::ifstream myFile(fileName,std::ios::ate);
    std::streampos size = myFile.tellg();
    for(int i=1;i<=size;i++){
        myFile.seekg(-i,std::ios::end);
        myFile.get(c);
        printf("%c\n",c);
    }
}
Kyle Strand

ios::end doesn't actually indicate that seekg should seek backward; rather, it simply indicates that offsets are relative to the end of the file. (Yes, I think it's a poor naming convention to call the class ios_base::seekdir.) As far as I know, there is no standard way to actually read a file backwards, though there are some suggestions here for how to emulate doing so: Read a file backwards?

Unless your primary intent is to waste time, you want to seek to the correct position, read through the file frontwards, then reverse the data in memory. If you're reading a large amount, and only want a small amount in memory at a time, you can modify that a little to read (for example) 16 or 64 Kilobyte chunks.

Once you have a chunk of data in memory, you have two choices. You can either process that from its end backward toward the beginning, or you can reverse it, then process it from beginning to end. The latter might look something like this:

// Warning: I have only tested this, not proven it correct.
std::vector<char> read_backwards(std::istream &is, int size) {
    std::vector<char> buffer;

    buffer.resize(size);

    is.seekg(-size, std::ios::end);
    is.read(&buffer[0], size);
    std::reverse(buffer.begin(), buffer.end());
    return buffer;
}

Attempting to actually read the file backward can (and often will) lead to a serious performance problem. To be specific, the standard library will often flush its buffers any time you do a seek on a stream, so each reading immediately following a seek will typically result calling a kernel function to read data from disk (or at least from the OS's cache).

In case you care why that is: the C++ standard bases its description of how files work on the C standard. The C standard says that if you open a stream for both reading and writing, you have to do a seek on the stream when/if you switch from reading to writing (or vice versa). Because of this, quite a few implementations of seeking on a stream flush the buffer at every seek, just in case you might be switching from writing to reading, and need to read back in data that was in the write buffer before the seek.

Usually this doesn't matter much: a seek will often take you outside the range that's in the buffer anyway, so you'd end up having to read data from the disk anyway. In the specific case being dealt with here, however, buffering can work quite nicely as long as you read the data "frontwards", then reverse it instead of doing a seek for each byte. Although it's best to use the biggest buffer that's practical, even a fairly small buffer can make a big difference in speed.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!