如何在Visual C++ 2008中创建一个UTF-8字符串字面量

69

在VC++ 2003中,我只需将源文件保存为UTF-8,所有字符串就会按原样使用。换句话说,以下代码将字符串按原样打印到控制台。如果源文件保存为UTF-8,则输出将为UTF-8。

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

我已经用UTF-8 BOM的格式保存了文件。但是在使用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为我编译这个的方法。请注意,在源文件中使用哪种区域设置都无关紧要。似乎没有一种区域设置可以说“我知道我在做什么,所以不要改变我的字符串文字”。特别是,无用的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在这种情况下试图太聪明了。


1
请参考新版VS的答案:https://dev59.com/8nnZa4cB1Zd3GeqPmiYX - bames53
我在VS 2012中遇到了同样的问题。 它破坏了我的UTF-8编码。为什么VS如此不稳定? 我想我会把我的数据放在一个外部文件中,这样它就不会被搞砸了。需要绕过有缺陷的编译器真是太烦人了。 另外,C++11允许将编码指定为u8"文字",但VS 2012不支持该功能,因此无济于事。 - Joe
18个回答

1
我曾经遇到过编译UTF-8窄字符(char)字符串字面量的类似问题,我发现必须同时有UTF-8 BOM和#pragma execution_character_set("utf-8")[1],或者两者都没有[2]。只使用其中之一会导致错误的转换。
我在https://github.com/jay/compiler_string_test中记录了详细信息。
[1]: Visual Studio 2012不支持execution_character_set。Visual Studio 2010和2015可以正常工作,而且如您所知,在2008年的补丁中也可以正常工作。
[2]: 本帖子中的某些评论指出,对于使用多字节本地代码页(例如日本)的开发人员,既不使用BOM也不使用pragma可能会导致错误的转换。

1
我知道我来晚了,但我认为我需要将这个内容传播开来。对于Visual C++ 2005及以上版本,如果源文件没有BOM(字节顺序标记),并且您的系统语言环境不是英语,则VC会认为您的源文件不是Unicode编码。
为了正确编译UTF-8源文件,您必须使用不带BOM的UTF-8编码保存,并且系统语言环境(非Unicode语言)必须为英语

enter image description here


不确定为什么,但似乎链接已经失效了,这里是一个新链接:https://raymai97.github.io/myblog/msvc-support-utf8-string-literal-since-vc6 - raymai97

1

也许可以尝试一个实验:

#pragma setlocale(".UTF-8")

或者:

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

0

所以,需要改变的事情。 现在我有了一个解决方案。

首先,您应该在单字节代码页本地环境下运行,例如英语,这样 cl.exe 就不会混乱获取代码。

其次,将源代码保存为 UTF8-NO BOM,请注意,是 NO-BOM,然后使用 cl.exe 进行编译,不要调用任何 C API,例如 printf wprint,所有这些都不起作用,我不知道为什么 :).... 以后可能会研究一下......

然后只需编译和运行,您就会看到结果...... 我的电子邮件是 luoyonggang,(谷歌的)希望得到一些......

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)");
}

不幸的是,我需要一个跨平台解决方案,在 Windows 上可以在非英语环境中工作,所以你的解决方案在我的使用情况下都失败了。谢谢你考虑这个问题。 - brofield
是的,那绝对是失败的:),只因为非英语字符总是采用2字节MBCS编码,比如中文。我正在尝试通过注入来钩取cl.exe,这样就可以通过读取一些环境变量进行配置了:) - lygstate

0

UTF-8源文件

  • 无BOM:被视为原始文件,除非您的系统使用>1byte/char代码页(如Shift JIS)。您需要将系统代码页更改为任何单字节代码页,然后您应该能够在文字中使用Unicode字符并且编译不会出现问题(至少我希望如此)。
  • 有BOM:在编译期间将字符和字符串文字转换为系统代码页。您可以使用GetACP()检查当前系统代码页。据我所知,没有办法将系统代码页设置为65001(UTF-8),因此没有直接使用带BOM的UTF-8的方法。

唯一可移植且与编译器无关的方法是使用ASCII字符集和转义序列,因为没有任何保证任何编译器都会接受UTF-8编码的文件。


实际上,LLVM编译器可以接受UTF-8编码的文件。它也可以被修补以允许使用UTF-8变量名(您选择的语言中的变量名!)。至于“可移植性”,自从我开始写软件以来,这一直是一个问题。 - Lloyd Sargent

0

我曾经遇到过类似的问题,解决方案是在高级保存选项中使用不带BOM的UTF8保存。


3
很不幸,这对我不起作用。我得到了编译错误,因为编译器认为源文件是Shift-JIS编码,导致字符串的解释不同。 - brofield
@brofield 我发现系统区域设置会导致编译器做出这样的假设。尝试将您的系统区域设置更改为英文。 - raymai97


-6

阅读文章。首先,您不需要UTF-8。UTF-8只是一种表示字符的方式。您需要宽字符(wchar_t)。您可以将它们写成L"yourtextgoeshere"。该文字的类型为wchar_t*。如果您很匆忙,只需查找wprintf即可。


2
我不想转换为wchar,因为我只需要将所有字符串再次转换为UTF-8。我希望VC2008保持我的字符串字面值不变。 - brofield
OP 需要 UTF-8。我不确定将其转换为 wchar 是否能解决他的问题。 - Lloyd Sargent

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接