Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Системное программирование и WinAPI > Использование Unicode, UTF-8, UTF-16


Автор: Emura 22.4.2009, 17:18
Возникли непонятки с использованием unicode и как следствие c utf. по поиску в темах одни куски информации. более полного и внятного изложения не нашел. есть желание разобраться в вопросе. первый пост планирую дополнять полученной информацией.
 
для начала

1.
Код

#include <tchar.h>
//...
int WINAPI _tWinMain(..)
//...


2. в настройках проекта "Character Set" сменить на "Use Unicode Character Set"

3. строки в коде обрамлять макросом  _T("строка")


Текущие вопросы: 
-какой тип данных для строк использовать предпочтительней? (TCHAR, std::wstring или другое ) а главное удобнее
-можно ли использовать TEXT("строка") ?


Автор: GremlinProg 22.4.2009, 19:23
Цитата(Emura @  22.4.2009,  19:18 Найти цитируемый пост)

#define UNICODE
#define _UNICODE

так лучше не делать, это просто делает невозможным смену типа проекта из его настроек (можно назвать этот способ "в лоб")
Цитата(Emura @  22.4.2009,  19:18 Найти цитируемый пост)
в настройках проекта "Character Set" сменить на "Use Unicode Character Set"

идеальный способ
Цитата(Emura @  22.4.2009,  19:18 Найти цитируемый пост)
строки в коде обрамлять макросом  _T("строка")

этот макрос нужно использовать повсюду, на что распространяется влияние пункта 2,, т.е. независимо от того, UNICODE приложение или ANSI
Цитата(Emura @  22.4.2009,  19:18 Найти цитируемый пост)
какой тип данных для строк использовать предпочтительней? (TCHAR, std::wstring или другое ) а главное удобнее

проще всего пользоваться одним видом трансляции: и глазу приятно и мозг не перегружен
из постоянных - это макрос _T(x) и тип данных _TCHAR
все транслируемые CRT-функции имеют так же префикс _t,
который переводит их в 2 группы с префиксами mbs и wcs, что соответственно означает multi byte string(байтовые строки char) и wide char string(широкосимвольные строки wchar_t)

Цитата(Emura @  22.4.2009,  19:18 Найти цитируемый пост)
можно ли использовать TEXT("строка") ?

конечно можно
два макроса: _T(x) и TEXT(x) - идентичны
разница между ними только в том, что первый включен в tchar.h, а второй - в WinNT.h,
что соответственно, и определяет, какой из них доступен в данный момент (если подключается и windows.h и tchar.h, то ограничений нет - пользуй что хочешь)

Добавлено через 5 минут и 30 секунд
Цитата(GremlinProg @  22.4.2009,  21:23 Найти цитируемый пост)
этот макрос нужно использовать повсюду, на что распространяется влияние пункта 2, т.е. независимо от того, UNICODE приложение или ANSI

в принципе, _T можно и не использовать, но тогда по-хорошему, чтобы не было проблем, нужно настраивать кодировку проекта, используя лобовой метод (пункт 1)

тогда, чтобы не было проблем, этот метод нужно расширить:

установка ANSI:
Код

#if defined(_UNICODE)
#undef _UNICODE
#endif
#if defined(UNICODE)
#undef UNICODE
#endif

установка UNICODE:
Код

#if !defined(_UNICODE)
#define _UNICODE
#endif
#if !defined(UNICODE)
#define UNICODE
#endif

Автор: Emura 23.4.2009, 01:28
спасибо за уточнения!

тогда от способа в лоб отказываемся тк он лишний, указываем Character Set через настройки проекта.
и в коде используем _T("строка")

Цитата

проще всего пользоваться одним видом трансляции: и глазу приятно и мозг не перегружен
из постоянных - это макрос _T(x) и тип данных _TCHAR
все транслируемые CRT-функции имеют так же префикс _t,
который переводит их в 2 группы с префиксами mbs и wcs, что соответственно означает multi byte string(байтовые строки char) и wide char string(широкосимвольные строки wchar_t)


_TCHAR и TCHAR в чем суть префикса "_" ? smile 

чтобы быстрее разобраться буду стараться задавать конкретные вопросы smile
планирую:
1) использовать UTF в самописных компонентах (принимать при вводе, для локализации и тп).
2) сохранять UTF в файл и читать от туда, запись осуществлять по-байтово.


ps. читаю попутно ФАКи


Автор: GremlinProg 23.4.2009, 11:53
Цитата(Emura @  23.4.2009,  03:28 Найти цитируемый пост)
_TCHAR и TCHAR в чем суть префикса "_" ?

суть все в том же:
_TCHAR объявлен в tchar.h, а CHAR - в WinNT.h
_ - для разделения типов, поскольку typedef уже генерирует новый тип, т.е. чтобы не было неоднозначности при подключении обоих файлов в один проект; оба идентично определяют транслируемый тип данных char и wchar_t

правило в принципе очевидно:
все, что с префиксом _ - из tchar.h
все, что без него - из WinNT.h

это так же касается и использования самих макосов трансляции: UNICODE и _UNICODE

Добавлено через 11 минут и 50 секунд
Цитата(Emura @  23.4.2009,  03:28 Найти цитируемый пост)
тогда от способа в лоб отказываемся тк он лишний

иногда приложение требуется писать только в одной кодировке, причины могут быть разные: отсутствие поддержки какого-нибудь API UNICODE, или наоборот - ANSI, проблемы с русскими кодировками, и т.п., поэтому, чтобы не вводить в заблуждение остальных участников проекта, следует этот момент четко зафиксировать в коде,

в таком случае и требуется метод "в лоб", т.е. он не лишний, просто требуется он крайне редко и не по делу его использовать не стоит, только когда реально невозможно поддержать в проекте обе ветки

Добавлено через 13 минут и 7 секунд
этот метод можно просто переместить в самый конец, как "случай на крайний случай" )

Автор: GremlinProg 23.4.2009, 12:22
Цитата(Emura @  22.4.2009,  19:18 Найти цитируемый пост)
std::wstring

для std::string и std::wstring нет транслятора, поэтому, при их использовании следует написать транслятор, например такой:
Код

#if defined(_UNICODE)
  #define _tstring  wstring
#else
  #define _tstring  string
#endif

и, соответственно, использовать std::_tstring

либо аналогично объявить typedef для _tstring
либо использовать их применительно к частным случаям: в функциях с префиксами mbs и wcs, или с постфиксом A и W в API, соответственно

Автор: Emura 23.4.2009, 14:38
GremlinProg, спасибо, с _t уяснил.

1) std::wstring и WCHAR - в чем существенные отличия и что использовать удобнее? 
2)  в каких случаях может потребоваться конвертировать WCHAR в multi byte? 
3) в каких случаях может потребоваться работа с LPTSTR ?
4) как определить длину для TCHAR ? (lstrlen ?) подозреваю что нужна функция с префиксом _t (но не нашел)

Код

2) сохранять UTF в файл и читать от туда, запись осуществлять по-байтово.

GremlinProg, направь пожалуйста - какая должна быть последовательность(логика) действий для данной задачи.


Автор: GremlinProg 23.4.2009, 17:42
Цитата(Emura @  23.4.2009,  16:38 Найти цитируемый пост)
сохранять UTF в файл и читать от туда, запись осуществлять по-байтово.

http://forum.vingrad.ru/index.php?showtopic=253883&view=findpost&p=1832431
тут одновременная поддержка 4-х кодировок, одна из которых - ANSI, остальные двухбайтовые
чтение осуществляется посимвольно (исходя из установок _UNICODE)
побайтово это делать не нужно
смотри пример
по крайней мере, чтение шаблона должно быть понятно
можно конечно выделить:
Код

    // определение размера поля BOM
    bomlen    = ftell(input);
    // определение числа символов в файле
    for(len = 0; !feof(input) ;_fgettc(input),++len);
    // распределение буфера под содержимое файла
    lpszBuffer        = (LPTSTR)malloc( (len + 1) * sizeof(_TCHAR) );
    // завершение буфера нуль-терминатором
    lpszBuffer[len]    = _T('\0');
    // перемещение файлового указателя к первому символу
    fseek(input,bomlen,SEEK_SET);
    // чтение файла в распределенный буфер
    for(std_out = 0,lpCh = lpszBuffer; !feof(input) ;*(lpCh++) = _fgettc(input));

Цитата(Emura @  23.4.2009,  16:38 Найти цитируемый пост)
как определить длину для TCHAR ? (lstrlen ?) подозреваю что нужна функция с префиксом _t (но не нашел)

_tcslen
строковые CRT-функции все здесь: http://msdn.microsoft.com/en-us/library/f0151s4x(VS.80).aspx
к любой функции в MSDN есть табличка Generic-Text Routine Mappings, в ней как раз все алиасы для нее и определены
пример таблицы:
Цитата

TCHAR.H routine    _UNICODE & _MBCS not defined    _MBCS defined  _UNICODE defined 
_tcscoll    strcoll     _mbscoll  wcscoll

Цитата(Emura @  23.4.2009,  16:38 Найти цитируемый пост)
в каких случаях может потребоваться работа с LPTSTR 

LPTSTR - это ни что иное как _TCHAR*, соответственно, и требуется он так же, при трансляции _UNICODE
Цитата(Emura @  23.4.2009,  16:38 Найти цитируемый пост)
в каких случаях может потребоваться конвертировать WCHAR в multi byte? 

в случаях, когда нужна байтовая строка: char*, а имеется WCHAR*
Цитата(Emura @  23.4.2009,  16:38 Найти цитируемый пост)
 std::wstring и WCHAR - в чем существенные отличия и что использовать удобнее?

сравнивать их нет смысла
std::wstring - строковый буфер
единственное, что у них общего - тип символа WCHAR

Добавлено через 12 минут и 57 секунд
если поддержка ANSI-файлов не предполагается, то можно работать и так:
Код

    std::wstring buffer;
    WCHAR*lpCh;
    // определение размера поля BOM
    bomlen    = ftell(input);
    // определение числа символов в файле
    for(len = 0; !feof(input) ;fgetwc(input),++len);
    // распределение буфера под содержимое файла
    if( len ){
        buffer.resize(len);
        // перемещение файлового указателя к первому символу
        fseek(input,bomlen,SEEK_SET);
       // чтение файла в распределенный буфер
        for(lpCh = &*buffer.begin(); !feof(input) ;*(lpCh++) = fgetwc(input));
    }

Автор: Emura 23.4.2009, 17:56
GremlinProg, большое спасибо за разъяснения. буду разбираться.

Автор: GremlinProg 23.4.2009, 18:14
незачто, у тебя правильный подход к систематизации знаний,
и правильные вопросы
за это тебе +1
Цитата(Emura @  23.4.2009,  16:38 Найти цитируемый пост)
std::wstring и WCHAR - в чем существенные отличия и что использовать удобнее

удобнее использовать конечно STL-контейнеры, в данном случае std::wstring, std::string

Автор: Emura 24.4.2009, 15:12
Цитата

незачто, у тебя правильный подход к систематизации знаний,
и правильные вопросы


спасибо! скоро правильных вопросов по кодировке будет еще больше smile еще не подошел к ним в плотную.

пс. я с c# перешел на с++ (на c# с php  smile ). в c# много удобств наделали, но в c++ больше свободы и независимость от net само собой. правда все удобства приходится создавать самому.. на что тратиться уйма не лишнего времени. зато потом это должно, точнее просто обязано оправдаться.... smile

Автор: Emura 28.4.2009, 13:07
вот понадобилось сравнить две строки и задумался что использовать

Код

 _tcscmp(s1.c_str(),s2.c_str())

//или  

lstrcmp(s1.c_str(),s2.c_str())


s1 и s2 могут быть как std::string так и std::wstring. на данный момент ориентируюсь на wstring.
может для пущей точности использовать и то и то? что посоветуете?

Автор: GremlinProg 28.4.2009, 13:50
без разницы, обе функции делают одно и то же
разница может быть только если локаль потока отличается от локали CRT:
первая функция использует локаль CRT: setlocale
вторая функция использует локаль потока, в котором она вызывается: GetThreadLocale
(хотя локаль CRT может сама синхронизироваться с локалью потока, не проверял, но в таком случае, результаты работы обеих функций будут уже абсолютно идентичны)
Цитата(Emura @  28.4.2009,  15:07 Найти цитируемый пост)
может для пущей точности использовать и то и то? что посоветуете?

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

Добавлено через 2 минуты и 38 секунд
Цитата(Emura @  28.4.2009,  15:07 Найти цитируемый пост)
s1 и s2 могут быть как std::string так и std::wstring. на данный момент ориентируюсь на wstring

не надо смешивать так же _t-функции с wcs-данными, и наоборот

Добавлено через 3 минуты и 29 секунд
Цитата(GremlinProg @  28.4.2009,  15:50 Найти цитируемый пост)
с wcs-данными

с wcs- и mbs-данными

Автор: Emura 28.4.2009, 14:03
ок, спасибо за совет.

Цитата

не надо смешивать так же _t-функции с wcs-данными, и наоборот


GremlinProg, не понял замечания, ведь _tcscmp расширяется в strcmp и в wcscmp. а если это про то, что wstring строковый буфер это я усвоил smile

Автор: GremlinProg 28.4.2009, 14:46
Цитата(Emura @  28.4.2009,  16:03 Найти цитируемый пост)
а если это про то, что wstring строковый буфер это я усвоил

я про то, что явное использование std::wstring и std::string уже не желательно с функциями _t
правильно, еси они используются неявно, т.е. через транслятор: http://forum.vingrad.ru/index.php?showtopic=256580&view=findpost&p=1850432

Автор: Emura 28.4.2009, 15:43
GremlinProg, да да да, именно этой политике я и придерживаюсь smile 

Автор: Emura 14.5.2009, 19:38
подскажите пожалуйста последовательность действий для следующей задачи.
есть файл, текстовый в UTF-8. пытаюсь нарисовать его содержимое.

с "нарисовать" думаю проблем нет, для этого нужен массив WCHAR. ...а вот как его извлеч из файла -призадумался чтото, хотя задача то вроде банальная.

Автор: GremlinProg 14.5.2009, 19:47
извлечь...
я же давал алгоритм

может что-то непонятно?

Код

FILE*input;
_tfopen_s(&input,_T("C:\\файл.txt"),_T("r, ccs=UTF-8"));

далее чтение: http://forum.vingrad.ru/index.php?showtopic=256580&view=findpost&p=1850770
Код

fclose(input);

lpszBuffer или  buffer (там 2 способа), после этого будет содержать контент из UTF-8-файла

Автор: Emura 14.5.2009, 20:04
да-да, смотрел, но недопонял что-то, каша в голове от этих кодировок.. строк smile 

cейчас читаю файл в CHAR* так

Код

            if((textSize = GetFileSize(hFile, 0)) <= 0)// получаем размер в байтах
            {
                return false;
            }

            if((Text = new CHAR[textSize]) == 0)// инициализируем текстовый буфер
            {
                return false;
            }

            ReadFile(hFile, Text, textSize, &readSize, 0);// читаем файл



может сначала прочитать в байты потом их конвертировать в CHAR или WCHAR? 

Код

// определение числа символов в файле
    for(len = 0; !feof(input) ;fgetwc(input),++len);

GremlinProg, а есть еще варианты получить кол-во?



Автор: GremlinProg 14.5.2009, 20:17
Emura, символ UTF-8 - не имеет постоянной длины (тут ANSI и UNICODE смешаны),
только ради расчета числа символов, я и прохожу файл дважды:
  первый раз, чтобы получить число символов, перед распределением буфера
  второй раз, чтобы их считать

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

и проще всего для этого использвать CRT-стримы (stream), а не файлы API (CreateFile),
потому что эта задача в CRT уже решена, велосипед изобретать не надо

Автор: Emura 14.5.2009, 20:29
ок, спасибо за советы.

кстати, вот попробовал такое -рисует не потребное smile 

Код

    WCHAR* tow(const CHAR* c, size_t size)
    {
        WCHAR* w = new WCHAR[size];
        mbstowcs(w,c,size);
        return w;
    };    
//...

        CHAR test[] ="тест"; 
        WCHAR *temp = tow(test, strlen(test)+1);

        ExtTextOutW(
            hdc, 
            0, 
            0, 
            ETO_CLIPPED, 
            &r, 
            temp, 
            wcslen(temp), 
            0);

Автор: GremlinProg 14.5.2009, 20:51
Цитата(Emura @  14.5.2009,  22:04 Найти цитируемый пост)
а есть еще варианты получить кол-во?

есть, но цикл все равно нужен:
Код

FILE file;
//
file._flag    = _IOREAD|_IOSTRG|_IOMYBUF;
file._ptr    = file._base = (char*)lpszInput;
file._cnt    = filesize_in_bytes;
for(len = 0; !feof(&file) ;fgetwc(&file),++len);

lpszInput - байтовый буфер, в который предварительно считан файл
причем тут нужно четко определить, имеется ли в файле BOM
это поле идентифиуирует кодировку файла в целом
для UTF-8, это первые 3 байта EF BB BF
если они присутствуют, то их нужно обязательно либо пропустить, либо вообще выбросить
все это уже делает _tfopen_s, без нее - проверяй вручную, но перед инициализацией переменной file

Автор: GremlinProg 14.5.2009, 21:23
нет, похоже так не получится
тут нужно еще указать кодировку в переменной file
а в CRT она ассоциируется похоже только с файловым дескриптором

так что только читать файл, либо переводить байты напрямую, в  юникод: BytesToUnicode

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)