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
как подписанный.