Я не могу понять разницу между std::string
и std::wstring
. Я знаю, wstring
поддерживает широкие символы, такие как символы Юникода. У меня есть следующие вопросы:
- Когда я должен использовать
std::wstring
болееstd::string
? - Может ли
std::string
содержать весь набор символов ASCII, включая специальные символы? - Является ли
std::wstring
поддерживаются компиляторами всех популярных C ++? - Что такое « широкий характер »?
Ответы:
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). Следующий код:
выводит следующий текст:
Вы увидите, что текст «оле» в
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 для получения дополнительной информации.
Вывод
Когда я должен использовать std :: wstring поверх std :: string?
В линуксе? Почти никогда (§).
На винде? Почти всегда (§).
На кроссплатформенный код? Зависит от вашего инструментария ...
(§): если вы не используете инструментарий / фреймворк, говорящий иначе
Может ли
std::string
содержать весь набор символов ASCII, включая специальные символы?Примечание: A
std::string
подходит для хранения «двоичного» буфера, гдеstd::wstring
нет!В линуксе? Да.
На винде? Только специальные символы доступны для текущей локали пользователя Windows.
Edit (после комментария от Johann Gerell ):
a
std::string
будет достаточно для обработки всехchar
основанных на строках строк (каждая изchar
которых будет числом от 0 до 255). Но:char
значения не являются ASCII.char
от 0 до 127 будет проведена правильноchar
от 128 до 255 будет иметь значение в зависимости от вашей кодировки (Unicode, Non-Unicode и т. д.), но оно сможет содержать все символы Unicode до тех пор, пока они закодированы в UTF-8.Является ли
std::wstring
поддерживается практически всеми компиляторами популярным C ++?В основном, за исключением компиляторов на основе GCC, которые портированы на Windows.
Он работает на моем g ++ 4.3.2 (под Linux), и я использовал Unicode API на Win32 начиная с Visual C ++ 6.
Что такое широкий характер?
На C / C ++ это написанный тип символа,
wchar_t
который больше, чем простойchar
тип символа. Предполагается, что он используется для размещения внутри символов, чьи индексы (например, символы Юникода) больше 255 (или 127, в зависимости ...).источник
olè
, кодируется ли это как UTF-8 или нет. Более того, по этой причине вы не можете изначально потокwchar_t *
вstd::cout
том , что типы несовместимы приводит к плохо сформированной программе , и это не имеет ничего общего с использованием кодировок. Стоит указать, используете ли выstd::string
илиstd::wstring
зависит от ваших собственных предпочтений кодирования, а не от платформы, особенно если вы хотите, чтобы ваш код был переносимым.std::wstring
следует использовать в Windows, потому что он лучше подходит для Unicode Windows API, который я считаю ошибочным. Если ваша единственная задача - вызывать Unicode Windows API, а не сортировать строки, тогда, конечно, но я не покупаю это как общий случай.If your only concern was calling into the Unicode Windows API and not marshalling strings then sure
Тогда мы согласны. Я пишу на C ++, а не JavaScript. В основе этого языка лежит предотвращение бесполезной сортировки или любой другой потенциально дорогостоящей обработки во время выполнения, когда это может быть выполнено во время компиляции. Кодирование с использованием WinAPI и его использованиеstd::string
- это просто неоправданная трата ресурсов времени выполнения. Вы находите это ошибочным, и это нормально, так как это ваша точка зрения. Мой собственный - я не буду писать код с пессимизацией в Windows только потому, что он выглядит лучше со стороны Linux.Я рекомендую избегать
std::wstring
в Windows или где-либо еще, за исключением случаев, когда это требуется интерфейсом, или где-нибудь рядом с вызовами Windows API и соответствующими преобразованиями кодировки в качестве синтаксического сахара.Мое мнение обобщено на http://utf8everywhere.org, соавтором которого я являюсь.
Если ваше приложение не ориентировано на вызовы API, например, в основном приложение пользовательского интерфейса, рекомендуется хранить строки Unicode в std :: string и кодировать в UTF-8, выполняя преобразование вблизи вызовов API. Преимущества, изложенные в статье, перевешивают очевидное раздражение конверсии, особенно в сложных приложениях. Это вдвойне верно для мультиплатформенной и библиотечной разработки.
А теперь, отвечая на ваши вопросы:
источник
Таким образом, каждый читатель здесь должен иметь четкое представление о фактах, ситуации. Если нет, то вы должны прочитать выдающийся исчерпывающий ответ Paercebal [кстати: спасибо!].
Мой прагматический вывод шокирующе прост: все эти «кодировки символов» в C ++ (и STL) по сути сломаны и бесполезны. Винить это в Microsoft или нет, это все равно не поможет.
Мое решение, после глубокого изучения, большого разочарования и вытекающих из этого событий, заключается в следующем:
согласитесь, что вы сами должны нести ответственность за кодирование и преобразование (и вы увидите, что многое из этого довольно тривиально)
используйте std :: string для любых строк в кодировке UTF-8 (просто a
typedef std::string UTF8String
)допустим, что такой объект UTF8String - просто тупой, но дешевый контейнер. Никогда не открывайте и / или не манипулируйте в нем символами напрямую (без поиска, замены и т. Д.). Вы могли бы, но вы действительно просто действительно не хотите тратить свое время на написание алгоритмов манипулирования текстом для многобайтовых строк! Даже если другие люди уже делали такие глупости, не делайте этого! Будь как будет! (Ну, есть сценарии, в которых это имеет смысл ... просто используйте для этого библиотеку ICU).
используйте std :: wstring для строк в кодировке UCS-2 (
typedef std::wstring UCS2String
) - это компромисс и уступка путанице, которую представил API-интерфейс WIN32). UCS-2 достаточно для большинства из нас (подробнее об этом позже ...).использовать экземпляры UCS2String всякий раз, когда требуется посимвольный доступ (чтение, манипулирование и т. д.). Любая символьная обработка должна выполняться в не многобайтовом представлении. Это просто, быстро, легко.
добавьте две служебные функции для преобразования туда и обратно между UTF-8 и UCS-2:
Преобразования просты, 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?
Для продвинутых вещей.
источник
Когда вы хотите, чтобы широкие символы сохранялись в вашей строке.
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 элементов и логических символов.источник
std::wstring
.Я часто использую std :: string для хранения символов utf-8 без каких-либо проблем. Я искренне рекомендую делать это при взаимодействии с API, которые используют utf-8 в качестве собственного типа строки.
Например, я использую utf-8 при взаимодействии моего кода с интерпретатором Tcl.
Главное предостережение - это длина std :: string, больше не количество символов в строке.
источник
источник
источник
Приложения, которые не удовлетворены только 256 различными символами, могут либо использовать широкие символы (более 8 бит), либо кодирование переменной длины (многобайтовое кодирование в терминологии C ++), например UTF-8. Широкие символы обычно требуют больше места, чем кодирование переменной длины, но быстрее обрабатываются. Многоязычные приложения, которые обрабатывают большие объемы текста, обычно используют широкие символы при обработке текста, но преобразуют его в UTF-8 при сохранении его на диск.
Единственная разница между a
string
и awstring
- это тип данных символов, которые они хранят. В строке хранитсяchar
s, размер которого гарантированно должен быть не менее 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 вашего пользовательского интерфейса, вероятно, поддерживает преобразование.источник
Хороший вопрос! Я думаю, что ДАННОЕ КОДИРОВАНИЕ (иногда также используется 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)
источник
Здесь есть несколько очень хороших ответов, но я думаю, что могу добавить пару вещей относительно Windows / Visual Studio. Это основано на моем опыте с VS2015. В Linux в основном ответом является использование UTF-8, закодированного
std::string
везде. На Windows / VS это становится более сложным. Вот почему. Windows ожидает, что строки, хранящиеся с использованиемchar
s, будут закодированы с использованием кодовой страницы локали. Это почти всегда набор символов 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 для кодирования, используя кодовую страницу, чтобы поместить его в astd::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, а затем кодбыло бы хорошо на любой платформе, я думаю.
ответы
Так что ответить на ваши вопросы
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 байта.источник
/utf-8
).1) Как упомянул Грег, wstring полезен для интернационализации, именно тогда вы будете выпускать свой продукт на других языках, кроме английского
4) Проверьте это для широких символов http://en.wikipedia.org/wiki/Wide_character
источник
Когда не следует использовать широкие символы?
Когда вы пишете код до 1990 года.
Очевидно, я переворачиваюсь, но на самом деле сейчас 21-й век. 127 персонажей уже давно перестали быть достаточными. Да, вы можете использовать UTF8, но зачем беспокоиться о головной боли?
источник
wchar_t
том, что его размер и значение зависят от ОС. Это просто меняет старые проблемы на новые. Принимая во внимание, чтоchar
этоchar
независимо от ОС (по крайней мере, на аналогичных платформах). Таким образом, мы могли бы также просто использовать UTF-8, упаковать все в последовательностиchar
s и сокрушаться о том, что C ++ оставляет нас самих по себе без каких-либо стандартных методов измерения, индексации, поиска и т. Д. В таких последовательностях.wchar_t
тип данных фиксированной ширины, поэтому массив из 10wchar_t
всегда будет заниматьsizeof(wchar_t) * 10
байты платформы. И UTF-16 - это кодирование с переменной шириной, в котором символы могут состоять из 1 или 2 16-битных кодовых точек (и s / 16/8 / g для UTF-8).