0

std::fstream のファイルのファイル サイズを取得するには多くの質問がありますが、それらはすべてファイル サイズをバイト単位で返し、ファイルが別のストリームで開かれている場合はエラーが発生しやすくなります。

バイトではなくコードポイントでファイルサイズを知りたいです。

これで、バイト単位の長さのみが返されstd::fstream::seekg(0,std::ios::end)ます。std::fstream::tellg()これは、ファイル内の UTF-16/32 文字の数を教えてくれません。その結果sizeof(wchar_t)をあなたの言うことを聞いて割ってください。UTF-8 ファイルでは機能せず、移植可能ではありません。

さて、より技術的な考え方のために、私は独自のクラスを持つimbuedストリームを持っています。ストリームへの 2 つのポインターを指定すると、長さを計算し、最大出力文字数または出力文字数のいずれかを返すメンバーがあります。ファイルのシークは、基本型ではなくシークすると思っていたでしょう。std::codecvtstd::codecvtlength()codecvt::intern_typechar

ヘッダーを調べたfstreamところ、seek infact でcodecvt. また、VS2010 の私のバージョンでは、codecvt::length()メンバーについても言及されていません。実際、 を呼び出すたびに、新しい文字列オブジェクトが作成され、 が返さcodecvt::in()れるたびに 1 文字ずつサイズが増加します。代わりに、メンバーを呼び出して、呼び出しに適切なバッファーを提供しません。in()partialcodecvt::max_length()

これは私の実装だけですか、それとも他の人も同じことを期待できますか? std::fstreamロケールをフルに活用するために VS2012 用に書き直されましたか?

基本的に、テキスト ファイルを使用するたびに独自のファイル ハンドラーを作成する必要があることにうんざりしています。ファイルBOMfstreamが存在する場合は最初にそれを読み取り、正しいcodecvt. char次に、それらの文字を、wchar_tまたはコードが要求するものに変換します。また、エンコーディングの事前知識がわかっている場合は、構築時に alocaleを指定できるようにコーディングしたいと考えています。

内部バッファで直接作業した方がよいでしょうか? fstream クラスを書き直すのに影響しますか、それとも私が知らないいくつかのトリックがありますか?

4

2 に答える 2

2

私があなたを正しく理解しているなら、あなたはそれを期待しています:

`std::basic_fstream<CharT,Traits>::seekg`

(これは継承により です)、ストリームに注入されているものbasic_istream<CharT,Traits>::seekgの単位でストリーム配置操作を実行する必要があります 。intern_typecodecvt

テンプレートbasic_istreamが宣言されています:

template< 
    class CharT, 
    class Traits = std::char_traits<CharT>
> class basic_istream;

メンバー関数の宣言:

basic_istream & basic_istream<CharT,Traits>::seekg(pos_type pos)

pos_typestd::char_traits<CharT>::pos_typeしたがって、 is は、クラスのテンプレート引数のみによって、 any への参照なしで、どの実装でも決定される型CharTです。basic_istreamcodecvt

basic_fstream<char>たとえば、Aは、読み取りまたは書き込みに選択されたエンコーディングに関係なく、 のままでbasic_fstream<char>あり、そのままです。pos_typebasic_fstream<char>::pos_type

上記の宣言は、それぞれ C++11 標準 § 27.7.1 および § 27.7.2.1 に従っています。pos_typeが注入された に対して不変であるという事実codecvt、したがって の動作もseekg(pos_type)、標準の結果です。

に相当する注釈が適用されbasic_istream& seekg( off_type off, std::ios_base::seekdir dir)ます。

std::codecvt::intern_type、指定されたエンコーディングが type の要素の外部シーケンスを変換する内部シーケンスの要素の型extern_typeです。は intern_type「プログラム内」シーケンスの要素タイプで、 は「extern_typeファイル内」シーケンスのタイプです。はintern_type 、ファイルの位置決め操作とは関係ありません。

コードポイントでファイルのサイズを確認する必要があり、対象となる可能性のあるエンコーディングが UTF-8、UTF-16、および UTF-32 であると仮定すると、これらのうち最初の 2 つについては、全体を読み取るしかありません。これらは可変長エンコーディングであるため、UTF-8 コードポイントは 1 ~ 4 バイトを消費し、UTF-16 コードポイントは 2 または 4 バイトを消費します。UTF-32 は固定長の 4 バイト エンコーディングであるため、可能性を割り引いた場合、完全なコードポイントの数は、ファイルのバイト長から BOM の長さを差し引いて 4 で割った値として計算できます。ファイルの終わり以外のエンコーディング エラーの数。

可変長エンコーディングの場合、コードポイントをカウントする最も簡単な方法は、推定エンコーディングのインジケータによってパラメータ化されたテンプレート関数を使用することです。ファイルを読み取り、最初に BOM を消費し、必要に応じて、推定エンコーディングのコードポイントの先頭要素である各ユニットを識別しますcharchar16_tリード要素に必要な数の後続要素の存在を確認し、見つかった場合はコードポイント カウントをインクリメントします。

于 2013-07-17T11:24:11.483 に答える
0

length関数はstd::char_traits文字数を返しますがCharT、これは必ずしもバイト数ではありません。したがって、基本的に行う必要があるのは、ファイルのバッファーをに読み取り、std::stringそのを出力することsize()です。

std::ofstream out("out.txt");
out.rdbuf()->pubimbue(std::locale("en_US.UTF8"));

std::streambuf* p = out.rdbuf();
p->pubseekoff(0, std::ios_base::beg);

std::string data; //  use std::u16string for UTF-16 data

data.assign(std::istreambuf_iterator<char>(out),
            std::istreambuf_iterator<char>());

std::cout << "We have " << data.size() << " codepoints";
于 2013-07-27T12:31:49.220 に答える