Как создать строковый литерал 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 пытается быть слишком умным в этом случае.
Ответы
Ответ 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]: Некоторые комментарии в этом потоке отметили, что использование ни спецификации, ни прагмы не может привести к некорректному преобразованию для разработчиков, использующих локальную кодовую страницу, которая является многобайтовой (например, Япония).
Ответ 16
Я согласен с Тео Воше. Прочитайте статью Абсолютный минимум Каждый разработчик программного обеспечения Абсолютно, положительно должен знать о Unicode и наборах символов (без отговорок!) на Joel On Software...
Ответ 17
Прочитайте статьи. Во-первых, вы не хотите UTF-8. UTF-8 - это всего лишь способ представления символов. Вам нужны широкие символы (wchar_t). Вы записываете их как L "yourtextgoeshere". Тип этого литерала - wchar_t *. Если вы спешите, просто найдите wprintf.