1. ホーム
  2. c++

[解決済み] std :: ifstream で LF, CR, CRLF を扱えるようにする?

2023-03-09 13:32:30

質問

具体的に興味があるのは istream& getline ( istream& is, string& str ); . ifstream のコンストラクタに、すべての改行エンコーディングを '\n' に変換するよう指示するオプションはありますか? を呼び出すことができるようにしたいです。 getline を呼び出すことができ、それがすべての行末を優雅に処理するようにしたいのです。

更新 : はっきり言って、私はほとんどどこでもコンパイルできて、ほとんどどこからでも入力を受けられるようなコードを書きたいのです。その中には、稀に '\r' が '\n' と共に存在するファイルも含まれます。

この問題を回避するのは簡単ですが、すべてのテキストファイル形式を柔軟に処理するための、標準の正しい方法について、私はまだ興味を持っています。

getline は全行、'˶'までを文字列に読み込んでいます。この'˶'はストリームから消費されますが、getlineは'˶'を文字列に含めません。ここまではいいのですが、'˶'の直前に'˶'があって、それが文字列に含まれるかもしれません。

があるのですが の3種類の改行があります。 がある。 Unix系では'en'、古いMac系では'en'、Windowsでは'en'と'en'が対になっていることが多い。

問題なのは getline では文字列の末尾に '\r' が残ってしまうことです。

ifstream f("a_text_file_of_unknown_origin");
string line;
getline(f, line);
if(!f.fail()) { // a non-empty line was read
   // BUT, there might be an '\r' at the end now.
}

編集 Neil の指摘に感謝します。 f.good() は私が望んでいたものではないことを指摘してくれたNeilに感謝します。 !f.fail() は私が欲しいものです。

私はそれを自分で手動で削除することができます(この質問の編集を参照)、それはWindowsのテキストファイルのために簡単です。しかし、私は、誰かが '\r' だけを含むファイルを送り込むことを心配しています。その場合、getline はそれが 1 行であると考え、ファイル全体を消費すると推測されます!

...そしてそれは、Unicode を考慮さえしていません :-)

...多分Boostはどんなテキストファイルタイプからでも一度に1行ずつ消費する素晴らしい方法をもっているのでしょう?

編集 Windowsのファイルを処理するために、これを使っているのですが、やはり必要ないような気がします そして、これは、'˶'ᴗ'ᴗ'ᴗ'のみのファイルにはフォークしません。

if(!line.empty() && *line.rbegin() == '\r') {
    line.erase( line.length()-1, 1);
}

どのように解決するのですか?

Neil が指摘したように、"C++ ランタイムは、特定のプラットフォームの行末規則が何であれ、正しく対処するはずです。

しかし、人々は異なるプラットフォーム間でテキスト ファイルを移動させるので、それでは十分ではありません。

しかし、異なるプラットフォーム間でテキストファイルを移動させることもあるので、それだけでは不十分です。

std::istream& safeGetline(std::istream& is, std::string& t)
{
    t.clear();

    // The characters in the stream are read one-by-one using a std::streambuf.
    // That is faster than reading them one-by-one using the std::istream.
    // Code that uses streambuf this way must be guarded by a sentry object.
    // The sentry object performs various tasks,
    // such as thread synchronization and updating the stream state.

    std::istream::sentry se(is, true);
    std::streambuf* sb = is.rdbuf();

    for(;;) {
        int c = sb->sbumpc();
        switch (c) {
        case '\n':
            return is;
        case '\r':
            if(sb->sgetc() == '\n')
                sb->sbumpc();
            return is;
        case std::streambuf::traits_type::eof():
            // Also handle the case when the last line has no line ending
            if(t.empty())
                is.setstate(std::ios::eofbit);
            return is;
        default:
            t += (char)c;
        }
    }
}

そして、これがテストプログラムです。

int main()
{
    std::string path = ...  // insert path to test file here

    std::ifstream ifs(path.c_str());
    if(!ifs) {
        std::cout << "Failed to open the file." << std::endl;
        return EXIT_FAILURE;
    }

    int n = 0;
    std::string t;
    while(!safeGetline(ifs, t).eof())
        ++n;
    std::cout << "The file contains " << n << " lines." << std::endl;
    return EXIT_SUCCESS;
}