C++ 是否将utf-16文本文件正确读入没有外部库的字符串?

C++ 是否将utf-16文本文件正确读入没有外部库的字符串?,c++,winapi,unicode,utf-16,C++,Winapi,Unicode,Utf 16,我从一开始就在使用StackOverflow,有时还想发帖提问,但我总是自己想出来,或者最终找到了答案。。。直到现在。这感觉应该相当简单,但我在互联网上徘徊了几个小时却没有成功,所以我转到这里: 我有一个非常标准的utf-16文本文件,混合了英文和中文字符。我希望这些字符以字符串结尾(从技术上讲,是wstring)。我已经看到了很多相关问题的答案(这里和其他地方),但他们要么想解决在不知道编码的情况下读取任意文件这一更难的问题,要么想在编码之间转换,要么只是对“Unicode”是一系列编码感到困

我从一开始就在使用StackOverflow,有时还想发帖提问,但我总是自己想出来,或者最终找到了答案。。。直到现在。这感觉应该相当简单,但我在互联网上徘徊了几个小时却没有成功,所以我转到这里:

我有一个非常标准的utf-16文本文件,混合了英文和中文字符。我希望这些字符以字符串结尾(从技术上讲,是wstring)。我已经看到了很多相关问题的答案(这里和其他地方),但他们要么想解决在不知道编码的情况下读取任意文件这一更难的问题,要么想在编码之间转换,要么只是对“Unicode”是一系列编码感到困惑。我知道我试图读取的文本文件的来源,它将始终是UTF16,它有一个BOM表和所有内容,并且可以保持这种方式

我一直在使用描述的解决方案,该解决方案适用于全英文的文本文件,但在遇到某些字符后,它停止读取文件。我发现的另一个建议是使用,这可能会奏效,但我真的不希望在分发应用程序中包含整个大型库,而只是在一个地方读取一个文本文件。不过,我不关心系统独立性——我只需要在Windows中编译和工作。当然,一个不依赖于这一事实的解决方案会更漂亮,但我同样乐意看到一个使用stl的解决方案,同时依赖于对Windows体系结构的假设,甚至是涉及win32函数或ATL的解决方案;我只是不想包括另一个像ICU这样的大型第三方图书馆。除非我想自己重新实现它,否则我还是完全不走运吗

编辑:我一直在为这个特定的项目使用VS2008,所以很遗憾C++11代码帮不上忙

编辑2:我意识到我以前借用过的非英语字符并没有像我想象的那样失败。相反,它在我的测试文档中的特定字符上失败,其中包括“:”(全宽冒号,U+FF1A)和“)”(全宽右括号,U+FF09)。bames53发布的解决方案也大多有效,但被同样的角色难住了

编辑3(和答案!):我一直使用的原始代码-确实-大部分工作-正如bames53帮助我发现的那样,ifstream只需要以二进制模式打开就可以工作。

C++11解决方案(据我所知,在您的平台上,Visual Studio自2010年以来就支持)将是:

#include <fstream>
#include <iostream>
#include <locale>
#include <codecvt>
int main()
{
    // open as a byte stream
    std::wifstream fin("text.txt", std::ios::binary);
    // apply BOM-sensitive UTF-16 facet
    fin.imbue(std::locale(fin.getloc(),
       new std::codecvt_utf16<wchar_t, 0x10ffff, std::consume_header>));
    // read     
    for(wchar_t c; fin.get(c); )
            std::cout << std::showbase << std::hex << c << '\n';
}
#包括
#包括
#包括
#包括
int main()
{
//作为字节流打开
std::wifstream fin(“text.txt”,std::ios::binary);
//应用BOM敏感UTF-16方面
fin.imbue(std::locale(fin.getloc(),
新标准:编解码器(VT_utf16));
//阅读
对于(wchar_t c;fin.get(c);)
标准::无法编辑:
因此,问题似乎是Windows在文本模式中将某些魔法字节序列视为文件的结尾。这可以通过使用二进制模式读取文件,
std::ifstream fin(“filename”,std::ios::binary);
,然后将数据复制到wstring中来解决



最简单的、不可移植的解决方案是将文件数据复制到wchar_________________________________________


您将很难以完全可移植的方式将UTF-16转换为特定于语言环境的wchar\t编码

这里是标准C++库中可用的Unicode转换功能(虽然VS 10和11只实现项目3, 4,而5)

  • codevt
  • codevt
  • 编解码器vt_utf8
  • 编解码器VT_utf16
  • 编解码器VT_utf8_utf16
  • C32R/mbrtoc32
  • c16rtomb/mbrtoc16
  • 每个人都做些什么

  • 始终在UTF-8和UTF-32之间转换的codecvt方面
  • 在UTF-8和UTF-16之间转换
  • 根据目标元素的大小在UTF-8和UCS-2或UCS-4之间转换(BMP之外的字符可能被截断)
  • 使用UTF-16编码方案和UCS-2或UCS-4在字符序列之间进行转换
  • 在UTF-8和UTF-16之间转换
  • 如果定义了宏
    \uuu STDC\u UTF\u 32\uu
    ,则这些函数将在当前区域设置的字符编码和UTF-32之间转换
  • 如果定义了宏
    \uu STDC\u UTF\u 16\uu
    ,则这些函数将在当前区域设置的字符编码和UTF-16之间转换
  • 如果定义了
    \uuu STDC\u ISO\u 10646\uuuu
    ,那么直接使用
    codevt\u utf16
    进行转换应该可以,因为该宏指示所有区域设置中的wchar\u t值对应于Unicode特许协议的短名称(因此意味着wchar\u t足够大,可以容纳任何此类值)

    不幸的是,没有定义直接从UTF-16到wchar\u t的内容。可以从UTF-16->UCS-4->mb(如果
    \u STDC\u UTF\u 32\u
    )->wc,但您将丢失任何无法在区域设置的多字节编码中表示的内容。当然,无论如何,从UTF-16转换为wchar\t将丢失任何无法在区域设置的wchar\t编码中表示的内容


    因此,它可能不值得移植,相反,您可以将数据读入wchar__t数组,或者使用其他Windows特定的工具,例如文件上的_O_U16TEXT模式

    这应该在任何地方构建和运行,但需要做一系列假设才能实际工作:

    #include <fstream>
    #include <sstream>
    #include <iostream>
    
    int main ()
    {
        std::stringstream ss;
        std::ifstream fin("filename");
        ss << fin.rdbuf(); // dump file contents into a stringstream
        std::string const &s = ss.str();
        if (s.size()%sizeof(wchar_t) != 0)
        {
            std::cerr << "file not the right size\n"; // must be even, two bytes per code unit
            return 1;
        }
        std::wstring ws;
        ws.resize(s.size()/sizeof(wchar_t));
        std::memcpy(&ws[0],s.c_str(),s.size()); // copy data into wstring
    }
    
    #包括
    #包括
    #包括
    int main()
    {
    std::stringstream-ss;
    std::ifstream fin(“文件名”);
    
    ss当您为UTF-16打开文件时,必须以二进制模式打开它。这是因为在文本模式下,某些字符会被特殊解释-具体地说,0x0d被完全过滤掉,0x1a标记为