Использовать подписанный или неподписанный символ при создании CString? - PullRequest
0 голосов
/ 02 ноября 2018

Я проверяю документ на CString . В следующем заявлении:

  • CStringT( LPCSTR lpsz ): Создает Unicode CStringT из строки ANSI. Вы также можете использовать этот конструктор для загрузки строкового ресурса, как показано в примере ниже.

  • CStringT( LPCWSTR lpsz ): Создает CStringT из строки Unicode.

  • CStringT( const unsigned char* psz ): позволяет построить CStringT из указателя на unsigned char.

У меня есть несколько вопросов:

  1. Почему существует две версии, одна для const char* (LPCSTR) и одна для unsigned char*? Какую версию я должен использовать для разных случаев? Например, CStringT("Hello") использует первую или вторую версию? При получении строки с нулевым символом в конце от стороннего производителя, такой как sqlite3_column_text() ( см. Здесь ), следует ли преобразовать ее в char* или unsigned char *? то есть я должен использовать CString((LPCSTR)sqlite3_column_text(...)) или CString(sqlite3_column_text(...))? Кажется, что оба будут работать, верно?

  2. Почему версия char* создает "Unicode" CStringT, а версия unsigned char* создает CStringT? CStringT является шаблонным классом для обозначения всех 3 экземпляров, т. Е. CString, CStringA, CStringW, так почему акцент на "Unicode" CStringT при построении с использованием LPCSTR (const char*)?

Ответы [ 2 ]

0 голосов
/ 02 ноября 2018

tl; dr: используйте любое из следующего:

  • CStringW value( sqlite3_column_text16() ); (при желании можно установить внутреннюю кодировку SQLite в UTF-16) или
  • CStringW value( CA2WEX( sqlite3_column_text(), CP_UTF8 ) );

Все остальное просто не сработает, так или иначе.


Перво-наперво: CStringT - это класс template , параметризованный (среди прочего) для типа символа, который он использует для представления сохраненной последовательности. Это передается как аргумент типа шаблона BaseType. Существует 2 конкретных экземпляра шаблона, CStringA и CStringW, которые используют char и wchar_t для хранения последовательности символов соответственно 1 .

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

  • XCHAR: тип символа, используемый для хранения последовательности.
  • YCHAR: тип символа, из которого экземпляр можно преобразовать в /.

В следующей таблице приведены типы бетона для CStringA и CStringW:

         | XCHAR   | YCHAR
---------+---------+--------
CStringA | char    | wchar_t
CStringW | wchar_t | char

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

  • char представляет кодированные кодовые единицы ANSI 2 .
  • whcar_t представляет кодированные единицы кода UTF-16.

Если ваша программа не соответствует этим предположениям, настоятельно рекомендуется отключить неявные преобразования между широкими и узкими и узкими в широкие. Для этого перед включением любых заголовочных файлов ATL / MFC определен символ препроцессора _CSTRING_DISABLE_NARROW_WIDE_CONVERSION. Рекомендуется делать это, даже если ваша программа соответствует предположениям для предотвращения случайных преобразований, которые являются как дорогостоящими, так и потенциально разрушительными.

После этого давайте перейдем к вопросам:

Почему существует две версии, одна для const char* (LPCSTR) и одна для unsigned char*?

Это просто: удобство. Перегрузка просто позволяет вам создать экземпляр CString независимо от подписи типа символа 3 . Реализация перегрузки, принимающая const unsigned char* аргумент 'forward' к c'tor, принимающая const char*:

CSTRING_EXPLICIT CStringT(_In_z_ const unsigned char* pszSrc) :
    CThisSimpleString( StringTraits::GetDefaultManager() )
{
    *this = reinterpret_cast< const char* >( pszSrc );
}

Какую версию мне использовать для разных случаев?

Это не имеет значения, пока вы создаете CStringA, т.е. преобразование не применяется. Если вы создаете CStringW, вам не следует использовать ни один из них (как описано выше).

Например, CStringT("Hello") использует первую или вторую версию?

"Hello" имеет тип const char[6], который преобразуется в const char* по отношению к первому элементу в массиве при передаче в CString c'tor. Вызывает перегрузку с аргументом const char*.

При получении строки с нулевым символом в конце от стороннего производителя, такой как sqlite3_column_text() ( см. Здесь ), следует ли преобразовать ее в char* или unsigned char *? то есть я должен использовать CString((LPCSTR)sqlite3_column_text(...)) или CString(sqlite3_column_text(...))?

SQLite предполагает кодировку UTF-8 в этом случае. CStringA может хранить текст в кодировке UTF-8, но на самом деле действительно опасно делать. CStringA предполагает кодировку ANSI, и читатели вашего кода, вероятно, тоже будут делать это. Рекомендуется либо изменить базу данных SQLite для хранения UTF-16 (и использовать sqlite_column_text16) для построения CStringW. Если это невозможно, вручную преобразуйте из UTF-8 в UTF-16 перед сохранением данных в экземпляре CStringW, используя макрос CA2WEX :

CStringW data( CA2WEX( sqlite3_column_text(), CP_UTF8 ) );

Кажется, что оба будут работать, верно?

Это не правильно. Ни один из них не работает, как только вы получаете не-ASCII символы из вашей базы данных.

Почему версия char* создает "Unicode" CStringT, а версия unsigned char* создает CStringT?

Это похоже на то, что документация пытается быть компактной. A CStringT - это шаблон класса. Это не Unicode и даже не существует. Я предполагаю, что раздел замечаний по конструкторам предназначен для того, чтобы подчеркнуть возможность построения строк Unicode из входных данных ANSI (и наоборот). Это также кратко упомянуто ( "Обратите внимание, что некоторые из этих конструкторов действуют как функции преобразования." ).

Чтобы подвести итог, вот список общих советов при использовании строк MFC / ATL:

  • Предпочитают использовать CStringW. Это единственный тип строки, чья подразумеваемая кодировка символов однозначна (UTF-16).
  • Используйте только CStringA при взаимодействии с устаревшим кодом. Обязательно запишите однозначно используемую кодировку символов. Также убедитесь, что «текущая активная локаль» может измениться в любое время. См. Следите за кодовой страницей: это строка CP_ACP или UTF-8? для получения дополнительной информации.
  • Никогда использовать CString. Просто глядя на код, уже не ясно, что это за тип (может быть любого из 2 типов). Аналогично, при рассмотрении вызова конструктора больше невозможно увидеть, является ли это операцией копирования или преобразования.
  • Отключить неявные преобразования для экземпляров шаблона класса CStringT.

1 Также есть CString, который использует отображение общего текста TCHAR в качестве BaseType. TCHAR расширяется до char или wchar_t, в зависимости от символов препроцессора. Таким образом, CString является псевдонимом для CStringA или CStringW в зависимости от тех же символов препроцессора. Если вы не нацелены на Win9x, не используйте какие-либо сопоставления с общим текстом.

2 В отличие от кодировок Unicode, ANSI не является автономным представлением. Интерпретация единиц кода зависит от внешнего состояния (текущей активной локали). Не использовать, если вы не взаимодействуете с устаревшим кодом.

3 Определяется реализацией, интерпретируется ли char как подписанный или неподписанный. В любом случае, char, unsigned char и signed char - это 3 разных типа. По умолчанию Visual Studio интерпретирует char как подписанный.

0 голосов
/ 02 ноября 2018

LPCSTR это просто const char*, а не const signed char*. char подписан или не подписан в зависимости от реализации компилятора, но char, signed char и unsigned char являются 3 различными типами для целей перегрузки. Строковые литералы в C ++ имеют тип const char[], поэтому CStringT("Hello") всегда будет использовать конструктор LPCSTR, а не конструктор unsigned char*.

sqlite3_column_text(...) возвращает unsigned char*, поскольку возвращает текст в кодировке UTF-8. Я не знаю, что на самом деле делает unsigned char* конструктор CStringT (он как-то связан со строками MBCS), но конструктор LPCSTR выполняет преобразование из ANSI в UNICODE, используя локаль пользователя по умолчанию. Это уничтожило бы текст UTF-8, который содержит символы не ASCII.

Лучшим вариантом в этом случае является преобразование текста UTF-8 в UTF-16 (используя MultiByteToWideChar() или эквивалентный или просто вместо этого sqlite3_column_text16(), который возвращает кодированный в UTF-16 текст), а затем используйте LPCWSTR (const wchar_t*) конструктор CStringT, поскольку Windows использует wchar_t для данных UTF-16.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...