LINUX.ORG.RU

Неправильная длина строки std::string в C++

 


3

1
#include <iostream>
#include <string>

using namespace std;

string getStringInEnglish()
{
	return "Masha was walking on the highway.";
}

string getStringInRussian()
{
	return "Шла Маша по шоссе.";
}

int main()
{
	//В строке всего 18 символов, а показывет 32
	cout << getStringInRussian().length() << endl;

	//Показывает правильно
	cout << getStringInEnglish().length() << endl;
	
	return 0;
}
★★

UTF-8, multibyte character set. Сейчас вбежит Eddy_Em и объяснит почему это плохо.

Deleted ()
wstring getStringInRussian()
{
  return L"Шла Маша по шоссе.";
}
Deleted ()

string хранит строки в точности так, как они были введены; как массив ненулевых байт. При этом std::string::length() возвращает длину в байтах. Следовательно, результат работы твоей программы зависит от того, в какой кодировке её исходник.

intelfx ★★★★★ ()
Ответ на: комментарий от Razip

Строка конвертируется компилятором из твоей кодировки в wchar_t и хранится в памяти уже как массив wchar_t, где каждый элемент — строго один символ. Тогда std::wstring::length() отрабатывает корректно.

intelfx ★★★★★ ()
Ответ на: комментарий от intelfx

string хранит строки в точности так, как они были введены; как массив ненулевых байт.

std::string хранит любые байты.

mashina ★★★★★ ()

length показывает не количество символов в строке, а размер в байтах, которые эта строка отжирает.

peregrine ★★★★★ ()
Ответ на: комментарий от mashina

Т. е. строка не обязана быть zero-terminated? Ок, спасибо, учту.

intelfx ★★★★★ ()

Еще один вопрос: как можно вывести такую строку в терминал? cout рыгает ошибками при компиляции.

Razip ★★ ()
Ответ на: комментарий от Deleted

Думаю, ТС и сам поймет, что хрюникод в таких случаях — зло.

Если хрюникод и впердоливать в свои приложения, то только в gettext. Но лучше КОИ8 и в gettext'е вбухивать. Все равно потом в нужную локаль перекодирует.

Eddy_Em ☆☆☆☆☆ ()
Ответ на: комментарий от intelfx

Нет, не правильно. wchar_t не даёт таких гарантий, что каждый элемент строго один символ. Точнее даёт, но только под Linux-ом, где его размер 32 бита, а на той же винде - 16 бит. Вообще он не предназначен для строк в юникоде, т.к. может быть вплоть до 8 бит. Потому более корректно, для работы с юникодом использовать что-то более подходящее, например, Glib::ustring для GTK или QString для Qt.

peregrine ★★★★★ ()
Ответ на: комментарий от peregrine

Точнее даёт, но только под Linux-ом, где его размер 32 бита, а на той же винде - 16 бит.

Нигде не даёт, некоторые печатные символы всегда могут быть разбиты на несколько code point'ов.

mashina ★★★★★ ()
Ответ на: комментарий от peregrine

Внезапно. Т. е. std::wstring::length() тоже может отличаться от количества символов?..

intelfx ★★★★★ ()
Ответ на: комментарий от intelfx

Для кодирования символов кириллицы в utf-8 используется два байта. std::wstring::length() возвращает количество wchar_t в строке, что при удачном стечении обстоятельств может равняться количеству символов. Если уж использовать стандартные строки для посимвольной работы с юникодом, то лучше std::u16string (C++11).
QString внутри хранит все в utf-16, но для представления некоторых символов все равно используются два элемента (т.н. суррогатные пары), что аналогично std::u16string.

m0rph ★★★★★ ()
Ответ на: комментарий от intelfx

Да. Количество байтов памяти, количество code units, количество code points, количество видимых человеку глифов — все эти числа могут быть различными.

ilammy ★★★ ()
Ответ на: комментарий от ilammy

Т. е. в случае труъ-интернационализации понятие «длины строки» не определено (ну разве что в сантиметрах)?.. Я догадывался, но шаблон таки треснул.

intelfx ★★★★★ ()
Ответ на: комментарий от intelfx

Ага. И выводятся они ещё иногда справа налево, а не как у людей.

По сути, есть только две универсальные длины строки: в байтах (для хранения и передачи в неизменном виде) и в пикселях/сантиметрах (для вывода конкретным шрифтом). Для точного определения остальных длин нужно знать ещё кодировку и язык.

ilammy ★★★ ()
Ответ на: комментарий от Chaser_Andrey

Ряд протоколов и форматов использует юникод как стандарт

Вывод: не использовать эти ущербные протоколы и стандарты.

Eddy_Em ☆☆☆☆☆ ()
Ответ на: комментарий от intelfx

Добавил комментарий, хоть ничего нового и не увидел. И эти люди участвуют в разработке системного ПО. ^_^"

Это была одна из первых вещей, которые я узнал, ибо кодировки нужно было различать как-то.

Вообще, комментарий про \0. D:

wakuwaku ★★★★ ()
Последнее исправление: wakuwaku (всего исправлений: 2)
Ответ на: комментарий от wakuwaku

Добавил комментарий, хоть ничего нового и не увидел.

Wut?

И эти люди участвуют в разработке системного ПО.

Кхм, да, я с i18n/l10n дела доселе не имел. Это не мешает мне разбираться в том, в чём разбираюсь.

Вообще, комментарий про \0.

Wut?

intelfx ★★★★★ ()
Ответ на: комментарий от ilammy

При каноничной длине строки в code points?

Deleted ()
Ответ на: комментарий от Deleted

Которая очень важна для строки не в Юникоде. Ну, конечно, как абстрактная длина в попугаях сойдёт, да. (Если закрыть глаза на кодировки, чьё множество представимых символов не является подмножеством Юникода.)

ilammy ★★★ ()
Ответ на: комментарий от Eddy_Em

Думаю, ТС и сам поймет, что хрюникод в таких случаях — зло.

не вижу никакого «зла». Length показывает размер в байтах, не вижу проблемы.

обдумай на досуге, ЗАЧЕМ тебе размер строки? ИМХО первое: выделить под неё память. Но ведь память тоже в байтах, а не в буквах!

ЗЫЖ если хочешь что-то типа «с третий по 17й символ», regex(7) тебе в помощь. Оно это умеет. Причём достаточно быстро. Регулярку написать, или сам?

emulek ()
Ответ на: комментарий от intelfx

Т. е. в случае труъ-интернационализации понятие «длины строки» не определено

определено. Это размер занимаемой В ПАМЯТИ. А на принтере/мониторе — нет конечно. При чём тут юникод? Понятие «символ» тоже в общем случае не определено, есть умляуты и ударения. Как их считать? Есть также невидимые символы(\0 к примеру), есть табуляции, есть перевод строки наконец. Всё это имеет одну и ту же «длину» только в твоём локалхосте, и в «твоём стандарте».

emulek ()
Ответ на: комментарий от emulek

и да, что-бы не рвать шаблон, задайте себе вопрос: ЗАЧЕМ МНЕ ЭТО? Ну зачем вам длинна строки В СИМВОЛАХ? Ежу очевидно, что по-русски она будет другой, по-китайски третей, а по-арабски вообще справа-налево. Т.е. по любому оно будет не представимо в индексе тривиального массива.

emulek ()

если тебе именно в символах, наверное лучше будет libicu воспользоваться

Harald ★★★★★ ()
Ответ на: комментарий от Eddy_Em

Солнышко, вот представь: наступило светлое будущеепрошлое. Никто не юзает юникод, все юзают кодировки. Я запускаю мою любимый словарь-переводчик с беларусского на иврит и оуеваю...

Pavval ★★★★★ ()

Всё просто: С++ к 2015 году из коробки так и не умеет в юникод (с полноценной поддержкой в STL).

mix_mix ★★★★★ ()
Последнее исправление: mix_mix (всего исправлений: 1)
Ответ на: комментарий от mashina

Символы шире 32 бит? Где ты их нашел? Или ты про то, что символы будут меньше и из-за этого будет сдвиг? Используй UTF32, вместо UTF8 в таком случае и будет тебе счастье, ибо там фиксированное число бит на символ.

peregrine ★★★★★ ()
Ответ на: комментарий от peregrine

Символы шире 32 бит?

К битности это не имеет отношения. «Й», например, можно закодировать как U+0419 или { U+0418 U+0306}

mashina ★★★★★ ()
Ответ на: комментарий от emulek

ЗАЧЕМ тебе размер строки?

а) чтобы выделить память
б) чтобы узнать количество символов.

Eddy_Em ☆☆☆☆☆ ()
Ответ на: комментарий от Eddy_Em

а) чтобы выделить память

дык для этого надо в байтах, проблемы?

б) чтобы узнать количество символов.

зачем?

emulek ()
Ответ на: комментарий от emulek

Использовать регулярки для оперирования подстроками это мощно.
//по сабжу, воздержаться от кириллицы в исходниках вообще.

comp00 ★★★★ ()
Ответ на: комментарий от intelfx

string хранит всё что угодно, в том числе и нули в середине, но всегда оканчивается нулем. Есть куча кода в котором string используется вместо vector<char>.

Reset ★★★★★ ()
Ответ на: комментарий от Reset

string хранит всё что угодно, в том числе и нули в середине, но всегда оканчивается нулем

Не оканичвается, string это просто blob. Ноль есть тоько в c_str() для совместимости.

mashina ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.