3

ループ内で std::ifstream を 1 行ずつ使用してファイルを読み込もうとしています。同じループで、全体として読み取りたいブロックを囲む 2 つのタグを見つけようとしています。

私は、seekg を使用してブロックの開始位置と終了位置を追跡し、両方の位置を見つけた後、 read(*,end-start) を使用してブロックを読み取ることができると考えました。

ただし、tellg はストリーム位置を返しますが、ファイルがテキスト モードで開かれているため [getline を呼び出すことができるように]、行末として \r\n を使用しているため、ifstream のパラメータ「文字数」- read-method は、\r\n から \n への変換後の数値を参照するため、予想よりも正確に n 文字多く読み取っています。ここで、n は 2 つのタグ間の行数です。

明らかに、多くの回避策がありますが、私は素晴らしく直感的な解決策を探しています。助言がありますか?

EDIT1@130507: 私の目標は、std lib ストリームを維持し、速度よりもメモリを優先することです。タグ間の周囲の部分とブロックの両方の部分を解析して処理する必要があります。

すでに開いているテキストモードストリームでバイナリモードに切り替えるか、読み取りのような文字変換を行わない (ベースクラスの) raw-read-method や、それを可能にするいくつかのマッパーメソッドを使用するなど、何か利用可能なものがあることを願っていました。ストリーム間のマップは、文字変換の前後に表示されますが、これまでのところ何も見つかりませんでした。

ここにいくつかのコードがあります:

std::ifstream testDataFileStream;
testDataFileStream.open(testDataFileName, std::ios_base::in);
testDataFileStream.unsetf(std::ios::skipws); // No white space skipping
if (testDataFileStream) {
    std::string line;
    while (getline(testDataFileStream, line))
        if (line == "starttag")
            break;
    if (line == "starttag")
    {
        std::ifstream::pos_type cmdStartPos = testDataFileStream.tellg();
        std::ifstream::pos_type cmdEndPos;
        while (getline(testDataFileStream, line))
            if (line == "endtag")
                break;
            else
                cmdEndPos = testDataFileStream.tellg();
        if (line == "endtag")
        {
            std::streamsize nofBytesToRead = cmdEndPos - cmdStartPos;

            // now, one possible workaround follows, however, it's obviously quite lame
            testDataFileStream.close();
            testDataFileStream.open(testDataFileName, std::ios_base::in | std::ios::binary);
            testDataFileStream.seekg(cmdStartPos);
            std::string cmdsString;
            cmdsString.resize(nofBytesToRead+1);
            testDataFileStream.read(&cmdsString[0], nofBytesToRead);
        } else {}
    } else {}
    testDataFileStream.close();
} else {}

テストファイルは次のようになります。

some text
more text
starttag
much stuff on many lines
endtag
even more text
4

4 に答える 4

1

Jerry Coffin のアプローチを拡張するために、ここに簡単な例を示します。C++11 のstd::move余分な割り当てを使用することで回避されます。ただし、特に長い行の場合、引数getline()の再割り当てが繰り返されることに注意してください。std::stringメモリ管理に本当に関心がある場合は、データを固定サイズのバッファに読み込むことを検討する必要があります。

とにかく、ここにコードがあります:

#include <fstream>
#include <iostream>
#include <vector>
#include <utility>

int main() {
    std::ifstream testDataFileStream;
    testDataFileStream.open("data.txt", std::ios_base::in);
    testDataFileStream.unsetf(std::ios::skipws); // No white space skipping
    if (testDataFileStream) {
        std::vector<std::string> buffer;
        std::string line;
        bool found = false;
        while (getline(testDataFileStream, line)) {
            if (line == "starttag")
                found = true;
            if (found) {
                buffer.push_back(std::move(line));
                if (line == "endtag")
                    found = false;
            }
        }
        for (std::string & s : buffer) {
            std::cout << s << std::endl;
        }
    }
}
于 2013-05-18T21:17:28.537 に答える
1

ファイルをテキスト モードで開くと、文字変換が行われます。

ファイルをバイナリモードで開くことができます。ios::binary

于 2013-05-16T16:51:14.023 に答える