为什么在C的宽文件流++默认狭窄的写入的数据?(Why does wide file-stream

2019-07-20 13:32发布

老实说,我只是没有得到在C ++标准库中的以下设计决定。 当写入宽字符到一个文件中, wofstream转换wchar_tchar的字符:

#include <fstream>
#include <string>

int main()
{
    using namespace std;

    wstring someString = L"Hello StackOverflow!";
    wofstream file(L"Test.txt");

    file << someString; // the output file will consist of ASCII characters!
}

我知道,这与标准做codecvt 。 还有codecvtutf8Boost 。 此外,还有一个codecvtutf16由马丁·约克在这里SO 。 现在的问题是,为什么 standard codecvt转换宽字符? 因为它们为什么不写的字!

此外,被我们该怎么得到真正的unicode streams用的C ++ 0x还是我失去了一些东西?

Answer 1:

为字符集使用C ++模型选自C继承,所以追溯至至少1989。

两个要点:

  • IO被烧焦的期限完成。
  • 它是语言环境的工作,以确定如何宽字符的序列化
  • 默认语言环境(命名为“C”)是很小的(我不记得从标准的限制,在这里它只能处理7位ASCII窄和宽字符集)。
  • 有一个名为的环境确定的区域设置“”

因此,要得到任何东西,你必须设置的语言环境。

如果我用简单的程序

#include <locale>
#include <fstream>
#include <ostream>
#include <iostream>

int main()
{
    wchar_t c = 0x00FF;
    std::locale::global(std::locale(""));
    std::wofstream os("test.dat");
    os << c << std::endl;
    if (!os) {
        std::cout << "Output failed\n";
    }
}

其使用环境的区域设置和输出的宽字符代码设为$ 00FF的一个文件。 如果我要使用“C”语言环境,我得到

$ env LC_ALL=C ./a.out
Output failed

该区域已无法处理宽字符,我们得到通知的问题作为IO失败。 如果我运行问一个UTF-8码,我得到

$ env LC_ALL=en_US.utf8 ./a.out
$ od -t x1 test.dat
0000000 c3 bf 0a
0000003

(OD -t X1只转储以十六进制表示的文件),正是我所期待的UTF-8编码的文件。



Answer 2:

对于第一个问题非常局部答案:文件一个字节序列,从而,与涉及当wchar_t “s,至少一些之间转换wchar_tchar必须发生。 使这种转换“智能”要求的字符编码的知识,所以这就是为什么这种转换允许语言环境有关,由于使用流的区域设置一个小的。

然后,问题是怎么说的转换应在标准所要求的唯一场所进行:“经典”之一。 有没有“正确”的答案是什么,以及标准因此它很模糊。 我从你的问题明白,你认为盲目铸造(或的memcpy() - 荷兰国际集团)的wchar_t []和的char []本来是一个很好的方式之间。 这不是没有道理的,实际上是在什么(或者是至少)在一些实施完成。

另一个POV是,由于一个的codecvt是一个语言环境方面,这是合理的预期,转换使用“区域的编码”(我handwavy在这里,因为这个概念是非常模糊)制成。 例如,人们期待土耳其语言环境使用ISO-8859-9,或日上使用移动JIS。 通过类似的“经典”的区域将转换为这种“语言环境编码”。 显然,微软选择了简单的修剪(这会导致IS-8859-1,如果我们假设wchar_t代表UTF-16和我们留在基本多文种平面),而Linux实现我知道决定坚持ASCII。

关于第二个问题:

此外,在我们都没机会用的C ++ 0x真正的unicode流还是我失去了一些东西?

在n2857(最新的C ++ 0x草案我手边)的[locale.codecvt]部分,一个可以读取:

专业化codecvt<char16_t, char, mbstate_t>的UTF-16和UTF-8编码方案之间进行转换,且专业化codecvt <char32_t, char, mbstate_t>的UTF-32和UTF-8编码方案之间进行转换。 codecvt<wchar_t,char,mbstate_t>天然字符集窄和宽字符之间的转换。

在[locale.stdcvt]部分中,我们发现:

对于小面codecvt_utf8 : -小面应当UTF-8多字节序列和UCS2或UCS4方案内(取决于ELEM的大小)之间进行转换。 [...]

对于小面codecvt_utf16 : -小面应当UTF-16的多字节序列和UCS2或UCS4方案内(取决于ELEM的大小)之间进行转换。 [...]

对于小面codecvt_utf8_utf16 : -小面应当UTF-8多字节序列和节目内UTF-16(一个或两个16位的代码)之间进行转换。

所以我想,这意味着“是”,但你必须要更精确地了解你的意思是“真正的unicode流”可以肯定的。



Answer 3:

我不知道wofstream。 但是C ++ 0X将包括保证宽度和可便携地用于UTF-8的符号性(无符号),UTF-16和UTF-32的新DISTICT字符类型(char16_t,char32_t)。 此外,还会有新的字符串(U“你好!”一个UTF-16编码字符串字面量,例如)

看看最近的C ++ 0x草案(N2960) 。



Answer 4:

关于第一个问题,这是我的猜测。

Iostreams库是关于下一个编码夫妇房屋的建造。 对于Unicode和其他不那么平常编码之间进行转换,例如,它的假设。

  • 你的程序中,你应该使用(固定宽度)宽字符编码。
  • 只有外部存储应该使用(宽度可变)多字节编码。

我相信是的std ::的codecvt两个模板特存在的原因。 一个字符类型(也许你只是用ASCII工作),另一种为wchar_t(内部程序)和炭(外部设备)之间的映射之间的映射。 所以每当你需要执行转换为多字节编码你应该这样做逐字节。 注意,你可以写,当你阅读/从/多字节编码写入每个字节来处理编码状态的方面。

这样想的C ++标准的行为是可以理解的。 毕竟,你正在使用的编码宽字符的ASCII(假设这是你的平台上的默认和你没有切换区域设置)的字符串。 “自然”的转换将每个宽字符的ASCII字符转换为普通(在这种情况下,一个字符)ASCII字符。 (转化存在并且是简单的。)

顺便说一句,我不知道你是否知道,但你可以通过创建一个返回noconv为转换一个方面避免这种情况。 然后,你将不得不与宽字符的文件。



Answer 5:

检查了这一点: 类basic_filebuf

您可以通过设置一个字符缓冲区,使用pubsetbuf改变默认行为。 一旦你这样做,输出将是为wchar_t而不是char。

换句话说你的例子中,你将有:

wofstream file(L"Test.txt", ios_base::binary); //binary is important to set!  
wchar_t buffer[128];  
file.rdbuf()->pubsetbuf(buffer, 128);  
file.put(0xFEFF); //this is the BOM flag, UTF16 needs this, but mirosoft's UNICODE doesn't, so you can skip this line, if any.  
file << someString; // the output file will consist of unicode characters! without the call to pubsetbuf, the out file will be ANSI (current regional settings)  


文章来源: Why does wide file-stream in C++ narrow written data by default?