为什么C++中的宽文件流默认会缩小写入数据?

Ara*_*raK 18 c++ unicode file wofstream

老实说,我只是没有在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!
}
Run Code Online (Sandbox Code Playgroud)

我知道这与标准有关codecvt.还有codecvtutf8Boost.此外,还有一个codecvt用于utf16马丁·约克在这里SO.现在的问题是,为什么standard codecvt转换宽字符?为什么不按原样写人物!

另外,我们是否会unicode streams使用C++ 0x或者我在这里遗漏了什么?

Éri*_*ant 13

对于第一个问题非常局部答案:文件字节左右的序列,其中当处理wchar_t的,至少一些之间的转换wchar_tchar必须发生."智能地"进行这种转换需要知道字符编码,因此这就是为什么允许这种转换依赖于语言环境,因为在流的语言环境中使用了一个方面.

然后,问题是如何在标准所要求的唯一区域设置中进行转换:"经典"转换.对此没有"正确"的答案,因此标准对此非常模糊.我从你的问题中了解到,你认为在wchar_t []和char []之间盲目地(或memcpy() - )会是一个好方法.这不是不合理的,事实上在某些实现中是(或至少是)完成的.

另一个POV是,因为codecvt是一个locale facet,所以有理由期望使用"locale的编码"进行转换(我在这里手写,因为概念非常模糊).例如,可以预期土耳其语语言环境使用ISO-8859-9或日语使用Shift JIS.通过相似性,"经典"语言环境将转换为此"语言环境的编码".显然,微软选择简单修剪(如果我们假设wchar_t代表UTF-16并且我们保持在基本的多语言平面中,则导致IS-8859-1 ),而我知道的Linux实现决定坚持使用ASCII.

对于你的第二个问题:

另外,我们是否会使用C++ 0x获得真正的unicode流,或者我在这里遗漏了什么?

在n2857的[locale.codecvt]部分(我手头的最新C++ 0x草案)中,可以读到:

专业化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流"的含义.


APr*_*mer 7

C++用于charsets的模型继承自C,因此可以追溯到至少1989年.

两个要点:

  • IO是以char为单位完成的.
  • 区域设置的工作是确定字符串序列化的宽度
  • 默认语言环境(名为"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";
    }
}
Run Code Online (Sandbox Code Playgroud)

它使用环境语言环境并将代码0x00FF的宽字符输出到文件.如果我要求使用"C"语言环境,我会得到

$ env LC_ALL=C ./a.out
Output failed
Run Code Online (Sandbox Code Playgroud)

语言环境无法处理宽字符,并且在IO失败时我们会收到问题通知.如果我运行问一个UTF-8语言环境,我明白了

$ env LC_ALL=en_US.utf8 ./a.out
$ od -t x1 test.dat
0000000 c3 bf 0a
0000003
Run Code Online (Sandbox Code Playgroud)

(od -t x1只是转储以十六进制表示的文件),正是我对UTF-8编码文件的期望.

  • 区域设置名称不是标准化的......所以你需要找出你是否有一个utf-8语言环境,它的名称是什么以及如何设置它(在posix下,在shell中运行locale -a会给你一个列表).我现在没有时间找出对宽字符的"C"语言环境有什么限制 - 我想这是实现定义的. (3认同)