std :: wstring VS std :: string

742

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

  1. Когда я должен использовать std::wstringболее std::string?
  2. Может ли std::stringсодержать весь набор символов ASCII, включая специальные символы?
  3. Является ли std::wstringподдерживаются компиляторами всех популярных C ++?
  4. Что такое « широкий характер »?
Rapptz
источник
10
Набор символов ASCII не имеет большого количества «специальных» символов, наиболее экзотическим, вероятно, является `(обратная цитата). std :: string может содержать около 0,025% всех символов Unicode (обычно 8-битный символ)
MSalters
3
Хорошую информацию о широких символах и о том, какой тип использовать, можно найти здесь: programmers.stackexchange.com/questions/102205/…
Yariv
14
Ну, а так как мы в 2012 году, был написан utf8everywhere.org . Он в значительной степени отвечает на все вопросы о правах и ошибках с C ++ / Windows.
Павел Радзивиловский
42
@MSalters: std :: string может содержать 100% всех символов Unicode, даже если CHAR_BIT равен 8. Это зависит от кодировки std :: string, которая может быть UTF-8 на системном уровне (как почти везде, кроме окон ) или на уровне вашего приложения. Родное узкое кодирование не поддерживает Unicode? Нет проблем, просто не используйте его, вместо этого используйте UTF-8.
Яков Галка
8
Отличное чтение на эту тему: utf8everywhere.org
Тимоти Шилдс

Ответы:

993

string? wstring?

std::stringявляется basic_stringшаблонным на char, и std::wstringна wchar_t.

char против wchar_t

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

Тогда как насчет Юникода ?

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

В линуксе?

Давайте возьмем ОС 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

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

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

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

На винде?

В 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называются «широкоформатными» (потому что каждый глиф состоит из одного или двух 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?

    В линуксе? Почти никогда (§).
    На винде? Почти всегда (§).
    На кроссплатформенный код? Зависит от вашего инструментария ...

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

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

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

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

    Edit (после комментария от Johann Gerell ):
    a std::stringбудет достаточно для обработки всех charоснованных на строках строк (каждая из charкоторых будет числом от 0 до 255). Но:

    1. Предполагается, что значение ASCII должно быть от 0 до 127. Более высокие charзначения не являются 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тип символа. Предполагается, что он используется для размещения внутри символов, чьи индексы (например, символы Юникода) больше 255 (или 127, в зависимости ...).

paercebal
источник
4
@gnud: Возможно, wchar_t должно было быть достаточно для обработки всех символов UCS-2 (большинства символов UTF-16) до появления UTF-16 ... Или, возможно, у Microsoft были другие приоритеты, кроме POSIX, например, предоставление простого доступа к Unicode без изменения кодированной страницы с использованием char на Win32.
paercebal
4
@ Сорин Sbarnea: UTF-8 может занимать 1-6 байтов, но, очевидно, стандарт ограничивает его 1-4. См. En.wikipedia.org/wiki/UTF8#Description для получения дополнительной информации.
paercebal
8
В то время как эти примеры дают разные результаты в Linux и Windows, программа на C ++ содержит поведение, определяемое реализацией, относительно того olè, кодируется ли это как UTF-8 или нет. Более того, по этой причине вы не можете изначально поток wchar_t *в std::coutтом , что типы несовместимы приводит к плохо сформированной программе , и это не имеет ничего общего с использованием кодировок. Стоит указать, используете ли вы std::stringили std::wstringзависит от ваших собственных предпочтений кодирования, а не от платформы, особенно если вы хотите, чтобы ваш код был переносимым.
Джон Лейдгрен
14
Windows на самом деле использует UTF-16, и в течение достаточно долгого времени старые версии Windows использовали UCS-2, но это уже не так. Моя единственная проблема здесь - это вывод, который std::wstringследует использовать в Windows, потому что он лучше подходит для Unicode Windows API, который я считаю ошибочным. Если ваша единственная задача - вызывать Unicode Windows API, а не сортировать строки, тогда, конечно, но я не покупаю это как общий случай.
Джон Лейдгрен
15
@ Джон Лейдгрен: If your only concern was calling into the Unicode Windows API and not marshalling strings then sureТогда мы согласны. Я пишу на C ++, а не JavaScript. В основе этого языка лежит предотвращение бесполезной сортировки или любой другой потенциально дорогостоящей обработки во время выполнения, когда это может быть выполнено во время компиляции. Кодирование с использованием WinAPI и его использование std::string- это просто неоправданная трата ресурсов времени выполнения. Вы находите это ошибочным, и это нормально, так как это ваша точка зрения. Мой собственный - я не буду писать код с пессимизацией в Windows только потому, что он выглядит лучше со стороны Linux.
paercebal
71

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

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

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

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

  1. Несколько слабых причин. Он существует по историческим причинам, когда считалось, что Widechars является правильным способом поддержки Unicode. Теперь он используется для интерфейса API, которые предпочитают строки UTF-16. Я использую их только в непосредственной близости от таких вызовов API.
  2. Это не имеет ничего общего с std :: string. Он может содержать любую кодировку, которую вы положили в него. Вопрос только в том, как вы относитесь к его содержанию. Я рекомендую UTF-8, поэтому он сможет правильно удерживать все символы Юникода. Это обычная практика для Linux, но я думаю, что программы для Windows также должны это делать.
  3. Нет.
  4. Широкий символ - это запутанное имя. В первые дни Unicode существовало мнение, что символ может быть закодирован в два байта, отсюда и название. Сегодня это означает «любая часть символа длиной два байта». UTF-16 рассматривается как последовательность таких байтовых пар (или широких символов). Символ в UTF-16 занимает одну или две пары.
Павел Радзивиловский
источник
37

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

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

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

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

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

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

  4. используйте std :: wstring для строк в кодировке UCS-2 ( typedef std::wstring UCS2String) - это компромисс и уступка путанице, которую представил API-интерфейс WIN32). 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?

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

Frunsi
источник
Черт, нехорошо знать, что встроенной поддержки Unicode нет.
Михай Данила
@Frunsi, мне любопытно узнать, пробовал ли ты Glib :: ustring и если да, что ты думаешь?
Кэролайн Белтран
@CarolineBeltran: Я знаю Glib, но я никогда не использовал его, и я, вероятно, никогда даже не буду его использовать, потому что он довольно ограничен довольно неопределенной целевой платформой (unixoid системами ...). Его порт Windows основан на внешнем уровне win2unix, и, по-моему, там вообще нет уровня совместимости с OSX. Все эти вещи явно направлены в неправильном направлении, по крайней мере, для моего кода (на этом уровне арки ...) ;-) Итак, Glib не вариант
Frunsi
9
Поиск, замена и т. Д. Прекрасно работает со строками UTF-8 (часть последовательности байтов, представляющая символ, никогда не может быть неверно истолкована как другой символ). На самом деле, UTF-16 и UTF-32 совсем не упрощают этого: на практике все три кодирования являются многобайтовыми кодировками, потому что воспринимаемый пользователем символ (кластер графем) может иметь любое количество кодовых точек юникода! Прагматичное решение - использовать UTF-8 для всего и конвертировать в UTF-16 только при работе с Windows API.
Даниил
5
@Frunsi: Поиск и замена работают так же хорошо с UTF-8, как и с UTF-32. Именно потому, что правильная обработка текста с поддержкой Unicode должна в любом случае иметь дело с «символами» с несколькими кодами, использование кодировки переменной длины, например UTF-8, не делает обработку строк более сложной. Так что просто используйте UTF-8 везде. Обычные строковые функции Си будут нормально работать в UTF-8 (и будут соответствовать порядковым сравнениям в строке Юникода), и если вам нужно что-то более ориентированное на язык, вам все равно придется обращаться к библиотеке Юникода, UTF-16/32 не могу спасти тебя от этого.
Даниил
25
  1. Когда вы хотите, чтобы широкие символы сохранялись в вашей строке. wideзависит от реализации. Visual C ++ по умолчанию 16 бит, если я правильно помню, в то время как GCC по умолчанию в зависимости от цели. Это 32 бита здесь. Обратите внимание, что wchar_t (тип широких символов) не имеет ничего общего с юникодом. Просто гарантируется, что он может хранить все члены самого большого набора символов, поддерживаемого реализацией его локалями, и, по крайней мере, столько же, сколько и char. Вы также можете хранить строки в кодировке Юникод, std::stringиспользуя utf-8кодировку. Но он не поймет значения кодовых точек юникода. Такstr.size() , не используя количество логических символов в вашей строке, а просто количество символов char или wchar_t, хранящихся в этой строке / wstring. По этой причине разработчики gtk / glib C ++ разработали Glib::ustringкласс, который может обрабатывать utf-8.

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

  2. Да, длина символа всегда не менее 8 бит, что означает, что он может хранить все значения ASCII.
  3. Да, все основные компиляторы поддерживают это.
Йоханнес Шауб - Литб
источник
Мне интересно о # 2. Я думал, что 7 бит будет технически действительным тоже? Или требуется иметь возможность хранить что-либо после 7-битных символов ASCII?
Джалф
1
да, Джалф c89 определяет минимальные диапазоны для базовых типов в своей документации limit.h (для unsigned char, это 0..255 мин) и чисто двоичной системы для целочисленных типов. это следует за char, unsigned char и char со знаком имеют минимальные битовые длины 8. c ++ наследует эти правила.
Йоханнес Шауб - лит
15
«Это означает, что функция s.size () вашего wstring вернет правильное количество элементов wchar_t и логических символов». Это не совсем точно, даже для Unicode. Было бы точнее сказать кодовую точку, чем «логический символ», даже в UTF-32 данный символ может состоять из нескольких кодовых точек.
Логан Капальдо
Вы, ребята, в сущности говорите, что C ++ не имеет встроенной поддержки набора символов Unicode?
Михай Данила
1
«Но он не поймет значения кодовых точек юникода». На окнах тоже нет std::wstring.
Дедупликатор
5

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

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

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


источник
1
Хуан: Вы имеете в виду, что std :: string может содержать все символы Юникода, но длина будет сообщаться неправильно? Есть ли причина, по которой он сообщает неверную длину?
3
При использовании кодировки utf-8 один символ Unicode может состоять из нескольких байтов. Вот почему кодировка utf-8 меньше при использовании в основном символов из стандартного набора ascii. Вам нужно использовать специальные функции (или свернуть свои собственные), чтобы измерить количество символов Юникода.
2
(Специфично для Windows) Большинство функций ожидают, что строка, использующая байты, является ASCII, а 2 байта - Unicode, более старые версии MBCS. Это означает, что если вы храните 8-битный Unicode, вам придется преобразовать в 16-битный Unicode для вызова стандартной функции Windows (если только вы не используете только часть ASCII).
Грег Домжан
2
Мало того, что std :: string сообщит неверную длину, но также выведет неверную строку. Если какой-то символ Unicode представлен в UTF-8 в виде нескольких байтов, которые std :: string воспринимает как свои собственные символы, то ваши типичные процедуры манипуляции std :: string будут, вероятно, выводить несколько странных символов, которые являются результатом неправильной интерпретации одного из них. правильный характер.
Михай Данила
2
Я предлагаю изменить ответ, чтобы указать, что строки следует рассматривать как только контейнеры байтов, и, если байты представляют собой кодировку Unicode (UTF-8, UTF-16, ...), то вам следует использовать специальные библиотеки, которые понимают который. Стандартные API на основе строк (длина, substr и т. Д.) Будут с треском проваливаться с многобайтовыми символами. Если это обновление будет сделано, я уберу свое понижение.
Михай Данила
4
  1. Когда вы хотите хранить «широкие» (Unicode) символы.
  2. Да: 255 из них (исключая 0).
  3. Да.
  4. Вот вступительная статья: http://www.joelonsoftware.com/articles/Unicode.html
ChrisW
источник
11
std :: string может содержать 0 очень хорошо (только будьте осторожны, если вы вызываете метод c_str ())
Мистер Фуз
3
И, строго говоря, символ не обязательно должен быть 8 битами. :) Ваша ссылка в # 4 является обязательной для прочтения, но я не думаю, что она отвечает на вопрос. Широкий символ не имеет ничего общего с юникодом. Это просто более широкий характер. (Насколько шире зависит от ОС, но обычно 16 или 32 бит)
jalf
2
  1. когда вы хотите использовать строки Unicode, а не только ascii, полезно для интернационализации
  2. да, но это не очень хорошо с 0
  3. не знаю ни одного, что не
  4. Широкий символ - это специфический для компилятора способ обработки представления фиксированной длины символа юникода, для MSVC это 2-байтовый символ, для gcc я понимаю, что это 4 байта. и +1 для http://www.joelonsoftware.com/articles/Unicode.html
Грег Домян
источник
1
2. Std :: string может содержать символ NULL просто отлично. Он также может содержать utf-8 и широкие символы.
@Juan: Это снова привело меня в замешательство. Если std :: string может хранить символы Юникода, что особенного в std :: wstring?
1
@Appu: std :: string может содержать символы Unicode UTF-8. Существует ряд стандартов Юникода, ориентированных на разную ширину символов. UTf8 имеет ширину 8 бит. Также есть UTF-16 и UTF-32 с шириной 16 и 32 бита соответственно
Грег Д
С помощью std :: wstring. Каждый символ Юникода может быть одним wchar_t при использовании кодировок фиксированной длины. Например, если вы решите использовать программный подход joel в качестве ссылки на Грега. Тогда длина строки - это точно количество символов Юникода в строке. Но это занимает больше места
Я не говорил, что он не может содержать 0 '\ 0', и то, что я имел в виду, не очень хорошо показывает, что некоторые методы могут не дать ожидаемого результата, содержащего все данные wstring. Столь резкие голоса.
Грег Домжан
2

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

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

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

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

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

Сеппо Энарви
источник
Итак, перефразируя первый абзац: приложению, требующему более 256 символов, необходимо использовать многобайтовую кодировку или Maybe_multibyte-кодировку.
Дедупликатор
Обычно 16- и 32-битные кодировки, такие как UCS-2 и UCS-4, не называются многобайтовыми. Стандарт C ++ различает многобайтовые кодировки и широкие символы. В широком представлении символов используется фиксированное число (обычно более 8) битов на символ. Кодировки, которые используют один байт для кодирования наиболее распространенных символов и несколько байтов для кодирования остальной части набора символов, называются многобайтовыми кодировками.
Сеппо Энарви
Извините, небрежный комментарий. Должен был сказать кодирование переменной длины. UTF-16 - это кодировка переменной длины, как и UTF-8. Притворяться, что это не плохая идея.
Дедупликатор
Неплохо подмечено. Нет причин, по которым wstrings нельзя было бы использовать для хранения UTF-16 (вместо UCS-2), но тогда удобство кодирования фиксированной длины теряется.
Сеппо Энарви
2

Хороший вопрос! Я думаю, что ДАННОЕ КОДИРОВАНИЕ (иногда также используется 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)

Leiyi.China
источник
0

Здесь есть несколько очень хороших ответов, но я думаю, что могу добавить пару вещей относительно 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. Или, если вы хотите, вы можете использовать функции API Windows для кодирования, используя кодовую страницу, чтобы поместить его в a std::string, но тогда вы, возможно, также не использовали широкий строковый литерал.

станд :: соиЬ

При выводе на консоль с помощью <<вы можете использовать только 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.

Другим вариантом может быть typedef, 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чтобы обойти различия, если просто используете Linux тогда никогда.

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

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

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

Фил Розенберг
источник
1
Относительно «Однако перед передачей исходного кода компилятору VS преобразует кодированный в кодировке UTF-8 текст в кодированный код, а любые символы, отсутствующие в кодовой странице, заменяются символом?». -> Я не думаю, что это правда, когда компилятор использует кодировку UTF-8 (использование /utf-8).
Рой Дантон
Я не знал об этом в качестве варианта. По этой ссылке docs.microsoft.com/en-us/cpp/build/reference/… кажется, что в свойствах проекта нет галочки для выбора, вы должны добавить его в качестве дополнительного параметра командной строки. Хорошее место!
Фил Розенберг
-2

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

4) Проверьте это для широких символов http://en.wikipedia.org/wiki/Wide_character

Рагу
источник
-6

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

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

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


источник
16
@dave: я не знаю, какую головную боль создает UTF-8, которая больше, чем у Widechars (UTF-16). в UTF-16 у вас также есть многосимвольные символы.
Павел Радзивиловский
Проблема в том, что если вы где-нибудь, кроме англоговорящей страны, вы НЕ ДОЛЖНЫ использовать wchar_t. Не говоря уже о том, что в некоторых алфавитах символов намного больше, чем вписывается в байт. Мы были там, в DOS. Кодовая страница шизофрении, нет, спасибо, не более ..
Свифт - Пятничный пирог
1
@Swift Проблема в wchar_tтом, что его размер и значение зависят от ОС. Это просто меняет старые проблемы на новые. Принимая во внимание, что charэто charнезависимо от ОС (по крайней мере, на аналогичных платформах). Таким образом, мы могли бы также просто использовать UTF-8, упаковать все в последовательности chars и сокрушаться о том, что C ++ оставляет нас самих по себе без каких-либо стандартных методов измерения, индексации, поиска и т. Д. В таких последовательностях.
underscore_d
1
@Swift У тебя, кажется, все в порядке. wchar_tтип данных фиксированной ширины, поэтому массив из 10 wchar_tвсегда будет занимать sizeof(wchar_t) * 10байты платформы. И UTF-16 - это кодирование с переменной шириной, в котором символы могут состоять из 1 или 2 16-битных кодовых точек (и s / 16/8 / g для UTF-8).
underscore_d
1
@SteveHollasch wchar_t Представление строки в Windows будет кодировать символы больше, чем FFFF, в качестве особой суррогатной пары, для других потребуется только один элемент wchar_t. Таким образом, это представление не будет совместимо с представлением, созданным компилятором gnu (где все символы меньше FFFF будут иметь нулевое слово перед ними). То, что хранится в wchar_t, определяется программистом и компилятором, а не каким-то соглашением
Swift - Friday Pie