在VC ++ 2003中,我可以将源文件保存为UTF-8,所有字符串都按原样使用。换句话说,以下代码将按字符串打印到控制台。如果源文件保存为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为我编译的方法。请注意,我在源文件中使用的区域设置无关紧要。似乎没有一个语言环境说“我知道我在做什么,所以不要f $%## ng更改我的字符串文字”。特别是,无用的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 ...”,因为相同的源代码是使用gcc编译的,可以很好地处理UTF-8文件。
有没有办法指定源文件的编译应该保持字符串文字不变?
以不同的方式询问,在编译源文件时,我可以使用哪些编译标志来指定与VC2003的向后兼容性。即,不要更改字符串文字,按字节逐字节使用它们。
更新
感谢您的建议,但我想避免使用wchar。由于这个应用程序专门处理UTF-8中的字符串,因此使用wchar会要求我将所有字符串转换回UTF-8,这应该是不必要的。所有输入,输出和内部处理均采用UTF-8格式。这是一个简单的应用程序,在Linux和使用VC2003编译时工作正常。我希望能够使用VC2008编译相同的应用程序并使其工作。
为此,我需要VC2008不要尝试将其转换为本地机器的语言环境(日语,932)。我希望VC2008向后兼容VC2003。我想要一个区域设置或编译器设置,表明字符串按原样使用,基本上是不透明的char数组或UTF-8。看起来我可能会被VC2003和gcc困住,但VC2008在这个例子中试图太聪明了。
答案 0 :(得分:32)
更新
我认为没有保证可以做到这一点。我在下面介绍的解决方案适用于英文版VC2003,但在使用日文版VC2003(或者可能是日文操作系统)进行编译时失败。无论如何,它不能依赖于工作。请注意,即使将所有内容声明为L“”字符串也不起作用(并且如下所述在gcc中很痛苦)。
相反,我相信你只需要咬住子弹并将所有文本移动到数据文件中并从那里加载它。我现在通过SimpleIni(跨平台的INI文件库)存储和访问INI文件中的文本。至少可以保证它在所有文本都不在程序之外。
原件:
我自己回答这个问题,因为只有Evan才明白这个问题。关于Unicode是什么以及如何使用wchar_t的答案与此问题无关,因为这不是关于国际化,也不是对Unicode,字符编码的误解。我感谢您尝试提供帮助,但如果我不够清楚,请道歉。
问题是我有源文件需要在各种平台和编译器下进行交叉编译。该程序执行UTF-8处理。它不关心任何其他编码。我想在UTF-8中使用字符串文字,就像目前使用gcc和vc2003一样。我如何用VC2008做到这一点? (即向后兼容的解决方案)。
这是我发现的:
gcc(v4.3.2 20081105):
VC2003:
VC2005 +:
因此,简单的答案是,为了这个特殊目的,VC2005 +被破坏并且不提供向后兼容的编译路径。将Unicode字符串输入编译程序的唯一方法是通过UTF-8 + BOM + wchar,这意味着我需要在使用时将所有字符串转换回UTF-8。
没有任何简单的跨平台方法将wchar转换为UTF-8,例如,wchar的大小和编码是什么?在Windows上,UTF-16。在其他平台上?它有所不同。有关详细信息,请参阅ICU project。
最后我决定在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和vc2008的测试中干净地编译并正常工作:
std::string mText;
mText = utf8("Chinese (Traditional)");
mText = utf8("中国語 (繁体)");
mText = utf8("중국어 (번체)");
mText = utf8("Chinês (Tradicional)");
答案 1 :(得分:16)
尽管使用宽字符串然后根据需要转换为UTF-8可能更好。我认为你最好的选择是如你所提到的那样在字符串中使用十六进制转义符。就像假设您想要代码点\uC911
一样,您可以这样做。
const char *str = "\xEC\xA4\x91";
我相信这会很好用,只是不太可读,所以如果你这样做,请评论它解释。
答案 2 :(得分:16)
Brofield,
我遇到了完全相同的问题,只是偶然发现了一个不需要将源字符串转换为宽字符并返回的解决方案:将源文件保存为UTF-8 ,不带签名,VC2008将不要管它。当我想出丢掉签名时工作得很好。总结一下:
Unicode(没有签名的UTF-8) - 代码页65001,不会在VC2008中抛出c4566警告并且不会导致VC混乱编码,而代码页65001(带签名的UTF-8)会抛出c4566(正如你所发现的那样)。
希望现在为您提供帮助并不会太晚,但它可能会加快您的VC2008应用程序移除您的解决方法。
答案 3 :(得分:14)
文件/高级保存选项/编码:“Unicode(UTF-8 无签名) - 代码页65001”
答案 4 :(得分:8)
Visual C ++(2005+)源文件的COMPILER标准行为是:
"Ä"
→C4 00
'Ä'
→C4
L"Ä"
→00C4 0000
L'Ä'
→00C4
"Ä"
→C3 84 00
(= UTF-8)'Ä'
→警告:多字符常量"Ω"
→E2 84 A6 00
(= UTF-8,正如所料)L"A"
→00C3 0084 0000
(错误!)L'Ä'
→警告:多字符常量L"Ω"
→00E2 0084 00A6 0000
(错误!)"Ä"
→C4 00
(= CP1252,不再是UTF-8),'Ä'
→C4
"Ω"
→错误:无法转换为CP1252!L"Ä"
→00C4 0000
(正确)L'Ä'
→00C4
L"Ω"
→2126 0000
(正确)你看,C编译器处理没有BOM的UTF-8文件的方式与CP1252相同。因此,编译器不可能将UTF-8和UTF-16字符串混合到编译输出中!所以你必须决定一个源代码文件:
L
前缀),L
前缀)。L
前缀独立地,EDITOR可以自动检测没有BOM的UTF-8文件作为UTF-8文件。
答案 5 :(得分:6)
从评论到这篇非常好的博客
“使用UTF-8作为C和C ++中使用Visual Studio的字符串的内部表示”
=> http://www.nubaria.com/en/blog/?p=289
#pragma execution_character_set("utf-8")
它需要Visual Studio 2008 SP1和以下修补程序:
答案 6 :(得分:4)
这个怎么样?将字符串存储在UTF-8编码文件中,然后将它们预处理为ASCII编码的C ++源文件。您可以使用十六进制转义符将UTF-8编码保留在字符串中。字符串
"中国語 (繁体)"
转换为
"\xE4\xB8\xAD\xE5\x9B\xBD\xE8\xAA\x9E (\xE7\xB9\x81\xE4\xBD\x93)"
当然,任何人都无法理解这一点,其目的只是为了避免编译器出现问题。
您可以使用C ++预处理器来引用转换后的头文件中的字符串,也可以在编译之前将整个UTF-8源转换为ASCII。
答案 7 :(得分:3)
使用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;
}
我相信这应该可以在任何平台上运行,但除了我自己的系统之外,我还无法测试它,所以它可能有bug。
#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;
}
答案 8 :(得分:1)
也许尝试一下实验:
#pragma setlocale(".UTF-8")
或:
#pragma setlocale("english_england.UTF-8")
答案 9 :(得分:1)
我有类似的问题。我的UTF-8字符串文字在编译期间被转换为当前的系统代码页 - 我只是在hex-viewer中打开了.obj文件,它们已经被破坏了。例如,字符ć只是一个字节。
我的解决方案是以UTF-8保存并且没有BOM。这就是我欺骗编译器的方式。它现在认为这只是一个普通的来源,并不会翻译字符串。在.obj文件中ć现在是两个字节。
请忽略一些评论员。我理解你想要的东西 - 我也想要同样的东西:UTF-8源,UTF-8生成的文件,UTF-8输入文件,通过通信线路的UTF-8而不用翻译。
也许这有帮助...
答案 10 :(得分:1)
我知道我迟到了,但我认为我需要spread this out。对于Visual C ++ 2005及更高版本,如果源文件不包含BOM(字节顺序标记),并且您的系统区域设置不是英语,VC将假定您的源文件不是Unicode。
要正确编译UTF-8源文件,必须以UTF-8保存而不使用BOM 编码,并且系统区域设置(非Unicode语言)必须为英语强>
答案 11 :(得分:0)
现在有一个 /utf-8
编译器命令行选项。
要在 Visual Studio 开发环境中设置此编译器选项:
打开项目属性页对话框。
选择 Configuration Properties
-> C/C++
-> Command Line
属性页。
在 Additional Options
中,添加 /utf-8
选项以指定您的首选编码。
选择“确定”以保存更改。
答案 12 :(得分:0)
我遇到了类似的问题,解决方案是使用高级保存选项保存在UTF8中而不是使用高级保存选项
答案 13 :(得分:0)
我有一个类似的问题,编译UTF-8窄(字符)字符串文字,我发现基本上我必须同时拥有UTF-8 BOM和#pragma execution_character_set("utf-8")
[1],或者既不是BOM也不是实用主义[2]。使用一个没有另一个导致不正确的转换。
我在https://github.com/jay/compiler_string_test
处记录了详细信息[1]:Visual Studio 2012不支持execution_character_set。 Visual Studio 2010和2015它运行良好,如你所知,2008年的补丁程序运行正常。
[2]:本主题中的一些评论指出,既不使用BOM也不使用pragma可能会导致使用多字节本地代码页(例如日本)的开发人员进行不正确的转换。
答案 14 :(得分:0)
UTF-8源文件
唯一可移植且独立于编译器的方法是使用ASCII字符集和转义序列,因为无法保证任何编译器都接受UTF-8编码文件。
答案 15 :(得分:0)
所以,要改变的事情。 现在我得到了一个解决方案。
首先,你应该在Single Byte Code Page Local(如英语)下运行,这样cl.exe就不会让代码变得混乱。
其次,将源代码保存为UTF8-NO BOM,请注意,NO-BOM,然后编译 cl.exe,不要调用任何C API,比如,printf wprint,所有那些工作人员都不工作,我不知道为什么:)....可能以后有一个研究......
然后只需编译并运行,您将看到结果..... 我的电子邮件是罗永刚,(谷歌的)希望有些......
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)");
}
答案 16 :(得分:-3)
我同意Theo Vosse。阅读The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)上的文章Joel On Software ...
答案 17 :(得分:-6)
阅读文章。首先,你不需要UTF-8。 UTF-8只是一种表示字符的方式。你想要宽字符(wchar_t)。你把它们写成L“yourtextgoeshere”。该文字的类型是wchar_t *。如果你赶时间,请查看wprintf。