Как создать строковый литерал UTF-8 в Visual C ++ 2008 - PullRequest
61 голосов
/ 27 марта 2009

В VC ++ 2003 я мог просто сохранить исходный файл как UTF-8, и все строки использовались как есть. Другими словами, следующий код будет выводить строки как есть на консоль. Если исходный файл был сохранен как UTF-8, тогда вывод будет UTF-8.

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

Я сохранил файл в формате UTF-8 с помощью спецификации 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 скомпилировать это для меня. Обратите внимание, что не имеет значения, какую локаль я использую в исходном файле. Кажется, не существует локали, которая говорит: «Я знаю, что я делаю, поэтому не меняйте мои строковые литералы». В частности, бесполезная псевдо-локаль 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. Я не хочу изменять файл для использования escape-строк, таких как "\ 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 в этом случае пытается быть слишком умным.

Ответы [ 17 ]

1 голос
/ 18 декабря 2009

У меня была похожая проблема. Мои строковые литералы UTF-8 были преобразованы в текущую системную кодовую страницу во время компиляции - я просто открыл файлы .obj в программе просмотра в шестнадцатеричном формате, и они уже были искажены. Например, символ ć был всего одним байтом.

Решением для меня было сохранение в UTF-8 и без спецификации. Вот как я обманул компилятор. Теперь он думает, что это просто нормальный источник, и не переводит строки. В файлах .obj ć теперь составляет два байта.

Не обращайте внимания на некоторых комментаторов, пожалуйста. Я понимаю, что вы хотите - я хочу того же: источник UTF-8, сгенерированные файлы UTF-8, входные файлы UTF-8, UTF-8 по линиям связи без перевода.

Может быть, это помогает ...

0 голосов
/ 09 апреля 2013

UTF-8 исходных файлов

  • Без спецификации : обрабатываются как необработанные, за исключением случаев, когда ваша система использует кодовую страницу размером> 1 байт / символ (например, Shift JIS). Вам нужно изменить системную кодовую страницу на любой однобайтовый, и тогда вы сможете использовать символы Юникода внутри литералов и компилировать без проблем (по крайней мере, я надеюсь).
  • С спецификацией : преобразовывают ли они символьные и строковые литералы в системную кодовую страницу во время компиляции. Вы можете проверить текущую системную кодовую страницу с помощью GetACP (). AFAIK, нет способа установить системную кодовую страницу на 65001 (UTF-8), поэтому нет возможности использовать UTF-8 напрямую с BOM.

Единственный переносимый и независимый от компилятора способ - использовать кодировку ASCII и escape-последовательности, поскольку нет никаких гарантий, что какой-либо компилятор примет файл в кодировке UTF-8.

0 голосов
/ 08 июля 2011

Итак, все должно быть изменено. Теперь у меня есть решение.

Прежде всего, вы должны работать под локальной однобайтовой кодовой страницей, такой как английский, чтобы cl.exe не получал коды, становящиеся хаосом.

Во-вторых, сохраните исходный код в UTF8-NO BOM, обратите внимание, NO-BOM, а затем скомпилируйте с cl.exe, НЕ вызывайте никакой C API, такой как printf wprint, весь этот персонал не работает, я не знаю, почему:) .... возможно, позже проведем исследование ...

Тогда просто скомпилируйте и запустите, вы увидите результат ..... моя электронная почта luoyonggang, (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)");
}
0 голосов
/ 29 марта 2009

Читайте статьи. Во-первых, вы не хотите UTF-8. UTF-8 - это только способ представления символов. Вы хотите широкие символы (wchar_t). Вы записываете их как L "yourtextgoeshere". Тип этого литерала - wchar_t *. Если вы спешите, просто посмотрите wprintf.

0 голосов
/ 08 декабря 2017

У меня была похожая проблема при компиляции узких (char) строковых литералов 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 не поддерживает execute_character_set. Visual Studio 2010 и 2015 работает нормально, и, как вы знаете, с патчем 2008 года он работает отлично.

[2]: В некоторых комментариях в этой теме отмечалось, что использование ни спецификации, ни прагмы может привести к неправильному преобразованию для разработчиков, использующих многобайтовую локальную кодовую страницу (например, в Японии).

0 голосов
/ 28 марта 2009

У меня была похожая проблема, решение состояло в том, чтобы сохранить в UTF8 без бума, используя расширенные опции сохранения

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...