Подтвердить что ты не робот

Как создать строковый литерал UTF-8 в Visual С++ 2008

В VС++ 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.

Я не могу найти способ заставить VС++ 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 при компиляции исходного файла. то есть не изменяйте строковые литералы, используйте их для байта, как они есть.

Update

Спасибо за предложения, но я хочу избежать wchar. Поскольку это приложение имеет дело только с строками в UTF-8, использование wchar потребует от меня преобразовать все строки обратно в UTF-8, что не должно быть ненужным. Вся входная, выходная и внутренняя обработка находится в UTF-8. Это простое приложение, которое отлично работает как в Linux, так и при компиляции с VC2003. Я хочу, чтобы иметь возможность компилировать то же приложение с VC2008 и работать.

Чтобы это произошло, мне нужно, чтобы VC2008 не пытался преобразовать его в локальный локальный язык (японский, 932). Я хочу, чтобы VC2008 был обратно совместим с VC2003. Я хочу установить локаль или компилятор, который говорит, что строки используются как есть, по существу, как непрозрачные массивы char, или как UTF-8. Похоже, я мог застрять в VC2003 и gcc, хотя VC2008 пытается быть слишком умным в этом случае.

4b9b3361

Ответ 1

Update:

Я решил, что нет гарантированного способа сделать это. Решение, которое я приводил ниже, работает для английской версии VC2003, но не удается при компиляции с японской версией VC2003 (или, возможно, это японская ОС). В любом случае, это не может зависеть от работы. Обратите внимание, что даже объявление всего, поскольку L "" строки не работают (и это болезненно в gcc, как описано ниже).

Вместо этого я считаю, что вам просто нужно укусить пулю и переместить весь текст в файл данных и загрузить его оттуда. Теперь я сохраняю и получаю доступ к тексту в файлах INI через SimpleIni (кросс-платформенная библиотека INI файлов). По крайней мере, есть гарантия, что он работает, поскольку весь текст выходит из программы.

Оригинал:

Я отвечаю на это сам, потому что только Эван, казалось, понял проблему. Ответы на вопрос о том, что такое Unicode и как использовать wchar_t, не имеют отношения к этой проблеме, поскольку речь идет не о интернационализации, а также о непонимании Unicode, кодировании символов. Я ценю вашу попытку помочь, хотя, извиняюсь, если я не был достаточно ясен.

Проблема в том, что у меня есть исходные файлы, которые нужно перекрестно скомпилировать под различными платформами и компиляторами. Программа обрабатывает UTF-8. Он не заботится о каких-либо других кодировках. Я хочу иметь строковые литералы в UTF-8, как в настоящее время работает с gcc и vc2003. Как мне это сделать с VC2008? (то есть обратное совместимое решение).

Вот что я нашел:

gcc (v4.3.2 20081105):

  • строковые литералы используются как есть (необработанные строки)
  • поддерживает кодированные исходные файлы UTF-8
  • исходные файлы не должны иметь спецификацию UTF-8

VC2003:

  • строковые литералы используются как есть (необработанные строки)
  • поддерживает кодированные исходные файлы UTF-8
  • исходные файлы могут иметь или не иметь спецификацию UTF-8 (это не имеет значения)

VC2005 +:

  • строковые литералы массируются компилятором (без сырых строк)
  • char строковые литералы перекодируются в указанную локаль
  • UTF-8 не поддерживается как целевой локаль
  • исходные файлы должны иметь спецификацию UTF-8

Итак, простой ответ заключается в том, что для этой конкретной цели VC2005 + сломан и не обеспечивает обратного совместимого пути компиляции. Единственный способ получить строки Unicode в скомпилированную программу - через UTF-8 + BOM + wchar, что означает, что мне нужно преобразовать все строки обратно в UTF-8 во время использования.

Нет простого кросс-платформенного метода преобразования wchar в UTF-8, например, какой размер и кодировка является wchar? В Windows UTF-16. На других платформах? Различается. Подробные сведения см. В

Ответ 2

Brofield,

У меня была такая же проблема, и я просто наткнулся на решение, которое не требует преобразования исходных строк в широкие символы и обратно: сохраните исходный файл как UTF-8 без подписи, а VC2008 оставит его в покое. Отлично работал, когда я решил отказаться от подписи. Подводя итог:

Юникод (UTF-8 без подписи) - Codepage 65001, не выдает предупреждение c4566 в VC2008 и не вызывает VC для кодирования, в то время как Codepage 65001 (UTF-8 с подписью) делает бросок c4566 ( как вы нашли).

Надеюсь, что не слишком поздно, чтобы помочь вам, но это может ускорить ваше приложение VC2008, чтобы удалить обходной путь.

Ответ 3

В то время как, вероятно, лучше использовать широкие строки, а затем конвертировать по мере необходимости в UTF-8. Я думаю, что ваш лучший выбор - это, как вы уже упоминали, использовать шестнадцатеричные escape-последовательности в строках. Предположим, что вам нужна кодовая точка \uC911, вы можете просто сделать это.

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

Я считаю, что это будет работать нормально, просто не очень читаемо, поэтому, если вы это сделаете, прокомментируйте это, чтобы объяснить.

Ответ 4

Файл/Расширенные параметры сохранения/Кодировка: "Юникод (UTF-8 без подписи) - Codepage 65001"

Ответ 5

Visual С++ (2005+) Стандартное поведение COMPILER для исходных файлов:

  • CP1252 (для этого примера, западноевропейская кодовая страница):
    • "Ä"C4 00
    • 'Ä'C4
    • L"Ä"00C4 0000
    • L'Ä'00C4
  • UTF-8 без спецификации:
    • "Ä"C3 84 00 (= UTF-8)
    • 'Ä' → предупреждение: многосимвольная константа
    • "Ω"E2 84 A6 00 (= UTF-8, как ожидалось)
    • L"A"00C3 0084 0000 (неверно!)
    • L'Ä' → предупреждение: многосимвольная константа
    • L"Ω"00E2 0084 00A6 0000 (неверно!)
  • UTF-8 с спецификацией:
    • "Ä"C4 00 (= CP1252, не более UTF-8),
    • 'Ä'C4
    • "Ω" → ошибка: невозможно преобразовать в CP1252!
    • L"Ä"00C4 0000 (правильно)
    • L'Ä'00C4
    • L"Ω"2126 0000 (правильно)

Вы видите, что компилятор C обрабатывает файлы UTF-8 без спецификации так же, как и CP1252. В результате компилятор не может смешивать строки UTF-8 и UTF-16 в скомпилированный вывод! Поэтому вам нужно решить, для одного файла исходного кода:

  • либо использовать UTF-8 с спецификацией и генерировать только строки UTF-16 (т.е. всегда использовать префикс L),
  • или UTF-8 без спецификации и генерировать только строки UTF-8 (т.е. никогда не использовать префикс L).
  • 7-разрядные символы ASCII не задействованы и могут использоваться с префикс L или без него

Независимо, EDITOR может автоматически обнаруживать файлы UTF-8 без спецификации в виде файлов UTF-8.

Ответ 6

От комментария к этому очень приятному блогу
"Использование UTF-8 в качестве внутреннего представления для строк в C и C++ с Visual Studio"
=> http://www.nubaria.com/ru/blog/?p=289

#pragma execution_character_set("utf-8") 

Он требует Visual Studio 2008 SP1 и следующее исправление:

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

Ответ 7

Как насчет этого? Вы сохраняете строки в кодированном файле UTF-8, а затем предварительно обрабатываете их в ASCII-кодированном исходном файле С++. Вы сохраняете кодировку UTF-8 внутри строки, используя шестнадцатеричные escape-последовательности. Строка

"中国語 (繁体)"

преобразуется в

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

Конечно, это невозможно прочитать любому человеку, и цель состоит в том, чтобы избежать проблем с компилятором.

Вы можете либо использовать препроцессор С++ для ссылки на строки в преобразованном заголовочном файле, либо вы можете преобразовать весь источник UTF-8 в ASCII перед компиляцией, используя этот трюк.

Ответ 8

Переносимое преобразование из любой собственной кодировки прямолинейно использует 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 не работают должным образом в моей системе (VC10 Express на Win7).

Таким образом, я написал простой конвертер на основе 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;
}

Я считаю, что это должно работать на любой платформе, но я не смог ее протестировать, кроме моей собственной системы, поэтому у нее могут быть ошибки.

#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;
}

Ответ 9

Возможно, попробуйте эксперимент:

#pragma setlocale(".UTF-8")

или

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

Ответ 10

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

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

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

Возможно, это помогает...

Ответ 11

Я знаю, что опаздываю на вечеринку, но я думаю, что мне нужно распространить это. Для Visual С++ 2005 и выше, если исходный файл не содержит спецификации (байтовый порядок), а языковой стандарт вашей системы не является английским, VC будет считать, что ваш исходный файл не находится в Юникоде.

Чтобы исходные файлы UTF-8 были скомпилированы правильно, вы должны сохранить в кодировке UTF-8 без спецификации, а языковой стандарт системы (язык, не являющийся Unicode), должен быть английским.

введите описание изображения здесь

Ответ 12

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

Ответ 13

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

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

Во-вторых, сохраните исходный код в спецификации UTF8-NO, обратите внимание, 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')

Запуск script 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)");
}

Ответ 14

Исходные файлы UTF-8

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

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

Ответ 15

У меня была аналогичная проблема с компиляцией строковых литералов UTF-8 (char), и я обнаружил, что в основном мне приходилось иметь как спецификацию UTF-8, так и #pragma execution_character_set("utf-8") [1], либо ни спецификацию, ни прагма [2]. Использование одного без другого привело к некорректному преобразованию.

Я записал детали на https://github.com/jay/compiler_string_test

[1]: Visual Studio 2012 не поддерживает execute_character_set. Visual Studio 2010 и 2015 он отлично работает, и, как вы знаете, с патчем в 2008 году он отлично работает.

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

Ответ 17

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