2013-07-17 74 views
0

获取std :: fstream文件的文件大小有很多问题,但它们都以字节为单位返回文件大小,并且如果文件在另一个流中打开,则容易出错。码流中的fstream文件大小

我想知道代码点的文件大小,而不是字节。

现在std::fstream::seekg(0,std::ios::end)后面跟着std::fstream::tellg()只返回字节长度。这并不能告诉我文件中有多少个UTF-16/32字符。将结果除以sizeof(wchar_t)我听到你说。不适用于UTF-8文件,不可移植。

现在,对于更具技术意识的人,我有imbued这个流与我自己的std::codecvt类。 std::codecvt有一个成员length(),它给出流中的两个指针计算长度并返回最大或输出字符数。我本来以为在文件上查找将通过codecvt::intern_type而不是基于char类型。

我查看了fstream标题,发现seek forfact不使用codecvt。而且,在VS2010的版本中,我们甚至没有提到codecvt::length()成员。事实上,每次调用codecvt::in()时,都会创建一个新的字符串对象,并且每次增加1个字符的大小,in()返回partial。它不是调用codecvt::max_length()成员,而是使用适当的缓冲区提供呼叫。

这是我的实施还是我可以期望别人也这样做? std::fstream已被重写为VS2012充分利用语言环境?

基本上,我厌倦了每次使用文本文件时都必须编写自己的文件处理程序。我希望创建一个fstream派生类,它将首先读取文件BOM(如果存在),并填入正确的codecvt。然后将这些字符转换为charwchar_t或代码要求的任何内容。我也希望以这样的方式对其进行编码,如果已知编码的先前知识,则可以在构建时指定locale

我会更好地直接在内部缓冲区工作,影响重写fstream类还是有一些我不知道的技巧?

+0

看来,一般一个坏主意,试图弄清楚码点一个UTF8编码文件的大小。你必须阅读整个文件。我认为应该没有理由提前知道这个大小。同时分配缓冲区以适应整个文件也很危险,因为文件可能很大而且不适合。一块一块地阅读它。 – madnut

+0

@madnut我同意加载整个文件不好,因此我使用'codecvt'。主要问题是在文件中寻找。这些fstreams不是文本文件友好的。但是我认为你在事先知道文件的长度方面是错误的。在某些情况下,它是必需的。 – Twifty

回答

2

如果我理解你的权利,你想到的是:

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

(其通过继承是basic_istream<CharT,Traits>::seekg),应该 在那些无论codecvt与其中 intern_type单位执行流定位操作流被灌输。

模板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其因此是 在任何实现所确定的类型仅由CharT模板 参数为basic_istream,并且没有提及任何codecvt

basic_fstream<char>,例如仍然basic_fstream<char>, 及其pos_type保持basic_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是到或从该指定​​的编码将 翻译extern_type类型的元件的外部序列内部 序列的元素的类型。 intern_type是“in-program”序列的元素类型,并且extern_type是“in-file”序列的类型。该文件上的定位操作与该文件无关。

如果必须找出码点文件的大小,并且假定 感兴趣的可能的编码是UTF-8,UTF-16和UTF-32,然后 对于前两项你别无选择,只能读取整个文件 ,因为它们是可变长度编码,UTF-8编码点消耗了1至4个字节,而UTF-16编码点消耗2或4个字节。 UTF-32是一个 固定长度的4字节编码,所以在这种情况下,您可以计算完整代码点的数量作为文件的字节长度,减去BOM长度(如果有的话), 除以4,如果您折扣编码错误的可能性,除了在文件结束时。

对于可变长度编码,计数 的最简单方法是使用由推测编码的 指示符参数化的模板函数。它将以或char16_t为单位读取文件,首先消耗BOM(如果有的话) , 确定每个单元是 中推测的编码的引导元素;验证是否存在导致元素所需的随后的 元素的数量,并且如果发现它们则增加码位数 。

+0

我明白你的意思是参考'pos_type'。我目前一直在阅读这些类的源代码,看看它是如何在内部工作的。我很惊讶地看到'basic_streambuf'不使用缓冲。它调用每个操作的'c'文件函数。无论如何,除了文件大小,我想我可能不得不编写自己的实现,只是因为'basic_fstream'不允许指定新的/替换'basic_streambuf'。 – Twifty

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";