ファイルを最初から逆方向に読み始めることなく、ファイルを1行ずつ逆方向に読み取る方法はありますか?
6 に答える
メモリマップトファイルを使用して、後方に移動します。OSは、ファイルの必要な部分を逆の順序でページングします。
コメントによると、可能な(非常に単純な)代替案は、行をに読み込むことになります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とLokiAstariのコメントによる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);
}
読み取り用にファイルを開き、を呼び出し
fseek()
てファイルの最後までシークしてから、を呼び出してftell()
ファイルの長さを取得します。stat()
または、またはを呼び出してファイルの長さを取得することもできますfstat()
上記の#1で取得したファイルサイズにバッファポインタを割り当てます。
ファイル全体をそのバッファ
fread()
に読み込みます。おそらく、ファイルをすべて1回で読み込むために使用できます(ファイルが十分に小さい場合)。別のcharポインターを使用して、ファイルをバッファーの最後から最初に移動します。
わずかに改善されたバージョンは次のようになります。-
1)最後の1つの位置までシークします
。2)最後の1つの位置を取得します
。3)文字を読み取って印刷します。
4)2つの位置を探します。
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()関数を使用して、ポインタを目的の場所に移動できます。次に、その時点からいくつかのデータをread()します。バッファの管理方法をよく知っている場合は、データを読み取ってキャッシュしてから、前の改行文字を検索できるため、非常に高速です。反転される\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」もテストする必要があります。
私の答えは、ファイルの行を格納するためにaを使用するものと似てvector
いますが、代わりに。を使用しlist
ます。
次のファイルに次のテキストがあるとしますinput.txt
。
hello
there
friend
私はファイルを1行ずつ読み、各行を自分の後ろではlist
なく前に押していました。これを使用するのではなくpush_back
、ファイルの内容を1行ずつaに読み込み、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