首页 文章

代码点中的fstream文件大小

提问于
浏览
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 Headers ,发现seek infact不使用 codecvt . 而且,在VS2010的版本中,甚至没有提到 codecvt::length() 成员 . 事实上,在每次调用 codecvt::in() 时,每次 in() 返回 partial 时,都会创建一个新的字符串对象并将其大小增加1个字符 . 它不会调用 codecvt::max_length() 成员并使用适当的缓冲区提供调用 .

这只是我的实现还是我希望其他人也这样做? std::fstream 是否已为VS2012重写以充分利用区域设置?

基本上,我希望创建一个 fstream 派生类,它将首先读取文件BOM(如果存在),并灌输正确的 codecvt . 然后将这些字符转换为 charwchar_t 或代码所要求的任何内容 . 我也希望以这样的方式对其进行编码:如果已知编码的先验知识,则可以在构造时指定 locale .

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

2 回答

  • 2

    如果我理解你,你期望:

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

    (通过继承是 basic_istream<CharT,Traits>::seekg ),应该以流为1的 codecvtintern_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 ,因此它是在任何实现中仅由 basic_istream 类的 CharT 模板参数确定的类型,并且不引用任何 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 对于任何充满的_101010都是不变的,因此也是 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"序列的类型 . intern_type 与文件上的定位操作无关 .

    如果您必须在代码点中找出文件的大小,并假设可能的编码是UTF-8,UTF-16和UTF-32,那么对于前两个,您别无选择,只能阅读整个文件,因为它们是可变长度编码,UTF-8代码点消耗1-4个字节,UTF-16代码点消耗2或4个字节 . UTF-32是一个固定长度的4字节编码,所以在这种情况下你可以计算完整代码点的数量作为文件的字节长度,减去BOM长度(如果有的话),除以4,如果你打折的可能性编码错误除了在文件结尾处 .

    对于可变长度编码,计算代码点的最简单方法是使用由假定编码的指示符参数化的模板函数 . 它将读取文件,首先消耗BOM(如果有的话),以 charchar16_t 为单位,根据假定的编码识别作为代码点的主要元素的每个单元;验证引导元素所需的后续元素数量的存在,并在找到它们时递增代码点计数 .

  • 0

    std::char_traitslength 函数返回 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";
    

相关问题