【发布时间】:2012-05-30 09:53:41
【问题描述】:
有没有一种方法可以逐行向后读取文件,而不必从头开始向后读取文件?
【问题讨论】:
-
您可以将文件的行读入
vector并反转它。 -
@hmjd 这应该是一个答案,IMO。
标签: c++
有没有一种方法可以逐行向后读取文件,而不必从头开始向后读取文件?
【问题讨论】:
vector 并反转它。
标签: c++
使用memory-mapped file 并向后走。操作系统将以相反的顺序分页文件的所需部分。
【讨论】:
根据评论,一个可能的(非常简单的)替代方案是将这些行读入vector。例如:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
int main()
{
std::ifstream in("main.cpp");
if (in.is_open())
{
std::vector<std::string> lines_in_reverse;
std::string line;
while (std::getline(in, line))
{
// Store the lines in reverse order.
lines_in_reverse.insert(lines_in_reverse.begin(), line);
}
}
}
编辑:
根据jrok's 和Loki Astari's cmets,push_back() 会更有效,但行将按文件顺序排列,因此需要反向迭代(reverse_iterator)或std::reverse():
std::vector<std::string> lines_in_order;
std::string line;
while (std::getline(in, line))
{
lines_in_order.push_back(line);
}
【讨论】:
std::reverse,也可以使用reverse_iterator 进行迭代。
push_back,那么是的,使用一种反向的形式。
push_backs 后跟 reverse 会更快。
reverse_iterator 或std::reverse(lines_in_order.begin(), lines_in_order.end()); 然后iterator。
打开文件进行读取,调用fseek()查找文件末尾,然后调用ftell()获取文件长度。或者,您可以通过调用 stat() 或 fstat()
分配一个缓冲区指针,指向上面 #1 中获得的文件大小。
将整个文件读入该缓冲区——您可能可以使用fread() 一次性读取所有文件(假设文件足够小)。
使用另一个 char 指针将文件从缓冲区的末尾横向移动到缓冲区的开头。
【讨论】:
稍微改进的版本是这样的:-
1)寻找到最后一个位置
2) 获得最后 1 个位置
3)读取一个字符并打印它;
4) 寻找 2 个 pos 回来;
5) 重复 3 &4 last-1 次;
ifstream in;
in.open("file.txt");
char ch;
int pos;
in.seekg(-1,ios::end);
pos=in.tellg();
for(int i=0;i<pos;i++)
{
ch=in.get();
cout<<ch;
in.seekg(-2,ios::cur);
}
in.close();
【讨论】:
简短的回答是否定的。但是,您可以使用 seek() 函数将指针移动到您想去的地方。然后从那个点读取()一些数据。如果你知道如何管理缓冲区,那么它应该很快,因为你可以读取和缓存数据,然后搜索前一个换行符。玩得开心 \r\n 将被反转...
-- 更新:对可能算法的一些阐述--
这不是有效的代码,但它应该让您了解我在这里要说的内容
文件内容:
int fpos = in.size() - BUFSIZ;
char buf[BUFSIZ];
in.seek(fpos);
in.read(buf, BUFSIZ);
fpos -= BUFSIZ; // repeat until fpos < 0, although think of size % BUFSIZ != 0
// now buf has characters... reset buffer position
int bpos = BUFSIZ - 1;
获取字符串:
// first time you need to call the read
if(bpos == -1) do_a_read();
// getting string
std::string s;
while(bpos >= 0 && buf[bpos] != '\n') {
s.insert(0, 1, buf[bpos]);
--bpos;
}
// if bpos == -1 and buf[0] != '\n' then you need to read another BUFSIZ chars
// and repeat the previous loop...
// before leaving, skip all '\n'
while(bpos >= 0 && buf[bpos] == '\n') {
--bpos;
}
return s;
为了简化 '\r',您可以先通过将所有 '\r' 转换为 '\n'。否则,'\n' 的所有测试也需要测试'\r'。
【讨论】:
\r\n 字节解释为换行符的问题。如果您向后读取字节,并自己解释换行字节,您当然可以向后读取行。这不是标准功能。
我的答案类似于使用vector 来存储文件行的答案,但我会改为使用list。
假设您在名为input.txt 的文件中有以下文本:
hello
there
friend
我会逐行阅读文件,不是将每一行推到我的list 的后面,而是推到它的前面。使用它而不是 push_back 与将文件的内容逐行读取到 vector 然后反转它或向后迭代它的效果相同。
#include <iostream>
#include <fstream>
#include <list>
#include <string>
#include <iterator>
#include <algorithm>
int main(void) {
std::ifstream file;
file.open("input.txt");
// Make sure the file opened properly
std::list<std::string> list;
std::string buffer;
while (std::getline(file, buffer)) {
list.push_front(buffer);
}
file.close();
std::copy(
list.begin(),
list.end(),
std::ostream_iterator<std::string>(std::cout, "\n")
);
return 0;
}
(注意底部带有std::copy的位只是打印列表的内容,用换行符作为元素之间的分隔符。)
然后打印:
friend
there
hello
【讨论】: