老实说,我只是没有得到在C ++标准库中的以下设计决定。 当写入宽字符到一个文件中, wofstream
转换wchar_t
成char
的字符:
#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
。 还有codecvt
为utf8
在Boost
。 此外,还有一个codecvt
为utf16
由马丁·约克在这里SO 。 现在的问题是,为什么 standard codecvt
转换宽字符? 因为它们为什么不写的字!
此外,被我们该怎么得到真正的unicode streams
用的C ++ 0x还是我失去了一些东西?
为字符集使用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编码的文件。
对于第一个问题非常局部答案:文件是一个字节序列,从而,与涉及当wchar_t
“s,至少一些之间转换wchar_t
和char
必须发生。 使这种转换“智能”要求的字符编码的知识,所以这就是为什么这种转换允许语言环境有关,由于使用流的区域设置一个小的。
然后,问题是怎么说的转换应在标准所要求的唯一场所进行:“经典”之一。 有没有“正确”的答案是什么,以及标准因此它很模糊。 我从你的问题明白,你认为盲目铸造(或的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流”可以肯定的。
我不知道wofstream。 但是C ++ 0X将包括保证宽度和可便携地用于UTF-8的符号性(无符号),UTF-16和UTF-32的新DISTICT字符类型(char16_t,char32_t)。 此外,还会有新的字符串(U“你好!”一个UTF-16编码字符串字面量,例如)
看看最近的C ++ 0x草案(N2960) 。
关于第一个问题,这是我的猜测。
Iostreams库是关于下一个编码夫妇房屋的建造。 对于Unicode和其他不那么平常编码之间进行转换,例如,它的假设。
- 你的程序中,你应该使用(固定宽度)宽字符编码。
- 只有外部存储应该使用(宽度可变)多字节编码。
我相信是的std ::的codecvt两个模板特存在的原因。 一个字符类型(也许你只是用ASCII工作),另一种为wchar_t(内部程序)和炭(外部设备)之间的映射之间的映射。 所以每当你需要执行转换为多字节编码你应该这样做逐字节。 注意,你可以写,当你阅读/从/多字节编码写入每个字节来处理编码状态的方面。
这样想的C ++标准的行为是可以理解的。 毕竟,你正在使用的编码宽字符的ASCII(假设这是你的平台上的默认和你没有切换区域设置)的字符串。 “自然”的转换将每个宽字符的ASCII字符转换为普通(在这种情况下,一个字符)ASCII字符。 (转化存在并且是简单的。)
顺便说一句,我不知道你是否知道,但你可以通过创建一个返回noconv为转换一个方面避免这种情况。 然后,你将不得不与宽字符的文件。
检查了这一点: 类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)