如何在Visual C ++ 2008中创build一个UTF-8string文字

在VC ++ 2003中,我可以将源文件保存为UTF-8,并且所有string均按原样使用。 换句话说,下面的代码将原样打印到控制台。 如果源文件保存为UTF-8,那么输出将是UTF-8。

printf("Chinese (Traditional)"); printf("中国語 (繁体)"); printf("중국어 (번체)"); printf("Chinês (Tradicional)"); 

我用UTF-8 BOM保存了UTF-8格式的文件。 但是使用VC2008编译的结果是:

 warning C4566: character represented by universal-character-name '\uC911' cannot be represented in the current code page (932) warning C4566: character represented by universal-character-name '\uAD6D' cannot be represented in the current code page (932) etc. 

导致这些警告的字符已损坏。 符合语言环境(在这种情况下,932 =日语)的转换为语言环境编码,即Shift-JIS。

我找不到一个方法让VC ++ 2008为我编译这个。 请注意,在源文件中使用的语言环境无关紧要。 似乎没有一个地方说“我知道我在做什么,所以不要改变我的string文字”。 特别是,无用的UTF-8伪区域不起作用。

 #pragma setlocale(".65001") => error C2175: '.65001' : invalid locale 

“C”也没有

 #pragma setlocale("C") => see warnings above (in particular locale is still 932) 

看来,VC2008强制所有字符到指定的(或默认)区域设置,该区域设置不能是UTF-8。 我不想改变文件使用像“\ xbf \ x11 …”这样的转义string,因为使用gcc编译了相同的源码,可以非常高兴地处理UTF-8文件。

有什么办法可以指定源文件的编译应该保持string文字不变吗?

换个angular度来说,在编译源文件时,可以使用哪些编译标志来指定与VC2003的向后兼容性。 即不要更改string文字,使用它们字节的字节。

更新

感谢您的build议,但我想避免wchar。 由于这个应用程序仅处理UTF-8中的string,因此使用wchar将要求我将所有string转换回UTF-8,这是不必要的。 所有input,输出和内部处理都是UTF-8。 这是一个简单的应用程序,在Linux上编译并使用VC2003进行编译时可以正常工作。 我想能够与VC2008编译相同的应用程序,并使其工作。

为此,我需要VC2008不尝试将其转换为本地计算机的语言环境(日语,932)。 我想让VC2008向后兼容VC2003。 我想要一个语言环境或编译器设置,说的string是原样使用,实质上是不透明的字符数组,或作为UTF-8。 看起来我可能会被困在VC2003和GCC中,VC2008在这个例子中是太聪明了。

更新:

我已经决定没有保证的方法来做到这一点。 下面我介绍的解决scheme适用于英文版VC2003,但使用日文版VC2003(或者也可能是日文版)编译时会失败。 无论如何,都不能依靠工作。 请注意,即使将所有内容都声明为L“”string也不起作用(并且在gcc中是痛苦的,如下所述)。

相反,我相信你只需要咬紧牙关,将所有文本移动到一个数据文件并从那里加载。 我现在通过SimpleIni (跨平台INI文件库)存储和访问INI文件中的文本。 至less有一个保证,因为所有的文本都不在程序中。

原版的:

我正在回答这个问题,因为只有埃文才明白这个问题。 有关Unicode是什么以及如何使用wchar_t的答案与这个问题无关,因为这不是国际化,也不是Unicode字符编码的误解。 如果我不够清楚,我很感激你的帮助。

问题是我有源文件需要在各种平台和编译器下交叉编译。 该程序执行UTF-8处理。 它不关心任何其他编码。 我想用UTF-8编写string文字,就像当前使用gcc和vc2003一样。 我如何使用VC2008? (即向后兼容的解决scheme)。

这是我发现的:

gcc(v4.3.2 20081105):

  • string文字是原样使用(原始string)
  • 支持UTF-8编码的源文件
  • 源文件不能有UTF-8 BOM

VC2003:

  • string文字是原样使用(原始string)
  • 支持UTF-8编码的源文件
  • 源文件可能有也可能没有UTF-8 BOM(没关系)

VC2005 +:

  • string文字是由编译器(没有原始string)
  • charstring文字被重新编码为指定的语言环境
  • UTF-8不支持作为目标语言环境
  • 源文件必须具有UTF-8 BOM

所以,简单的答案是,为了这个特定的目的,VC2005 +被打破,不提供向后兼容的编译path。 将Unicodestring导入编译的程序的唯一方法是通过UTF-8 + BOM + wchar,这意味着我需要在使用时将所有string转换回UTF-8。

没有任何简单的将wchar转换为UTF-8的跨平台方法,例如,wchar的大小和编码是什么? 在Windows上,UTF-16。 在其他平台上? 它有所不同。 有关详细信息,请参阅ICU项目 。

最后,我决定,除了vc2005 +之外的所有编译器,我将避免使用源代码如下所示的转换成本。

 #if defined(_MSC_VER) && _MSC_VER > 1310 // Visual C++ 2005 and later require the source files in UTF-8, and all strings // to be encoded as wchar_t otherwise the strings will be converted into the // local multibyte encoding and cause errors. To use a wchar_t as UTF-8, these // strings then need to be convert back to UTF-8. This function is just a rough // example of how to do this. # define utf8(str) ConvertToUTF8(L##str) const char * ConvertToUTF8(const wchar_t * pStr) { static char szBuf[1024]; WideCharToMultiByte(CP_UTF8, 0, pStr, -1, szBuf, sizeof(szBuf), NULL, NULL); return szBuf; } #else // Visual C++ 2003 and gcc will use the string literals as is, so the files // should be saved as UTF-8. gcc requires the files to not have a UTF-8 BOM. # define utf8(str) str #endif 

请注意,这个代码只是一个简单的例子。 生产使用将需要以各种方式清理它(线程安全,错误检查,缓冲区大小检查等)。

这用于像下面的代码。 它编译干净,在我的gcc,vc2003和vc2008testing中正确工作:

 std::string mText; mText = utf8("Chinese (Traditional)"); mText = utf8("中国語 (繁体)"); mText = utf8("중국어 (번체)"); mText = utf8("Chinês (Tradicional)"); 

虽然使用宽string可能会更好,然后根据需要转换为UTF-8。 我认为你最好的办法就是像你所说的那样在string中使用hex的转义字符。 就像你想要的代码点\uC911 ,你可以做到这一点。

 const char *str = "\xEC\xA4\x91"; 

我相信这会工作得很好,只是不太可读,所以如果你这样做,请评论它来解释。

Brofield,

我有完全相同的问题,只是偶然发现了一个解决scheme,不需要将您的源string转换为宽字符,并返回:将源文件保存为UTF-8, 无需签名,VC2008将离开它。 工作很好,当我想出放弃签名。 总结一下:

Unicode(没有签名的UTF-8) – Codepage 65001,不会在VC2008中抛出c4566警告,并且不会导致VC弄乱编码,而Codepage 65001(UTF-8 With Signature)却抛出了c4566find)。

希望这不是太晚,以帮助您,但它可能会加快您的VC2008应用程序删除您的解决方法。

文件/高级保存选项/编码:“Unicode(UTF-8 无签名 ) – Codepage 65001”

Visual C ++(2005+)编译器对源文件的标准行为是:

  • CP1252(本例为西欧代码页):
    • "Ä"C4 00
    • 'Ä'C4
    • L"Ä"00C4 0000
    • L'Ä'00C4
  • 没有BOM的UTF-8:
    • "Ä"C3 84 00 (= UTF-8)
    • 'Ä' →警告:多字符常量
    • "Ω"E2 84 A6 00 (= UTF-8,如预期的那样)
    • L"A"00C3 0084 0000 (错!)
    • L'Ä' →警告:多字符常量
    • L"Ω"00E2 0084 00A6 0000 (错!)
  • 带有BOM的UTF-8:
    • "Ä"C4 00 (= CP1252,不再有UTF-8),
    • 'Ä'C4
    • "Ω" →错误:不能转换为CP1252!
    • L"Ä"00C4 0000 (正确)
    • L'Ä'00C4
    • L"Ω"2126 0000 (正确)

您可以看到,C编译器以与CP1252相同的方式处理没有BOM的UTF-8文件。 因此,编译器不可能将UTF-8和UTF-16string混合到编译输出中! 所以你必须决定一个源代码文件:

  • 或者使用带有BOM的UTF-8并且只生成UTF-16string(即总是使用L前缀),
  • 没有BOM的UTF-8,只生成UTF-8string(即从不使用L前缀)。
  • 7位ASCII字符不涉及,可以使用有或没有L前缀

独立地,EDITOR可以自动检测没有BOM的UTF-8文件作为UTF-8文件。

从评论到这个非常漂亮的博客
“使用UTF-8作为Visual Studio中C和C ++string的内部表示”
=> http://www.nubaria.com/en/blog/?p=289

 #pragma execution_character_set("utf-8") 

它需要Visual Studio 2008 SP1和以下修补程序:

http://support.microsoft.com/kb/980263 ….

这个怎么样? 您将string存储在UTF-8编码文件中,然后将其预处理为ASCII编码的C ++源文件。 您使用hex转义string保留UTF-8编码。 string

 "中国語 (繁体)" 

被转换成

 "\xE4\xB8\xAD\xE5\x9B\xBD\xE8\xAA\x9E (\xE7\xB9\x81\xE4\xBD\x93)" 

当然这是任何人都无法读懂的,目的只是为了避免编译器的问题。

您可以使用C ++预处理器来引用已转换头文件中的string,也可以在编译之前使用此技巧将整个UTF-8源代码转换为ASCII。

使用char_traits :: widen()直接转换来自任何本地编码的便携式转换。

 #include <locale> #include <string> #include <vector> ///////////////////////////////////////////////////////// // NativeToUtf16 - Convert a string from the native // encoding to Unicode UTF-16 // Parameters: // sNative (in): Input String // Returns: Converted string ///////////////////////////////////////////////////////// std::wstring NativeToUtf16(const std::string &sNative) { std::locale locNative; // The UTF-16 will never be longer than the input string std::vector<wchar_t> vUtf16(1+sNative.length()); // convert std::use_facet< std::ctype<wchar_t> >(locNative).widen( sNative.c_str(), sNative.c_str()+sNative.length(), &vUtf16[0]); return std::wstring(vUtf16.begin(), vUtf16.end()); } 

从理论上讲,从UTF-16到UTF-8的回程应该是一样的容易,但是我发现UTF-8语言环境在我的系统(Win7上的VC10 Express)上不能正常工作。

于是我写了一个基于RFC 3629的简单转换器。

 ///////////////////////////////////////////////////////// // Utf16ToUtf8 - Convert a character from UTF-16 // encoding to UTF-8. // NB: Does not handle Surrogate pairs. // Does not test for badly formed // UTF-16 // Parameters: // chUtf16 (in): Input char // Returns: UTF-8 version as a string ///////////////////////////////////////////////////////// std::string Utf16ToUtf8(wchar_t chUtf16) { // From RFC 3629 // 0000 0000-0000 007F 0xxxxxxx // 0000 0080-0000 07FF 110xxxxx 10xxxxxx // 0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx // max output length is 3 bytes (plus one for Nul) unsigned char szUtf8[4] = ""; if (chUtf16 < 0x80) { szUtf8[0] = static_cast<unsigned char>(chUtf16); } else if (chUtf16 < 0x7FF) { szUtf8[0] = static_cast<unsigned char>(0xC0 | ((chUtf16>>6)&0x1F)); szUtf8[1] = static_cast<unsigned char>(0x80 | (chUtf16&0x3F)); } else { szUtf8[0] = static_cast<unsigned char>(0xE0 | ((chUtf16>>12)&0xF)); szUtf8[1] = static_cast<unsigned char>(0x80 | ((chUtf16>>6)&0x3F)); szUtf8[2] = static_cast<unsigned char>(0x80 | (chUtf16&0x3F)); } return reinterpret_cast<char *>(szUtf8); } ///////////////////////////////////////////////////////// // Utf16ToUtf8 - Convert a string from UTF-16 encoding // to UTF-8 // Parameters: // sNative (in): Input String // Returns: Converted string ///////////////////////////////////////////////////////// std::string Utf16ToUtf8(const std::wstring &sUtf16) { std::string sUtf8; std::wstring::const_iterator itr; for (itr=sUtf16.begin(); itr!=sUtf16.end(); ++itr) sUtf8 += Utf16ToUtf8(*itr); return sUtf8; } 

我相信这应该可以在任何平台上工作,但除了在我自己的系统上,我还没有能够testing它,所以它可能有错误。

 #include <iostream> #include <fstream> int main() { const char szTest[] = "Das tausendschöne Jungfräulein,\n" "Das tausendschöne Herzelein,\n" "Wollte Gott, wollte Gott,\n" "ich wär' heute bei ihr!\n"; std::wstring sUtf16 = NativeToUtf16(szTest); std::string sUtf8 = Utf16ToUtf8(sUtf16); std::ofstream ofs("test.txt"); if (ofs) ofs << sUtf8; return 0; } 

也许尝试一下实验:

 #pragma setlocale(".UTF-8") 

要么:

 #pragma setlocale("english_england.UTF-8") 

我有一个类似的问题。 我的UTF-8string文字在编译过程中被转换成了当前的系统代码页 – 我只是在一个hex查看器中打开了.obj文件,而且这些文件已经被破坏了。 例如,字符只是一个字节。

我的解决scheme是保存在UTF-8和没有BOM。 这就是我欺骗编译器。 它现在认为这只是一个正常的来源,并不翻译string。 在.obj文件中,现在是两个字节。

请忽略一些评论员。 我明白你想要什么 – 我也想要同样的东西:UTF-8源,UTF-8生成的文件,UTF-8input文件,UTF-8在通信线路上没有任何翻译。

也许这有助于…

我知道我迟到了,但是我想我需要把这件事情分散开来 。 对于Visual C ++ 2005及更高版本,如果源文件不包含BOM(字节顺序标记),并且系统区域设置不是英文,则VC将假定您的源文件不是Unicode格式。

要正确编译UTF-8源文件,必须以不使用BOM编码的UTF-8格式保存, 系统区域设置(非Unicode语言)必须为英文

在这里输入图像描述

我有一个类似的问题,解决scheme是使用先进的保存选项,以UTF8保存

所以,事情要改变。 现在我有一个解决scheme。

首先,你应该在单字节代码页本地下运行,比如说英文,这样cl.exe就不会让代码变得混乱。

其次,将源代码保存在UTF8-NO BOM中,请注意,NO-BOM,然后用cl.exe进行编译,不要调用任何C API,比如printf,所有这些工作人员都不工作,不知道为什么:)….稍后可能有研究…

然后编译运行,你会看到结果…..我的电子邮件是罗永刚,(Google的)希望有一些……

WScript的:

 #! /usr/bin/env python # encoding: utf-8 # Yonggang Luo # the following two variables are used by the target "waf dist" VERSION='0.0.1' APPNAME='cc_test' top = '.' import waflib.Configure def options(opt): opt.load('compiler_c') def configure(conf): conf.load('compiler_c') conf.check_lib_msvc('gdi32') conf.check_libs_msvc('kernel32 user32') def build(bld): bld.program( features = 'c', source = 'chinese-utf8-no-bom.c', includes = '. ..', cflags = ['/wd4819'], target = 'myprogram', use = 'KERNEL32 USER32 GDI32') 

运行脚本run.bat

 rd /s /q build waf configure build --msvc_version "msvc 6.0" build\myprogram rd /s /q build waf configure build --msvc_version "msvc 9.0" build\myprogram rd /s /q build waf configure build --msvc_version "msvc 10.0" build\myprogram 

源代码main.c:

 //encoding : utf8 no-bom #include <stdio.h> #include <string.h> #include <Windows.h> char* ConvertFromUtf16ToUtf8(const wchar_t *wstr) { int requiredSize = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, 0, 0, 0, 0); if(requiredSize > 0) { char *buffer = malloc(requiredSize + 1); buffer[requiredSize] = 0; WideCharToMultiByte(CP_UTF8, 0, wstr, -1, buffer, requiredSize, 0, 0); return buffer; } return NULL; } wchar_t* ConvertFromUtf8ToUtf16(const char *cstr) { int requiredSize = MultiByteToWideChar(CP_UTF8, 0, cstr, -1, 0, 0); if(requiredSize > 0) { wchar_t *buffer = malloc( (requiredSize + 1) * sizeof(wchar_t) ); printf("converted size is %d 0x%x\n", requiredSize, buffer); buffer[requiredSize] = 0; MultiByteToWideChar(CP_UTF8, 0, cstr, -1, buffer, requiredSize); printf("Finished\n"); return buffer; } printf("Convert failed\n"); return NULL; } void ShowUtf8LiteralString(char const *name, char const *str) { int i = 0; wchar_t *name_w = ConvertFromUtf8ToUtf16(name); wchar_t *str_w = ConvertFromUtf8ToUtf16(str); printf("UTF8 sequence\n"); for (i = 0; i < strlen(str); ++i) { printf("%02x ", (unsigned char)str[i]); } printf("\nUTF16 sequence\n"); for (i = 0; i < wcslen(str_w); ++i) { printf("%04x ", str_w[i]); } //Why not using printf or wprintf? Just because they do not working:) MessageBoxW(NULL, str_w, name_w, MB_OK); free(name_w); free(str_w); } int main() { ShowUtf8LiteralString("English english_c", "Chinese (Traditional)"); ShowUtf8LiteralString("简体 s_chinese_c", "你好世界"); ShowUtf8LiteralString("繁体 t_chinese_c", "中国語 (繁体)"); ShowUtf8LiteralString("Korea korea_c", "중국어 (번체)"); ShowUtf8LiteralString("What? what_c", "Chinês (Tradicional)"); } 

UTF-8源文件

  • 没有BOM :除非系统使用> 1byte / char代码页(如Shift JIS),否则视为原始数据。 您需要将系统代码页更改为任何单个字节,然后您应该能够在文字中使用Unicode字符,并且可以毫无问题地进行编译(至less我希望)。
  • 使用BOM :在编译期间,他们将字符和string文字转换为系统代码页。 您可以使用GetACP()检查当前的系统代码页。 AFAIK,没有办法将系统代码页设置为65001(UTF-8),因此没有办法直接使用UTF-8和BOM。

唯一可移植和独立于编译器的方式是使用ASCII字符集和转义序列,因为不能保证任何编译器都能接受UTF-8编码的文件。

我同意Theo Vosse。 阅读文章绝对最小每个软件开发人员绝对,积极必须知道Unicode和字符集(无借口!) 乔尔软件 …

阅读文章。 首先,你不需要UTF-8。 UTF-8只是表示字符的一种方式。 你想要宽字符(wchar_t)。 你把它们写成L“yourtextgoeshere”。 该文字的types是wchar_t *。 如果你匆忙,只要查找wprintf。