Opened 8 years ago

Closed 8 years ago

Last modified 6 years ago

#158 closed баг (invalid)

Ошибки строковых литералов

Reported by: alx Owned by: dimag
Priority: major Milestone: 2 очередь
Component: ПО MC04-Dispatcher. Пульт диспетчера/техника Keywords:
Cc: san

Description

Во многих местах программы используется конструкция, подобная этой:

QString::fromWCharArray(L"Data/call.png")

Данная конструкция ошибочна, так как текст внутри кавычек закодирован в CP-1251, а символы данной кодировки не являются whide char (wchar_t). CP-1251 - это 8-битная кодировка, все символы которой помещаются в обычный char и, следовательно, такой текст должен образовывать narrow string literal.
Вышеприведенные конструкции необходимо заменить на что-то типа

QString("Data/call.png")

Change History (5)

comment:1 by dimag, 8 years ago

Resolution: fixed
Status: newclosed

Для строк содержащих только ASCII символы можно, но у меня привычка.
Строки с русскими буквами в кодировке CP-1251, Qt будет воспринимать как в кодировке UTF-8, поэтому все сообщения на русском языке я сохраняя как w_char строку, тогда символы будут преобразовываться в кодировку по умолчанию принятую в QString корректно. QString не знает кодировку CP-1251, но знает UNICODE, поэтому я сохраняю строку в формате Unicode.
При передаче char * русских строковых QString воспримет их UTF-8 строку. Поэтому я задаю русские строковые константы в UNICODE.

comment:2 by alx, 8 years ago

Resolution: fixed
Status: closedreopened

Открываю тикет снова, так как тикет был закрыт, но ошибка не была исправлена.

Для строк содержащих только ASCII символы можно, но у меня привычка.

Проблема не в привычке, а в том, что код ошибочен. Литерал L"abc" является валидным только если текст закодирован в мультибайтовой кодировке, где каждый из составляющих литерал символов - 'a', 'b', 'c' - кодируется несколькими байтами. В вашем случае это не так, исходный текст программы кодирован в кодировке Windows-1251, где каждый символ кодируется одним байтом. Так, в приведенном выше примере между символами кавычек находится три байта - 0x61, 0x62 и 0x63. Теперь посмотрим, как литерал L"abc" будет обрабатываться компилятором. Компилятор должен создать массив элементов типа wchar_t и поместить туда текст внутри кавычек. Допустим, wchar_t имеет размер 2 байта. Компилятор берет два байта 0x61 и 0x62 и помещает их в первый элемент (первый символ wchar_t) массива. Текст в кавычках на этом не закончился, и компилятор начинает формировать второй символ массива, но сталкивается с проблемой - внутри кавычек остался только один байт 0x63, что меньше чем размер wchar_t! Поэтому компилятору ничего не остается кроме как остановить компиляцию и выдать ошибку...

Строки с русскими буквами в кодировке CP-1251, Qt будет воспринимать как в кодировке UTF-8,

Во-первых, Qt в данном случае ни при чем. Ваш код некорректен с точки зрения языка C++.

Во-вторых, если, как Вы пишете, код в одной кодировке (CP-1251) будет восприниматься как в другой кодировке (UTF-8), то, очевидно, что это неправильно и должно быть исправлено.

поэтому все сообщения на русском языке я сохраняя как w_char строку, тогда символы будут преобразовываться в кодировку по умолчанию принятую в QString корректно.

А что, если строка представлена 8-битными символами (то есть как массив char, а не wchar_t), то по-вашему ее символы будут преобразованы в кодировку QString некорректно? Почему? Если Вы установили (вызовом setCodecForCStrings() - см. #170) правильную кодировку (в вашем случае это Windows-1251), то Qt совершенно правильно перекодирует переданный ему массив char из кодировки Windows-1251 в свое внутреннее представление.

А что получается у Вас? Чуть изменим приведенный выше пример - возьмем литерал L"abcd". При двухбайтном wchar_t компилятор создаст массив с двумя (!) символами wchar_t - один с кодом 0x6162, другой - c кодом 0x6364. И fromWCharArray() будет интерпретировать переданный ей аргумент как строку из двух символов в кодировке UCS-2, что не соответствует нашей действительности.

При передаче char * русских строковых QString воспримет их UTF-8 строку.

Неверное утверждение. При вызове конструктора QString(const char * str) строка символов декодируется с помощью функции fromAscii(), которая, в свою очередь, декодирует переданную строку из кодировки, установленной вызовом setCodecForCStrings():

Note that, despite the name, this function actually uses the codec defined by
QTextCodec::setCodecForCStrings() to convert str to Unicode. Depending on the
codec, it may not accept valid US-ASCII (ANSI X3.4-1986) input. If no codec
has been set, this function does the same as fromLatin1().

Смотрите также раздел Converting Between 8-Bit Strings and Unicode Strings в документации.

И еще просьба: если закрываете тикет с решением fixed, указывайте, пожалуйста, ревизию, в которой сделано исправление. Можно прямо в комментарии к коммиту писать "Closes #xxx", тогда указанный тикет будет закрыт автоматически со ссылкой на этот коммит.

comment:3 by alx, 8 years ago

Еще дополню цитатой из википедии:

Both C and C++ introduced fixed-size character types char16_t and char32_t in the 2011 revisions of their respective standards to provide unambiguous representation of 16-bit and 32-bit Unicode transformation formats, leaving wchar_t implementation-defined. The ISO/IEC 10646:2003 Unicode standard 4.0 says that:

"The width of wchar_t is compiler-specific and can be as small as 8 bits. Consequently, programs that need to be portable across any C or C++ compiler should not use wchar_t for storing Unicode text. The wchar_t type is intended for storing compiler-defined wide characters, which may be Unicode characters in some compilers."

comment:4 by alx, 8 years ago

Resolution: invalid
Status: reopenedclosed

Я еще немного изучил вопрос. Оказывается, я был неправ, предполагая, что компилятор "механически" переносит данные из строкового литерала в создаваемый массив. Компилятор выполняет-таки декодирование текста (для чего ему требуется указывать, в какой кодировке закодирован исходный текст).

Таким образом, конструкции типа QString::fromWCharArray(L"Data/call.png") корректны и ошибкой не являются. Другое дело, что лично я не вижу смысла в том, чтобы хранить строковые константы в wchar_t, заведомо зная, что они 8-битные, когда есть методы fromAscii(), fromLocal8Bit(), которые сразу принимают на входе 8-битные строки. Как бы то ни было, тикет закрываю.

comment:5 by san, 6 years ago

Milestone: Текущее2 очередь

Milestone renamed

Note: See TracTickets for help on using tickets.