0

获取 std::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 事实上不使用codecvt. 而且,在我的 VS2010 版本中,codecvt::length()甚至没有提到该成员。事实上,在每次调用时,都会创建一个新的字符串对象,每次返回时都会codecvt::in()增加 1 个字符。相反,它不会调用成员并为调用提供足够的缓冲区。in()partialcodecvt::max_length()

这只是我的实现还是我可以期望其他人也这样做?是否std::fstream已针对 VS2012 重写以充分利用语言环境?

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

直接在内部缓冲区上工作会更好,影响重写 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_type因此std::char_traits<CharT>::pos_type,它是在任何实现中仅由类的CharT模板参数确定的basic_istream类型,而不参考任何codecvt.

例如, Abasic_fstream<char>仍然是 a 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_typeintern_type是“程序内”序列的元素类型,而是 extern_type“文件内”序列的类型。与文件上的intern_type 定位操作无关。

如果您必须以代码点为单位找出文件的大小,并假设可能感兴趣的编码是 UTF-8、UTF-16 和 UTF-32,那么对于前两个,您别无选择,只能阅读整个文件,因为它们是可变长度编码,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 回答