std :: wstring VS std :: строка

Я не могу понять разницу между std::string и std::wstring. Я знаю, что wstring поддерживает широкие символы, такие как символы Unicode. У меня есть следующие вопросы:

  1. Когда мне следует использовать std::wstring вместо std::string?
  2. Может ли std::string содержать весь набор символов ASCII, включая специальные символы?
  3. std::wstring поддерживаются ли все популярные компиляторы C ++?
  4. Что такое «широкий символ»?

person Community    schedule 31.12.2008    source источник
comment
В наборе символов ASCII не так много специальных символов, наиболее экзотичным из них, вероятно, является `(обратная кавычка). std :: string может содержать около 0,025% всех символов Unicode (обычно 8-битный символ)   -  person MSalters    schedule 02.01.2009
comment
Если под специальным вы подразумеваете символы от 128 до 255, которые зависят от используемой нормы, то да, они поддерживаются.   -  person Zonko    schedule 10.06.2011
comment
Хорошую информацию о широких символах и о том, какой тип использовать, можно найти здесь: programmers.stackexchange.com/questions/102205/   -  person Yariv    schedule 14.03.2012
comment
Ну, а поскольку мы находимся в 2012 году, был написан utf8everywhere.org. Он в значительной степени отвечает на все вопросы о том, что правильно, а что нет в C ++ / Windows.   -  person Pavel Radzivilovsky    schedule 21.06.2012
comment
@MSalters: std :: string может содержать 100% всех символов Unicode, даже если CHAR_BIT равен 8. Это зависит от кодировки std :: string, которая может быть UTF-8 на системном уровне (как почти везде, кроме окон ) или на уровне вашего приложения. Родная узкая кодировка не поддерживает Unicode? Нет проблем, просто не используйте его, используйте вместо этого UTF-8.   -  person Yakov Galka    schedule 22.06.2012
comment
Что касается приложения на основе WinAPI, очень неудобно использовать std :: string, потому что вы потеряете преобразования (UNICODE ‹-› ANSI), которые происходят очень часто. Конечно, вы можете использовать псевдонимы ANSI для функций WinAPI, но это всего лишь макросы, которые неявно преобразуют ваши аргументы, закодированные в ANSI, в аргументы UNICODE и вызывают реальный код API, основанный на ВСЕМ UNICODE (см. J.Richter Programming Windows 5th ed.)   -  person nickolay    schedule 17.05.2013
comment
Отличное чтение по этой теме: utf8everywhere.org   -  person Timothy Shields    schedule 05.08.2013


Ответы (12)


string? wstring?

std::string - это basic_string, созданный по шаблону на char, а std::wstring на _ 7_.

char vs. wchar_t

char должен содержать символ, обычно 8-битный.
wchar_t должен содержать широкий символ, и тогда все становится сложно:
В Linux wchar_t составляет 4 байта, а в Windows это 2 байта.

А как насчет Unicode?

Проблема в том, что ни char, ни wchar_t напрямую не привязаны к юникоду.

В Linux?

Возьмем ОС Linux: моя система Ubuntu уже поддерживает Unicode. Когда я работаю со строкой символов, она изначально закодирована в UTF-8 (т.е. Unicode строка символов). Следующий код:

#include <cstring>
#include <iostream>

int main(int argc, char* argv[])
{
   const char text[] = "olé" ;


   std::cout << "sizeof(char)    : " << sizeof(char) << std::endl ;
   std::cout << "text            : " << text << std::endl ;
   std::cout << "sizeof(text)    : " << sizeof(text) << std::endl ;
   std::cout << "strlen(text)    : " << strlen(text) << std::endl ;

   std::cout << "text(ordinals)  :" ;

   for(size_t i = 0, iMax = strlen(text); i < iMax; ++i)
   {
      std::cout << " " << static_cast<unsigned int>(
                              static_cast<unsigned char>(text[i])
                          );
   }

   std::cout << std::endl << std::endl ;

   // - - - 

   const wchar_t wtext[] = L"olé" ;

   std::cout << "sizeof(wchar_t) : " << sizeof(wchar_t) << std::endl ;
   //std::cout << "wtext           : " << wtext << std::endl ; <- error
   std::cout << "wtext           : UNABLE TO CONVERT NATIVELY." << std::endl ;
   std::wcout << L"wtext           : " << wtext << std::endl;

   std::cout << "sizeof(wtext)   : " << sizeof(wtext) << std::endl ;
   std::cout << "wcslen(wtext)   : " << wcslen(wtext) << std::endl ;

   std::cout << "wtext(ordinals) :" ;

   for(size_t i = 0, iMax = wcslen(wtext); i < iMax; ++i)
   {
      std::cout << " " << static_cast<unsigned int>(
                              static_cast<unsigned short>(wtext[i])
                              );
   }

   std::cout << std::endl << std::endl ;

   return 0;
}

выводит следующий текст:

sizeof(char)    : 1
text            : olé
sizeof(text)    : 5
strlen(text)    : 4
text(ordinals)  : 111 108 195 169

sizeof(wchar_t) : 4
wtext           : UNABLE TO CONVERT NATIVELY.
wtext           : ol�
sizeof(wtext)   : 16
wcslen(wtext)   : 3
wtext(ordinals) : 111 108 233

Вы увидите, что текст «olé» в char на самом деле состоит из четырех символов: 110, 108, 195 и 169 (не считая завершающего нуля). (Я позволю вам изучить код wchar_t в качестве упражнения)

Итак, работая с char в Linux, вы обычно должны использовать Unicode, даже не подозревая об этом. И поскольку std::string работает с char, std::string уже поддерживает Unicode.

Обратите внимание, что std::string, как и API строки C, будет считать, что строка "olé" состоит из 4 символов, а не из трех. Поэтому вы должны быть осторожны при усечении / игре с символами Unicode, потому что некоторые комбинации символов запрещены в UTF-8.

В Windows?

В Windows это немного иначе. Win32 должен был поддерживать множество приложений, работающих с char и разными кодировками / кодовые страницы, созданные во всем мире до появления Unicode.

Таким образом, их решение было интересным: если приложение работает с char, то строки символов кодируются / печатаются / отображаются на этикетках графического интерфейса пользователя с использованием локальной кодировки / кодовой страницы на машине. Например, «olé» будет «olé» во французской локализованной Windows, но будет чем-то другим в локализованной на кириллицу Windows («olй», если вы используете Windows-1251). Таким образом, «исторические приложения» обычно будут работать по-прежнему.

Для приложений на основе Unicode Windows использует wchar_t, который имеет ширину 2 байта и закодирован в UTF-16, который представляет собой кодировку Unicode на 2-байтовых символах (или, по крайней мере, наиболее совместимый UCS-2, что почти то же самое, что и IIRC).

Приложения, использующие char, называются «многобайтовыми» (потому что каждый глиф состоит из одного или нескольких char), а приложения, использующие wchar_t, называются «widechar» (потому что каждый глиф состоит из одного или двух wchar_t. См. MultiByteToWideChar и WideCharToMultiByte API преобразования Win32 для получения дополнительной информации.

Таким образом, если вы работаете в Windows, вы очень хотите использовать wchar_t (если только вы не используете фреймворк, скрывающий это, например GTK + или QT ... ). Дело в том, что за кулисами Windows работает со строками wchar_t, поэтому даже в исторических приложениях их char строки будут преобразованы в wchar_t при использовании API, такого как SetWindowText() (функция API низкого уровня для установки метки в графическом интерфейсе Win32).

Проблемы с памятью?

UTF-32 составляет 4 байта на символ, поэтому особо нечего добавить, если только текст UTF-8 и текст UTF-16 всегда будут использовать меньше или такой же объем памяти, чем текст UTF-32 (и обычно меньше ).

Если есть проблема с памятью, вы должны знать, что для большинства западных языков текст UTF-8 будет использовать меньше памяти, чем тот же текст UTF-16.

Тем не менее, для других языков (китайский, японский и т. Д.) Используемая память будет либо такой же, либо немного больше для UTF-8, чем для UTF-16.

В целом, UTF-16 будет в основном использовать 2, а иногда и 4 байта на символ (если вы не имеете дело с какими-то эзотерическими языковыми глифами (клингонский? Эльфийский?), Тогда как UTF-8 будет тратить от 1 до 4 байтов.

См. http://en.wikipedia.org/wiki/UTF-8#Compared_to_UTF-16 для получения дополнительной информации.

Заключение

  1. Когда следует использовать std :: wstring вместо std :: string?

    В Linux? Почти никогда (§).
    В Windows? Почти всегда (§).
    О кроссплатформенном коде? Зависит от вашего набора инструментов ...

    (§): если вы не используете инструментарий / фреймворк, говорящий иначе

  2. Может ли std::string содержать весь набор символов ASCII, включая специальные символы?

    Примечание: std::string подходит для хранения «двоичного» буфера, а std::wstring - нет!

    В Linux? Да.
    В Windows? Только специальные символы доступны для текущего языкового стандарта пользователя Windows.

    Изменить (после комментария от Иоганна Герелла):
    std::string будет достаточно для обработки всех строк на основе char (каждая char является числом от 0 до 255). Но:

    1. ASCII is supposed to go from 0 to 127. Higher chars are NOT ASCII.
    2. char от 0 до 127 будет удерживаться правильно
    3. char от 128 до 255 будет иметь значение в зависимости от вашей кодировки (unicode, non-unicode и т. д.), но он сможет содержать все глифы Unicode, если они закодированы в UTF-8.
  3. Поддерживается ли std::wstring почти всеми популярными компиляторами C ++?

    В основном, за исключением компиляторов на основе GCC, которые портированы в Windows.
    Он работает на моем g ++ 4.3.2 (под Linux), и я использовал Unicode API в Win32, начиная с Visual C ++ 6.

  4. Что такое широкий символ?

    В C / C ++ это символьный тип, записанный wchar_t, который больше, чем простой char символьный тип. Предполагается, что он будет использоваться для вставки внутри символов, чьи индексы (например, глифы Unicode) больше 255 (или 127, в зависимости от ...).

person paercebal    schedule 31.12.2008
comment
Гм. Я не знал, что окна не соответствуют спецификации POSIX в этом отношении. POSIX говорит, что wchar_t должен иметь возможность представлять различные коды расширенных символов для всех членов самого большого набора символов, указанного среди локалей, поддерживаемых средой компиляции. - person gnud; 02.01.2009
comment
@gnud: Возможно, wchar_t должно было быть достаточно для обработки всех символов UCS-2 (большинства символов UTF-16) до появления UTF-16 ... Или, возможно, у Microsoft были другие приоритеты, кроме POSIX, например, предоставление легкого доступа к Unicode без изменения кодовой страницы использования char в Win32. - person paercebal; 02.01.2009
comment
@gnud: обратите внимание на определение wchar_t, цитируемое в Википедии: en.wikipedia.org/wiki/Wchar_t ... Очевидно, whcar_t в Windows следует тому, что было запрошено Unicode ... ^ _ ^ ... - person paercebal; 02.01.2009
comment
Ваш ответ очень хорошо объясняет различия между двумя альтернативами. Замечание: UTF-8 может занимать 1-6 байтов, а не 1-4, как вы написали. Также мне хотелось бы узнать мнение людей о двух альтернативах. - person sorin; 10.01.2010
comment
@Sorin Sbarnea: UTF-8 может занимать 1-6 байтов, но, по-видимому, стандарт ограничивает его до 1-4. Дополнительную информацию см. В en.wikipedia.org/wiki/UTF8#Description. - person paercebal; 13.01.2010
comment
Компиляция и выполнение вашего кода в Mac OS X дает тот же результат, что и на вашем компьютере с Linux. - person WolfgangP; 05.07.2010
comment
@Wolfgang Plaschg: Спасибо за информацию. Это не неожиданно, поскольку MacOS X - это Unix, поэтому кажется естественным, что они пошли по тому же пути, что и char - это UTF-8 для поддержки Unicode ... AFAIK, единственная причина, по которой Windows не пошла по тому же пути, заключалась в продолжении поддержки pre -Старые приложения на основе кодировки юникода. - person paercebal; 05.07.2010
comment
@paercebal UTF-8 не может занимать 6 байтов. Именно потому, что стандарт ограничивает его 4 байтами. Стандарт определяет вещи, поэтому 6 байтов означают, что это больше не UTF-8 по определению. - person Mihai Nita; 28.05.2011
comment
@ Михай Нита: UTF-8 cannot take 6 bytes. Exactly because the the standard limits it to 4 bytes.. Я согласен. Я настолько согласен с вами, что уже писал об этом в предыдущем комментарии: @Sorin Sbarnea: UTF-8 could take 1-6 bytes, but apparently the standard limits it to 1-4. ... ^ _ ^ ... Думаю, смысл моего замечания состоял в том, чтобы напомнить, что ограничение до 4 было искусственным, что кодировка, используемая UTF -8 может поддерживать до 6 байтов для 1-байтового символа, даже если стандарт решил ограничить его до 4. - person paercebal; 29.05.2011
comment
Я хочу сделать #include ‹stdlib.h› std :: wstring ws; ws + = wchar (2591); / * Символ затенения 25% * / std :: wcout ‹---------------- ws; но это дает мне пустой результат. КАК мне ввести конкретный большой номер символа Unicode в строку wstring и вывести его? - person Jim Michaels; 21.01.2012
comment
@ Джим Майклс: Вы пытаетесь вывести символ x0A1F (Гурмукхи). wchar_t может содержать этот символ, поэтому ваша строка верна. Если результат wcout неверен, это может быть связано с тем, что использование шрифта для консоли вывода не готово для символов Гурмукхи (unicode.org/charts/PDF/U0A00.pdf) - person paercebal; 21.01.2012
comment
Хотя этот пример дает разные результаты в Linux и Windows, программа на C ++ содержит поведение, определяемое реализацией, относительно того, закодирован ли olè как UTF-8 или нет. Более того, причина, по которой вы не можете изначально передавать wchar_t * в std::cout, заключается в том, что типы несовместимы, что приводит к плохо сформированной программе, и это не имеет ничего общего с использованием кодировок. Стоит отметить, что то, используете ли вы std::string или std::wstring, зависит от ваших собственных предпочтений кодирования, а не от платформы, особенно если вы хотите, чтобы ваш код был переносимым. - person John Leidegren; 09.08.2012
comment
@JohnLeidegren: While this examples produces different results on Linux and Windows the C++ program contains implementation-defined behavior as to whether olè is encoded as UTF-8 or not.: Да. В самом деле, суть заключалась в том, чтобы показать это. Further more, the reason you cannot natively stream wchar_t * to std::cout is because the types are incompatible resulting in an ill-formed program and it has nothing to do with the use of encodings.: Конечно. Я давал несколько комбинаций, и, если это невозможно, объяснял, почему в коде, для полноты, не подчеркивая то, что вы предлагаете ... - person paercebal; 09.08.2012
comment
@ Джон Лейдегрен: It's worth pointing out that whether you use std::string or std::wstring depends on your own encoding preference rather than the platform: Верно. Но тогда, если ограничения используют Unicode, но не используют 4 байта для каждого символа, платформа в значительной степени ограничивает ваши варианты, то есть std::wstring в Windows и std::string в Linux ... (Вы можете попробовать использовать UTF-8 std::string в Windows, но тогда ваши строки UTF-8 не будут поняты WinAPI с использованием символов char *.) - person paercebal; 09.08.2012
comment
@paercebal Независимо от того, что поддерживает платформа, это совершенно произвольно и помимо сути. Если вы храните все строки внутри как UTF-8 в Windows, вам придется преобразовать их в ANSI или UTF-16 и вызвать соответствующую функцию Win32, но если вы знаете, что ваши строки UTF-8 - это просто строки ASCII, которых вы не делаете надо что-нибудь делать. Платформа не столько диктует, как использовать строки, сколько обстоятельства. - person John Leidegren; 09.08.2012
comment
@John Leidegren: Конечно, платформа диктует, как вы используете струны. В Windows у вас нет выбора: char строки имеют определенную кодовую страницу / кодировку, поэтому необходимо решить, как вы используете std::string, либо путем написания преобразователей, либо с помощью функций, специфичных для кодовой страницы. Что касается std::wstring, если вы не используете интерфейс преобразования, вы знаете, что кодировка должна быть версией Windows UTF-16 (в прошлый раз, когда я проверял, это была UCS-2), поэтому как вы интерпретируете символы в этом контексте. Насколько я понимаю, это как, а не обстоятельства. Но не будем терять время на лексику ... - person paercebal; 09.08.2012
comment
Windows на самом деле использует UTF-16, и в течение некоторого времени более старые версии Windows использовали UCS-2, но это уже не так. Моя единственная проблема здесь заключается в том, что std::wstring следует использовать в Windows, потому что он лучше подходит для Unicode Windows API, что я считаю ошибочным. Если ваша единственная проблема заключалась в вызове Unicode Windows API, а не в сортировке строк, тогда конечно, но я не покупаю это как общий случай. - person John Leidegren; 09.08.2012
comment
@ Джон Лейдегрен: If your only concern was calling into the Unicode Windows API and not marshalling strings then sure: Тогда мы согласны. Я кодирую на C ++, а не на JavaScript. В основе этого языка лежит недопущение бесполезного маршалинга или любой другой потенциально дорогостоящей обработки во время выполнения, когда это можно сделать во время компиляции. Кодирование с использованием WinAPI и использование std::string - это неоправданная трата ресурсов времени выполнения. Вы находите это ошибочным, и это нормально, поскольку это ваша точка зрения. Лично я не буду писать код с пессимизацией в Windows только потому, что со стороны Linux он выглядит лучше. - person paercebal; 09.08.2012
comment
@gnud: см. этот отличный ответ, чтобы узнать, почему требование POSIX (на самом деле это требование C ++) не нарушает использование кодирование переменной длины. - person Yakov Galka; 10.11.2012
comment
В качестве небольшой поправки кодировка UTF-16 может занимать 2 ИЛИ 4 байта на символ. (см. unicode.org/faq/utf_bom.html#gen6) - person lfalin; 20.08.2014
comment
@lfalin: Конечно. В первый раз, когда я говорю о широких символах в Windows, я описываю, как Windows не совсем понимала (по крайней мере, для меня), как она обрабатывает Unicode (что такое UCS-2 или UTF-16?). Во второй раз я пишу о размере символа: в целом, UTF-16 будет в основном использовать 2 байта на символ (если вы не имеете дело с какими-то глифами эзотерического языка (клингон? Эльфийский?), В то время как UTF-8 потратит от 1 до 4 байтов., Что примерно соответствует тому, что вы говорите (в основном ключевое слово). Я думаю, что в моем ответе следует уточнить позицию Windows по этому вопросу. . - person paercebal; 21.08.2014
comment
Интересно отметить, что если вы сделаете cout перед wcout, символы Юникода не будут печататься с wcout. Если, однако, вы начинаете с wcout, cout даже не печатаются вообще, и все распечатки в Юникоде печатаются правильно. Как будто в библиотеках хранится какое-то внутреннее состояние? - person Climax; 16.05.2015
comment
@paercebal: Просто примечание: один из таких экзотических языков - китайский, кстати. Таким образом, некоторое время назад PRC решила сделать поддержку некоторых кодовых точек вне BMP обязательной. - person Deduplicator; 10.10.2015
comment
при работе с символом в Linux обычно приходится использовать Unicode, даже не подозревая об этом. И поскольку std :: string работает с char, std :: string уже поддерживает Unicode. - это должно сопровождаться БОЛЬШИМ предупреждением: никогда не усекайте, не ограничивайте, не принимайте символы в ваших строках. Это можно понять из всего ответа, но следует сделать предельно ясным. - person Piotr Findeisen; 09.12.2016
comment
Что делает это wchar_t []? - person Michele; 08.04.2017
comment
{0x42, 0x65, 0x6E, 0x6A, 0x61, 0x6D, 0xED, 0x6E, 0x20, 0x70, 0x69, 0x64, 0x69, 0xF3, 0x20, 0x75, 0x6E, 0x61, 0x20, 0x62, 0x65, 0x62, 0x64, 0x64 , 0x20, 0x64, 0x65, 0x20, 0x6B, 0x69, 0x77, 0x69, 0x20, 0x79, 0x20, 0x66, 0x72, 0x65, 0x73, 0x61, 0x3B, 0x20, 0x4E, 0x6F, 0xE9, 0x2C, 0x20 , 0x6E, 0x20, 0x76, 0x65, 0x72, 0x67, 0xFC, 0x65, 0x6E, 0x7A, 0x61, 0x2C, 0x20, 0x6C, 0x61, 0x20, 0x6D, 0xE1, 0x73, 0x20, 0x65, 0x78x, 0x71 , 0x73, 0x69, 0x74, 0x61, 0x20, 0x63, 0x68, 0x61, 0x6D, 0x70, 0x61, 0xF1, 0x61, 0x20, 0x64, 0x65, 0x6C, 0x20, 0x6D, 0x65, 0x6E, 0x, 0x00}; - person Michele; 08.04.2017
comment
@Michele: Ничего, это просто последовательность байтов. Его нельзя интерпретировать как UTF8, но кажется, что он интерпретируется как UTF16. Или любую из тысячи кодовых страниц. - person Mooing Duck; 13.07.2017
comment
@paercebal Я понимаю, что эта ветка комментариев стара, как само время, но настаивать на сопоставлении строкового формата WinAPI из соображений производительности просто глупо. Стоимость самих вызовов API значительно превзойдет затраты на конверсию; стоимость дополнительного хранилища, необходимого для строк UTF-16, вероятно, сведет на нет любые потенциальные выгоды, связанные с преобразованием; и если вы общаетесь с другими API, вам, вероятно, все равно придется выполнять преобразования. См. Пример utf8everywhere.org/#faq.cvt.perf. - person Stuntddude; 25.10.2017
comment
Для программы Windows, которая получает входные данные как строки в кодировке UTF-8, нет смысла преобразовывать все в wchar_t. Конвертируйте только при прямом взаимодействии с WinAPI. Пока компилятор работает с кодировкой UTF-8, я не вижу смысла отдавать предпочтение wchar_t перед char. Как обычно, это зависит от требований. - person Roi Danton; 14.01.2019
comment
Спасибо, что напомнили нам основную истину: строки ужасны в C / C ++ - person Mr. Boy; 13.02.2020
comment
std::string подходит для хранения «двоичного» буфера, а std::wstring - нет! - Вы можете предоставить источник для этого утверждения? - person Paul; 03.10.2020
comment
Я чего-то не понимаю, если у меня есть кроссплатформенная программа, означает ли это, что мне нужно создать слой абстракции поверх std :: string для локализованного текста, который, например, превратился бы в std :: string в Linux и std :: wstring в Windows? - person jrh; 13.12.2020

Я рекомендую избегать std::wstring в Windows или где-либо еще, кроме случаев, когда этого требует интерфейс, или где-либо рядом с вызовами Windows API и соответствующими преобразованиями кодировки в качестве синтаксического сахара.

Мое мнение изложено в http://utf8everywhere.org, соавтором которого я являюсь.

Если ваше приложение не ориентировано на вызовы API, например в основном приложение пользовательского интерфейса, предлагается хранить строки Unicode в std :: string и закодированные в UTF-8, выполняя преобразование рядом с вызовами API. Преимущества, описанные в статье, перевешивают очевидное раздражение преобразования, особенно в сложных приложениях. Это вдвойне верно для многоплатформенной и библиотечной разработки.

А теперь, отвечая на ваши вопросы:

  1. Несколько слабых причин. Он существует по историческим причинам, когда расширение символов считалось правильным способом поддержки Unicode. Теперь он используется для интерфейса API, которые предпочитают строки UTF-16. Я использую их только в непосредственной близости от таких вызовов API.
  2. Это не имеет ничего общего с std :: string. Он может содержать любую кодировку, которую вы в него вставляете. Вопрос только в том, как Вы относитесь к его содержанию. Я рекомендую UTF-8, поэтому он сможет правильно хранить все символы Unicode. Это обычная практика в Linux, но я думаю, что программы Windows тоже должны это делать.
  3. No.
  4. Широкий символ - это сбивающее с толку имя. На заре Unicode считалось, что символ можно закодировать двумя байтами, отсюда и название. Сегодня это означает «любую часть символа длиной два байта». UTF-16 рассматривается как последовательность таких пар байтов (также называемых широкими символами). Символ в UTF-16 принимает одну или две пары.
person Pavel Radzivilovsky    schedule 29.12.2009
comment
Вот мое объяснение кодировки строк в контексте JavaScript: github.com/duzun/string-encode.js/blob/master/ - person DUzun; 24.09.2020
comment
Я думаю, что ваша идея использовать wstring только для вызовов API интересна, но меня немного смущает передача данных в программе; прямо сейчас я использую строковый поток для передачи данных из fstream в, можно ли предположить, что стандартная библиотека C ++ способна определить, что текстовый файл является UTF-8, и автоматически создаст строку в правильной кодировке? Или он будет интерпретировать текстовый файл как 8-битные символы и возвращать искаженный текст? Нормы что-нибудь об этом говорят? - person jrh; 13.12.2020

Итак, каждый читатель здесь должен иметь четкое представление о фактах и ​​ситуации. Если нет, то вы должны прочитать чрезвычайно исчерпывающий ответ Паэрцебала [кстати: спасибо!].

Мой прагматический вывод шокирующе прост: вся эта "кодировка символов" в C ++ (и STL) в значительной степени нарушена и бесполезна. Винить в этом Microsoft или нет, это все равно не поможет.

Мое решение, после тщательного исследования, большого разочарования и вытекающих из этого событий, заключается в следующем:

  1. примите, что вы должны сами нести ответственность за кодирование и преобразование (и вы увидите, что многое из этого довольно тривиально)

  2. используйте std :: string для любых строк в кодировке UTF-8 (только typedef std::string UTF8String)

  3. согласитесь, что такой объект UTF8String - просто глупый, но дешевый контейнер. Никогда не обращайтесь и / или не управляйте символами в нем напрямую (без поиска, замены и т. Д.). Вы могли бы, но вы действительно очень, очень не хотите тратить свое время на написание алгоритмов обработки текста для многобайтовых строк! Даже если другие люди уже совершали такие глупые поступки, не делайте этого! Будь как будет! (Ну, есть сценарии, в которых это имеет смысл ... просто используйте для них библиотеку ICU).

  4. используйте std :: wstring для строк в кодировке UCS-2 (typedef std::wstring UCS2String) - это компромисс и уступка беспорядку, который привнес в WIN32 API). Для большинства из нас достаточно UCS-2 (подробнее об этом позже ...).

  5. используйте экземпляры UCS2String всякий раз, когда требуется посимвольный доступ (чтение, управление и т. д.). Любая символьная обработка должна выполняться в НЕ-многобайтовом представлении. Это просто, быстро, легко.

  6. добавьте две служебные функции для преобразования между UTF-8 и UCS-2:

    UCS2String ConvertToUCS2( const UTF8String &str );
    UTF8String ConvertToUTF8( const UCS2String &str );
    

Преобразования просты, здесь должен помочь Google ...

Вот и все. Используйте UTF8String везде, где важна память, и для всех операций ввода-вывода UTF-8. Используйте UCS2String везде, где строка должна анализироваться и / или обрабатываться. Вы можете конвертировать между этими двумя представлениями в любое время.

Альтернативы и улучшения

  • преобразования из & в однобайтовые кодировки символов (например, ISO-8859-1) могут быть реализованы с помощью простых таблиц перевода, например const wchar_t tt_iso88951[256] = {0,1,2,...}; и соответствующий код для преобразования в UCS2 и из него.

  • если UCS-2 недостаточно, переключитесь на UCS-4 (typedef std::basic_string<uint32_t> UCS2String)

ICU или другие библиотеки Unicode?

Для продвинутых пользователей.

person Frunsi    schedule 07.11.2011
comment
Черт возьми, нехорошо знать, что встроенной поддержки Unicode нет. - person Mihai Danila; 15.12.2013
comment
@Frunsi, мне любопытно узнать, пробовали ли вы Glib :: ustring, и если да, то что вы думаете? - person Caroline Beltran; 19.09.2014
comment
@CarolineBeltran: Я знаю Glib, но я никогда им не пользовался и, вероятно, никогда не буду им пользоваться, потому что он скорее ограничен довольно неспецифической целевой платформой (юниксоидные системы ...). Его порт Windows основан на внешнем уровне win2unix, и там, IMHO, вообще нет уровня совместимости с OSX. Все это явно ведет в неправильном направлении, по крайней мере, для моего кода (на этом уровне арки ...) ;-) Итак, Glib не вариант - person Frunsi; 20.09.2014
comment
Я думаю, что пункты 2 и 3 требуют НЕ использовать std :: string для utf8. ЕСЛИ вы все еще хотите сэкономить в памяти, тогда создайте подкласс std :: string, чтобы вы получали по крайней мере утверждения и предупреждения при использовании substr, concat и length и, в основном, любого содержимого, нарушающего функциональные возможности строковых операций. Лично я советую использовать wstrings для строк Unicode, независимо от того, выбираете ли вы utf8, 16 или 32 или ucs-2. Вам будет намного проще выполнять ввод-вывод с ними. Даже компоненты пользовательского интерфейса в настоящее время правильно работают со строками Unicode, поэтому понижающее преобразование должно быть необходимо только при работе со старыми компонентами. - person StarShine; 07.10.2014
comment
@StarShine & @CarolineBeltran: Может быть ... Но создание подкласса std::string приводит к еще одному взгляду на проблему, который является просто еще одним неправильным типом std :: string, как это уже есть у std :: string. комплексное решение будет содержать std::string, который различается между проблемами компоновки памяти и проблемами последовательности символов. Итак, для начала, например, у std :: string должны быть метод size() и метод nchars(). - person Frunsi; 08.10.2014
comment
Кстати: даже C ++ 11x, C ++ 14x, ни какие-либо будущие стандарты, ни кто-либо еще не заботился об этой проблеме. Итак, I18N в C ++ - это все еще то, решения по-прежнему ожидаются ... - person Frunsi; 08.10.2014
comment
О, и @StarShine: прочтите, пожалуйста, полный ответ. Это не так просто, как вы думаете. - person Frunsi; 08.10.2014
comment
@Frunsi: А, может, я это пропустил. Как ваш UTF8String typedef обеспечивает комплексное решение, которое различается между проблемами разметки памяти и проблемами последовательности символов? В лучшем случае это инструмент рефакторинга, но не решение. Во-первых, удачи с включением nchars () в стандарт. Во-вторых, насколько вы действительно можете быть уверены, что сторонние библиотеки не разрушают ваши последовательности utf8? Наконец, utf8 сложнее анализировать и отлаживать. Если вы с самого начала используете wstring и ucs2 или правильный utf16, ваш отладчик отобразит правильную китайскую строку, и вам не придется разбирать ее вместе с байтовыми кодами. - person StarShine; 08.10.2014
comment
@StarShine: UTF8String typedef не является комплексным решением. Это просто прагматичное решение, которое работает (в большинстве случаев в большинстве случаев). ИМХО настало время, чтобы люди, занимающиеся стандартами C ++, предложили лучшее решение. Основы (Unicode и его различные схемы кодирования, такие как UTF8 и UCS-2, здесь и здесь, чтобы остаться), так что сейчас самое подходящее время ;-) - person Frunsi; 10.10.2014
comment
@StarShine: также обратите внимание, что у моего решения будут те же проблемы, что и у UCS-2, например. при работе с китайскими струнами! Так что это действительно прагматическая вещь, а не комплексное решение. - person Frunsi; 10.10.2014
comment
Поиск, замена и т. Д. Отлично работают со строками UTF-8 (часть последовательности байтов, представляющая символ, никогда не может быть неверно интерпретирована как другой символ). Фактически, UTF-16 и UTF-32 нисколько не упрощают эту задачу: на практике все три кодировки являются многобайтовыми, поскольку воспринимаемый пользователем символ (кластер графемы) может иметь любое количество длинных кодовых точек Unicode! Прагматичное решение - использовать UTF-8 для всего и преобразовывать в UTF-16 только при работе с Windows API. - person Daniel; 17.10.2014
comment
@ Дэниел: Как вы думаете, почему прагматичное решение будет использовать UTF-8 для всего? Однобайтовый код поиска и замены может не причинить большого вреда последовательностям байтов UTF-8, но он также не решит реальных проблем: P Использование UTF-8 для всего - неправильный путь для всех ... Использование UTF-8 для хранения & Transfer - это нормально, но его использование для обработки строк приведет к экспоненциальному росту кода, необходимого для обработки всех случаев и комбинаций. Может быть. Но, может быть, все операции с символами можно переписать для работы с графемами? Наверное, нет, правда? Так... - person Frunsi; 23.10.2014
comment
@Daniel: поиск, замена и т. Д. НЕ будут работать нормально со строками UTF-8, к сожалению, это намного сложнее, см., Например, utf8everywhere.org/#myth.strlen - и, конечно же, UTF-16 и UTF-32 не облегчай это. Так? - person Frunsi; 23.10.2014
comment
@Frunsi: поиск и замена работают с UTF-8 так же хорошо, как и с UTF-32. Именно потому, что правильная обработка текста с учетом Unicode в любом случае должна иметь дело с «символами» с несколькими кодовыми точками, использование кодировки переменной длины, такой как UTF-8, не усложняет обработку строк. Так что просто используйте UTF-8 везде. Нормальные строковые функции C будут нормально работать в UTF-8 (и соответствовать порядковым сравнениям в строке Unicode), и если вам нужно что-то более языковое, вам все равно придется вызывать библиотеку Unicode, UTF-16/32 не могу спасти вас от этого. - person Daniel; 23.10.2014
comment
Пока эта ошеломляющая ошибка в языке не будет исправлена, ознакомьтесь с Glib::ustring, действительно интеллектуальная оболочка вокруг std::string из проекта glibmm, которая обертывает обычные string методы с должным учетом количества отображаемых символов (не байтов / char кодировки) в строке. - person underscore_d; 21.05.2017
comment
Как std::string работает с UTF-8? Я думал, что std::string использует char, что всего 1 байт? - person Aaron Franke; 27.02.2019
comment
re: пункт 5, использование 16-битных символов для обработки строк просто, быстро, легко ... и НЕПРАВИЛЬНО. Потому что, несмотря на то, что в этом ответе говорится, что они UCS-2, многие среды на самом деле являются UTF-16, что означает, что вам приходится иметь дело с суррогатами. И даже без суррогатов приходится иметь дело с совмещением персонажей. wchar не защищает вас от всего этого. К сожалению, настоящий ответ - текст сложен и сложен; узнайте, как это на самом деле работает. - person Sean McMillan; 06.09.2019

  1. Если вы хотите, чтобы в вашей строке хранились широкие символы. wide зависит от реализации. Visual C ++ по умолчанию 16 бит, если я правильно помню, а GCC по умолчанию в зависимости от цели. Здесь длина 32 бита. Обратите внимание: wchar_t (тип широких символов) не имеет ничего общего с юникодом. Просто гарантируется, что он может хранить все члены самого большого набора символов, поддерживаемого реализацией его локали, и по крайней мере столько же, сколько char. Вы можете хранить строки Юникода в std::string, используя также кодировку utf-8. Но он не поймет значения кодовых точек Unicode. Таким образом, str.size() не даст вам количество логических символов в вашей строке, а просто количество элементов char или wchar_t, хранящихся в этой строке / wstring. По этой причине разработчики оболочки gtk / glib C ++ разработали Glib::ustring, который может обрабатывать UTF-8.

    Если ваш wchar_t имеет длину 32 бита, вы можете использовать utf-32 в качестве кодировки Unicode, и вы можете хранить и строки Unicode, используя фиксированную (utf-32 - фиксированная длина ) кодирование. Это означает, что функция s.size() вашей wstring затем вернет нужное количество элементов wchar_t и логических символов.

  2. Да, char всегда имеет длину не менее 8 бит, что означает, что он может хранить все значения ASCII.
  3. Да, его поддерживают все основные компиляторы.
person Johannes Schaub - litb    schedule 31.12.2008
comment
Мне интересно узнать о №2. Я думал, 7 бит тоже будут технически допустимыми? Или требуется иметь возможность хранить что-либо, кроме 7-битных символов ASCII? - person jalf; 31.12.2008
comment
да, Джалф. c89 определяет минимальные диапазоны для базовых типов в своей документации по limits.h (для unsigned char это 0..255 мин) и чистую двоичную систему для целочисленных типов. он следует за char, unsigned char и signed char имеет минимальную длину в битах 8. С ++ наследует эти правила. - person Johannes Schaub - litb; 31.12.2008
comment
Это означает, что функция s.size () вашей wstring вернет нужное количество элементов wchar_t и логических символов. Это не совсем точно даже для Unicode. Было бы точнее сказать кодовую точку, чем логический символ, даже в UTF-32 данный символ может состоять из нескольких кодовых точек. - person Logan Capaldo; 16.05.2010
comment
Вы, ребята, по сути, говорите, что C ++ не имеет встроенной поддержки набора символов Unicode? - person Mihai Danila; 15.12.2013
comment
Но он не поймет значения кодовых точек Unicode. В окнах тоже std::wstring. - person Deduplicator; 09.01.2015
comment
@MihaiDanila Это зависит от того, как вы определяете встроенную поддержку. Может ли он хранить последовательности символов Юникода? Абсолютно. Предоставляет ли он какой-либо стандартный класс, который может работать с такими последовательностями с точки зрения количества отображаемых в них символов, а не просто наивно индексировать / находить / и т. Д. По количеству байтов, тем самым, возможно, разбивая последовательности кодовых точек и делая вещи ужасно неправильными? Нет. И это ужасно. Это 2017 год. Я могу только надеяться, что, поскольку мы, наконец, получаем поддержку стандартной файловой системы и сети, возможно, настоящие строки Unicode будут слабо видны где-то за горизонтом. - person underscore_d; 21.05.2017
comment
@underscore_d Поддержка хранения закодированных кодовых точек Unicode в байтах вряд ли заметна как поддержка. И, да, я согласен с тем, что отсутствие стандартной поддержки Unicode в этом языке в 21 веке просто смехотворно. - person Mihai Danila; 22.05.2017
comment
@MihaiDanila, по крайней мере, у нас есть std::codecvt<charNN_t, char> и т.д., начиная с C ++ 11, для преобразования между UTF-NN и UTF-8. Хотя std::wstring_convert устарел с C ++ 17 ... - person Ruslan; 25.09.2018

Я часто использую std :: string для хранения символов utf-8 без каких-либо проблем. Я настоятельно рекомендую сделать это при взаимодействии с API, которые также используют utf-8 в качестве собственного строкового типа.

Например, я использую utf-8 при взаимодействии моего кода с интерпретатором Tcl.

Главное предостережение - это длина std :: string, которая больше не является количеством символов в строке.

person Community    schedule 31.12.2008
comment
Хуан: Вы имеете в виду, что std :: string может содержать все символы Юникода, но длина будет сообщаться неверно? Есть ли причина, по которой он сообщает неправильную длину? - person ; 31.12.2008
comment
При использовании кодировки utf-8 один символ Юникода может состоять из нескольких байтов. Вот почему кодировка utf-8 меньше при использовании в основном символов из стандартного набора ascii. Вам нужно использовать специальные функции (или свернуть свои собственные), чтобы измерить количество символов Юникода. - person ; 31.12.2008
comment
(Специально для Windows) Большинство функций будут ожидать, что строка, использующая байты, будет ASCII, а 2 байта - Unicode, более старые версии MBCS. Это означает, что если вы храните 8-битный юникод, вам придется преобразовать его в 16-битный юникод для вызова стандартной функции Windows (если вы не используете только часть ASCII). - person Greg Domjan; 31.12.2008
comment
Как упоминают Грег и Джоэл (о программном обеспечении), действительно важно понимать, как кодирование работает с API, с которым вы имеете дело. Постоянное переключение между 8 и 16 битной кодировкой в ​​системе Windows может быть неоптимальным. - person ; 31.12.2008
comment
Std :: string не только неверно сообщит длину, но и выведет неправильную строку. Если какой-то символ Unicode представлен в UTF-8 в виде нескольких байтов, которые std :: string воспринимает как свои собственные символы, то ваши обычные процедуры обработки std :: string, вероятно, будут выводить несколько странных символов, которые являются результатом неправильной интерпретации одного. правильный характер. - person Mihai Danila; 15.12.2013
comment
Если я хочу создать программу (работающую в Windows), которая будет свободно использовать множество различных символов Unicode, таких как японские / китайские символы, польские буквы, кириллица и т. Д., Что мне следует использовать? Будет ли достаточно UTF-8? - person Kusavil; 19.08.2014
comment
Что сказал @Mihai Данила. Я настоятельно рекомендую не использовать std :: string для utf-8, особенно при частых строковых операциях, таких как конкатенация и подстрока. Широкие строки могут занимать много места, но если вы серьезно относитесь к программным продуктам и данным в многоязычном и многокультурном мире, использование std :: string становится архаичным, а попытки его использовать просто засоряют код всевозможными странностями. места с функциями, которые большую часть времени «выглядят правильно». Я занимаюсь разработкой игр почти 10 лет на многих разных платформах, поэтому я знаю, о чем говорю. - person StarShine; 07.10.2014
comment
Я предлагаю изменить ответ, чтобы указать, что строки следует рассматривать только как контейнеры байтов, и, если байты представляют собой некоторую кодировку Unicode (UTF-8, UTF-16, ...), тогда вам следует использовать определенные библиотеки, которые понимают что. Стандартные API-интерфейсы на основе строк (длина, подстановка и т. Д.) С многобайтовыми символами терпят неудачу. Если это обновление будет сделано, я сниму свой голос против. - person Mihai Danila; 07.10.2014
comment
Кажется, что в стандартном C ++ нет хороших вариантов для кроссплатформенного международного использования. Недавно я написал текстовый графический интерфейс для программы с настраиваемыми разрывами строк, семантическими тегами, международными символами ... После исследования нескольких подходов я выбрал std :: strings, используя UTF-8 для хранения текстовых данных, но записывая библиотека функций для отображения между символами и байтами, для выполнения общих строковых функций, таких как вставка текста, извлечение и поиск, а также для выполнения преобразований в другие форматы для ввода-вывода. Я приехал сюда, чтобы посмотреть, есть ли теперь лучший способ, похоже, нет. - person QuesterZen; 22.06.2017

Хороший вопрос! Я считаю, что КОДИРОВАНИЕ ДАННЫХ (иногда также используется CHARSET) - это МЕХАНИЗМ ВЫРАЖЕНИЯ ПАМЯТИ, предназначенный для сохранения данных в файл или передачи данных через сети, поэтому я отвечаю на этот вопрос так:

1. Когда мне следует использовать std :: wstring вместо std :: string?

Если платформа программирования или функция API являются однобайтовыми, и мы хотим обработать или проанализировать некоторые данные Unicode, например, прочитанные из файла Windows'.REG или сетевого 2-байтового потока, мы должны легко объявить переменную std :: wstring обработать их. например: wstring ws = L "中国 a" (память на 6 октетов: 0x4E2D 0x56FD 0x0061), мы можем использовать ws [0] для получения символа '中' и ws [1] для получения символа '国' и ws [2] для получить символ "а" и т. д.

2. Может ли std :: string содержать весь набор символов ASCII, включая специальные символы?

да. Но обратите внимание: американский ASCII означает, что каждый октет 0x00 ~ 0xFF обозначает один символ, включая печатный текст, такой как «123abc & * _ &», а вы сказали специальный, в основном печатайте его как '.' избегайте путаницы с редакторами или терминалами. А некоторые другие страны расширяют свою собственную кодировку "ASCII", например Китайский, используйте 2 октета для обозначения одного символа.

3. Поддерживается ли std :: wstring всеми популярными компиляторами C ++?

Может быть, или в основном. Я использовал: VC ++ 6 и GCC 3.3, ДА

4. Что такое «широкий характер»?

широкий символ обычно указывает на использование 2 или 4 октетов для хранения символов всех стран. 2 октета UCS2 является репрезентативной выборкой, и далее, например, Английская 'a', его память составляет 2 октета из 0x0061 (по сравнению с ASCII 'a, его память составляет 1 октет 0x61)

person Leiyi.China    schedule 29.10.2013


Приложения, которые не удовлетворены только 256 различными символами, могут использовать либо широкие символы (более 8 бит), либо кодировку переменной длины (многобайтовую кодировку в терминологии C ++), такую ​​как UTF-8. Для широких символов обычно требуется больше места, чем для кодирования с переменной длиной, но они обрабатываются быстрее. Многоязычные приложения, обрабатывающие большие объемы текста, обычно используют широкие символы при обработке текста, но преобразуют его в UTF-8 при сохранении на диск.

Единственная разница между string и wstring - это тип данных символов, которые они хранят. Строка хранит chars, размер которых гарантированно составляет не менее 8 бит, поэтому вы можете использовать строки для обработки, например. Текст ASCII, ISO-8859-15 или UTF-8. В стандарте ничего не говорится о наборе символов или кодировке.

Практически каждый компилятор использует набор символов, первые 128 символов которого соответствуют ASCII. То же самое и с компиляторами, использующими кодировку UTF-8. При использовании строк в UTF-8 или какой-либо другой кодировке переменной длины важно помнить, что индексы и длины измеряются в байтах, а не в символах.

Тип данных wstring - wchar_t, размер которого не определен в стандарте, за исключением того, что он должен быть по крайней мере таким же большим, как char, обычно 16 или 32 бит. wstring может использоваться для обработки текста в кодировке расширенных символов, определенной в реализации. Поскольку кодировка не определена в стандарте, преобразовать между строками и wstrings непросто. Нельзя также предположить, что wstrings имеют кодировку фиксированной длины.

Если вам не нужна многоязычная поддержка, вы можете использовать только обычные строки. С другой стороны, если вы пишете графическое приложение, часто бывает, что API поддерживает только широкие символы. Тогда вы, вероятно, захотите использовать такие же широкие символы при обработке текста. Имейте в виду, что UTF-16 - это кодировка переменной длины, а это означает, что вы не можете предполагать, что length() вернет количество символов. Если API использует кодировку фиксированной длины, такую ​​как UCS-2, обработка упрощается. Преобразование между широкими символами и UTF-8 сложно сделать переносимым способом, но опять же, ваш API пользовательского интерфейса, вероятно, поддерживает преобразование.

person Seppo Enarvi    schedule 11.09.2011
comment
Итак, перефразируя первый абзац: приложение, которому требуется более 256 символов, должно использовать многобайтовую кодировку или кодировку might_multibyte. - person Deduplicator; 10.10.2015
comment
Однако обычно 16- и 32-битные кодировки, такие как UCS-2 и UCS-4, не называются многобайтовыми кодировками. Стандарт C ++ различает многобайтовые кодировки и широкие символы. В широком символьном представлении используется фиксированное количество (обычно более 8) бит на символ. Кодировки, которые используют один байт для кодирования наиболее распространенных символов и несколько байтов для кодирования остальной части набора символов, называются многобайтовыми кодировками. - person Seppo Enarvi; 13.10.2015
comment
Извините, неряшливый комментарий. Надо было сказать кодирование переменной длины. UTF-16 - это кодировка переменной длины, как и UTF-8. Притворяться, что это не так, - это плохая идея. - person Deduplicator; 13.10.2015
comment
Неплохо подмечено. Нет причин, по которым wstrings нельзя использовать для хранения UTF-16 (вместо UCS-2), но тогда теряется удобство кодирования фиксированной длины. - person Seppo Enarvi; 13.10.2015

  1. когда вы хотите использовать строки Unicode, а не только ascii, полезно для интернационализации
  2. да, но он не работает с 0
  3. не в курсе того, что не
  4. широкий символ - это специфичный для компилятора способ обработки представления фиксированной длины символа Юникода, для MSVC это 2-байтовый символ, для gcc, как я понимаю, это 4 байта. и +1 за http://www.joelonsoftware.com/articles/Unicode.html
person Greg Domjan    schedule 31.12.2008
comment
2. Строка std :: string вполне может содержать символ NULL. Он также может содержать символы UTF-8 и широкие символы. - person ; 31.12.2008
comment
@Juan: Это снова сбило меня с толку. Если std :: string может содержать символы Unicode, что особенного в std :: wstring? - person ; 31.12.2008
comment
@Appu: std :: string может содержать символы Юникода UTF-8. Существует ряд стандартов Юникода, ориентированных на разную ширину символов. UTf8 имеет ширину 8 бит. Также есть UTF-16 и UTF-32 шириной 16 и 32 бит соответственно. - person Greg D; 31.12.2008
comment
С помощью std :: wstring. Каждый символ Юникода может быть одним wchar_t при использовании кодировок фиксированной длины. Например, если вы решите использовать подход «joel on software», на который ссылается Грег. Тогда длина строки wstring равна количеству символов Юникода в строке. Но занимает больше места - person ; 31.12.2008
comment
Я не сказал, что он не может содержать 0 '\ 0', и то, что я имел в виду под «не играет хорошо», - это то, что некоторые методы могут не дать вам ожидаемого результата, содержащего все данные строки wstring. Так сурово против голосов против. - person Greg Domjan; 31.12.2008
comment
Я не хотел обидеть. Но я не согласен с вашими ответами ни на 1, ни на 2. Из аргументации Джоэла я понимаю, почему вы можете захотеть использовать wchar_t при работе в системе Windows. Однако обычный символ работает так же хорошо для i18n. - person ; 31.12.2008

Здесь есть несколько очень хороших ответов, но я думаю, что могу добавить пару вещей, касающихся Windows / Visual Studio. Это основано на моем опыте работы с VS2015. В Linux основной ответ - использовать везде в кодировке UTF-8 std::string. В Windows / VS все становится сложнее. Вот почему. Windows ожидает, что строки, хранящиеся с использованием chars, будут закодированы с использованием кодовой страницы языкового стандарта. Это почти всегда набор символов ASCII, за которым следуют 128 других специальных символов в зависимости от вашего местоположения. Позвольте мне просто заявить, что это не только при использовании Windows API, есть еще три основных места, где эти строки взаимодействуют со стандартным C ++. Это строковые литералы, выводимые в std::cout с использованием << и передача имени файла в std::fstream.

Скажу сразу, что я программист, а не языковый специалист. Я понимаю, что USC2 и UTF-16 не одно и то же, но для моих целей они достаточно близки, чтобы быть взаимозаменяемыми, и я использую их как таковые здесь. На самом деле я не уверен, что использует Windows, но, как правило, мне и знать не нужно. В этом ответе я указал UCS2, поэтому заранее извините, если я кого-нибудь расстрою своим незнанием этого вопроса, и я рад изменить его, если у меня что-то не так.

Строковые литералы

Если вы вводите строковые литералы, содержащие только символы, которые могут быть представлены вашей кодовой страницей, тогда VS сохраняет их в вашем файле с кодировкой 1 байт на символ на основе вашей кодовой страницы. Обратите внимание, что если вы измените свою кодовую страницу или передадите свой исходный код другому разработчику, использующему другую кодовую страницу, я думаю (но не тестировал), что персонаж будет другим. Если вы запустите свой код на компьютере с другой кодовой страницей, я не уверен, изменится ли и символ.

Если вы введете какие-либо строковые литералы, которые не могут быть представлены вашей кодовой страницей, VS попросит вас сохранить файл как Unicode. Затем файл будет закодирован как UTF-8. Это означает, что все символы, отличные от ASCII (включая те, которые находятся на вашей кодовой странице), будут представлены 2 или более байтами. Это означает, что если вы передадите свой источник кому-то другому, он будет выглядеть так же. Однако перед передачей исходного кода компилятору VS преобразует текст в кодировке UTF-8 в текст, закодированный для кодовой страницы, и любые символы, отсутствующие в кодовой странице, заменяются на ?.

Единственный способ гарантировать правильное представление строкового литерала Unicode в VS - это поставить перед строковым литералом знак L, что сделает его широким строковым литералом. В этом случае VS преобразует текст в кодировке UTF-8 из файла в UCS2. Затем вам нужно передать этот строковый литерал в конструктор std::wstring или преобразовать его в utf-8 и поместить в std::string. Или, если вы хотите, вы можете использовать функции Windows API для его кодирования, используя свою кодовую страницу, чтобы поместить его в std::string, но тогда вы также можете не использовать широкий строковый литерал.

std :: cout

При выводе на консоль с использованием << вы можете использовать только std::string, а не std::wstring, и текст должен быть закодирован с использованием кодовой страницы вашего языкового стандарта. Если у вас есть std::wstring, вы должны преобразовать его с помощью одной из функций Windows API, и любые символы, отсутствующие на вашей кодовой странице, будут заменены на ? (возможно, вы можете изменить символ, я не могу вспомнить).

std :: fstream имена файлов

ОС Windows использует UCS2 / UTF-16 для имен файлов, поэтому независимо от вашей кодовой страницы вы можете иметь файлы с любым символом Unicode. Но это означает, что для доступа или создания файлов с символами, которых нет на вашей кодовой странице, вы должны использовать std::wstring. Другого пути нет. Это специальное расширение Microsoft для std::fstream, поэтому, вероятно, не будет компилироваться в других системах. Если вы используете std :: string, вы можете использовать только те имена файлов, которые содержат только символы на вашей кодовой странице.

Ваши варианты

Если вы просто работаете над Linux, то вы, вероятно, не зашли так далеко. Просто используйте везде UTF-8 std::string.

Если вы просто работаете в Windows, просто везде используйте UCS2 std::wstring. Некоторые пуристы могут сказать, что используйте UTF8, а затем конвертируйте, когда это необходимо, но зачем возиться с этими хлопотами.

Если вы кроссплатформенный, то, честно говоря, это беспорядок. Если вы пытаетесь использовать UTF-8 везде в Windows, вам нужно быть очень осторожным со строковыми литералами и выводом на консоль. Вы можете легко испортить там свои строки. Если вы используете std::wstring везде в Linux, у вас может не быть доступа к широкой версии std::fstream, поэтому вам придется выполнить преобразование, но риск повреждения отсутствует. Так что лично я считаю, что это лучший вариант. Многие не согласятся, но я не одинок - это, например, путь, по которому идут wxWidgets.

Другой вариант - ввести unicodestring как std::string в Linux и std::wstring в Windows и иметь макрос под названием UNI () с префиксом L в Windows и ничего в Linux, затем код

#include <fstream>
#include <string>
#include <iostream>
#include <Windows.h>

#ifdef _WIN32
typedef std::wstring unicodestring;
#define UNI(text) L ## text
std::string formatForConsole(const unicodestring &str)
{
    std::string result;
    //Call WideCharToMultiByte to do the conversion
    return result;
}
#else
typedef std::string unicodestring;
#define UNI(text) text
std::string formatForConsole(const unicodestring &str)
{
    return str;
}
#endif

int main()
{

    unicodestring fileName(UNI("fileName"));
    std::ofstream fout;
    fout.open(fileName);
    std::cout << formatForConsole(fileName) << std::endl;
    return 0;
}

я думаю, будет хорошо на любой платформе.

Ответы

Итак, чтобы ответить на ваши вопросы

1) Если вы программируете для Windows, то все время, если кросс-платформенный, то, возможно, все время, если вы не хотите иметь дело с возможными проблемами повреждения в Windows или писать код для конкретной платформы #ifdefs, чтобы обойти различия, если только используя линукс тогда никогда.

2) Да. Кроме того, в Linux вы также можете использовать его для всех Unicode. В Windows вы можете использовать его только для всего Юникода, если вы выберете ручное кодирование с использованием UTF-8. Но Windows API и стандартные классы C ++ ожидают, что std::string будет закодирован с использованием кодовой страницы локали. Сюда входят все символы ASCII плюс еще 128 символов, которые меняются в зависимости от кодовой страницы, для которой настроен ваш компьютер.

3) Я так считаю, но если нет, то это просто определение типа 'std :: basic_string' с использованием wchar_t вместо char

4) Широкий символ - это тип символа, размер которого больше, чем стандартный тип char, равный 1 байту. В Windows это 2 байта, в Linux - 4 байта.

person Phil Rosenberg    schedule 17.08.2018
comment
Что касается Однако, перед передачей исходного кода компилятору VS преобразует текст в кодировке UTF-8 в текст, закодированный для кодовой страницы, и любые символы, отсутствующие в кодовой странице, заменяются на?. - ›Я не думаю, что это правда, когда компилятор использует кодировку UTF-8 (используйте /utf-8). - person Roi Danton; 14.01.2019
comment
Я не знал об этом как о возможности. По этой ссылке docs.microsoft.com/en-us/cpp/build/reference/ кажется, что в свойствах проекта нет флажка для выбора, вы должны добавить это как дополнительная опция командной строки. Хорошее место! - person Phil Rosenberg; 15.01.2019

1) Как упоминал Грег, wstring полезен для интернационализации, именно тогда вы будете выпускать свой продукт на языках, отличных от английского.

4) Обратите внимание на широкий символ http://en.wikipedia.org/wiki/Wide_character

person Raghu    schedule 31.12.2008

Когда НЕ следует использовать широкие символы?

Когда вы пишете код до 1990 года.

Очевидно, я перевернулся, но на самом деле сейчас 21 век. 127 знаков давно перестали быть достаточными. Да, вы можете использовать UTF8, но зачем беспокоиться о головной боли?

person Community    schedule 10.06.2009
comment
@dave: Я не знаю, какая головная боль у UTF-8 больше, чем у Widechars (UTF-16). в UTF-16 у вас также есть многосимвольные символы. - person Pavel Radzivilovsky; 29.12.2009
comment
Проблема в том, что если вы находитесь где угодно, но не в англоязычной стране, вам НЕОБХОДИМО использовать wchar_t. Не говоря уже о том, что в некоторых алфавитах символов больше, чем вы можете уместить в байтах. Мы были там, в DOS. Кодовая шизофрения, нет, спасибо, не более .. - person Swift - Friday Pie; 27.11.2016
comment
@Swift Проблема с wchar_t в том, что его размер и значение зависят от ОС. Он просто меняет старые проблемы на новые. В то время как char - это char независимо от ОС (по крайней мере, на аналогичных платформах). Таким образом, мы могли бы просто использовать UTF-8, упаковать все в последовательности char и сетовать на то, что C ++ оставляет нас полностью самими собой без каких-либо стандартных методов измерения, индексации, поиска и т. Д. В таких последовательностях. - person underscore_d; 21.05.2017
comment
@underscore_d То, что вы описываете, является наименьшей из проблем, если вы пишете код на C ++. Широкий символ wchat_t - это фундаментальный тип в C ++, но не в C, но его двоичное представление не зависит от платформы, как вы описываете, это время выполнения. Таким образом, символ может иметь длину 1 или 2 байта (как минимум) в зависимости от того, какая фактическая строка хранится. Unicode UTF-16 - символы фиксированного размера. Дело в том, что wchar_t - это тип, поддерживаемый для определенной платформы на уровне имен файловых систем (включая окна), в то время как другие платформы используют многобайтовые символы. - person Swift - Friday Pie; 21.05.2017
comment
@Swift Кажется, у вас все наоборот. wchar_t - это тип данных фиксированной ширины, поэтому массив из 10 wchar_t всегда будет занимать sizeof(wchar_t) * 10 байта платформы. А UTF-16 - это кодировка переменной ширины, в которой символы могут состоять из 1 или 2 16-битных кодовых точек (и s / 16/8 / g для UTF-8). - person underscore_d; 21.05.2017
comment
@Swift Извините, это неправильно, по крайней мере, для wchar_t в Windows. В Windows wchar_t - это кодировка UTF-16. Простой тест: wchar_t * test = L ????; // Кодовая точка U + 20000 В отладчике вы увидите строку из двух значений: 0xD840 и 0xDC00, что является кодировкой символа UTF-16. - person Steve Hollasch; 02.11.2017
comment
@SteveHollasch, вы сохранили для него utf16, так что вы его поняли. это зависимый от компиляции примитивный тип, который не приводит и не ограничивает то, что вы пытаетесь ему присвоить. Как API и компилятор будут обрабатывать ist, не определено, в целом это не то же представление, что и ЛЮБОЙ unicode. Wchar_t, как определено Windows api, составляет 16 бит на символ. Итак, у вас есть суррогат - два символа с кодами 0X00DC и 0x40D8. но код, который будет рассматривать это как массив Unicode, будет действовать правильно, вам просто будет трудно определить, является ли это двумя символами или одним. В linux wchar_t 32-битный, ваш код не вызовет проблем - person Swift - Friday Pie; 05.11.2017
comment
@SteveHollasch представление строки wchar_t в окнах будет кодировать символы больше, чем FFFF, как специальную суррогатную пару, другое будет принимать только один элемент wchar_t. Таким образом, это представление не будет совместимо с представлением, созданным компилятором GNU (где перед всеми символами меньше FFFF будет нулевое слово). Что хранится в wchar_t, определяется программистом и компилятором, а не каким-то соглашением. - person Swift - Friday Pie; 05.11.2017