C++ и UNICODE

Тема в разделе "LANGS.C", создана пользователем slayer, 15 фев 2008.

  1. slayer

    slayer New Member

    Публикаций:
    0
    Регистрация:
    2 июн 2004
    Сообщения:
    23
    Проблема такая: перебираю директории (пока) с помощью FindFirstFile/FindNextFile и сохраняю их в файле с помощью wofstream. Работаю с UNICODE. Вообщем некоректно записываются некоторые директории: напимер Räubern, вместо ä получается русский символ. В wostream писал и просто строку TCHAR и пробовал через basic_string<TCHAR>, не работает. Может кто сталкивался с таким? Использовать внешнюю библиотеку для работы с UNICODE-ом не хотелось бы, хотелось бы средствами C++.
    Еще интересно что, сохранил я строку Räubern в файл .txt (UNICODE). Теперь читаю это в basic_string<TCHAR> и вижу там такой вот буфер:
    0xFFFE, 0x0052 (R), 0, 0x00e4 (ä), 0, 0x0075 (u), 0, .....
    А в FileData.cFileName эта же строка выглядит так:
    0x0052 (R), 0x00e4 (ä), 0x0075 (u) ......
    То есть никаких 0xFFFE и нулевых слов между символами нет. Вообщем непонятно, зачем редактор пихает это 0xFFFE в файл, попытка записать эту константу в файл перед записью FileData.cFileName ни к чему хорошему не привели
     
  2. tigsid

    tigsid Member

    Публикаций:
    0
    Регистрация:
    11 июн 2004
    Сообщения:
    62
    Все что я написал внизу не совсем верно,
    и возможно я ошибаюсь но:

    Си и С++ не поддерживают unicode, они поддерживают широкие символы.
    Широкий символ - это тип который может вместить все символы текущей locale системы.

    Unicode есть UTF-32 BigEndian, UTF-32 LittleEndian, UTF-16 BE, UTF-16 LE, UTF-8
    (и еще нескольколько).

    В Win32 под UNICODE и широкими символами понимается UTF-16 LE.
    Но все *W функции возвращают результат в Широких Символах.
    Поэто например в версии Windows для другой архитектуру :)
    UNICODE может быть уже UTF-16 BE или UTF-32 LE.

    0xFFFE - это UNICODE BOM. т.е. ID кодировки в которой сохранен файл.

    Что теперь у тебя происходит с файлами.

    с помощью Find*FileW ты получаешь строку в Широких символах.
    С помощью wofstream ты сохраняешь в файл результат, но
    текст сохраняется в ansi кодировки текущей локали и поэтому несоответствие.
    (т.е. строка в UNICODE перекодируется в однобайтовую кодировку
    и уже сохраняется в файл)

    теперь при чтении из файла в широкую строку.
    Один байт преобразуется в широкий символ и получается.
    52 в 0052, 00 в 0000, E4 в 00E4, 00 в 0000.

    т.е. все "широкие" функции в C/C++ читаю/записувают текстовой файл
    в ansi кодировки текущей локали.
    Но при чтении/записи происходит
    преобразования из 1-но байтового символа в 2-ух байтовый.


    Попробуй открывать файл не как текстовый, а как бинарный.
    и используй fwrite/stream->write для записи результата Find*FileW.
     
  3. AsmGuru62

    AsmGuru62 Member

    Публикаций:
    0
    Регистрация:
    12 сен 2002
    Сообщения:
    689
    Адрес:
    Toronto
  4. slayer

    slayer New Member

    Публикаций:
    0
    Регистрация:
    2 июн 2004
    Сообщения:
    23
    Спасибо всем за полезную информацию. Вот что я выяснил. Похоже проблема в преобразовании типа из wchar_t в char при выводе в wstream. Поэтому приходися ручками выводить вторую половину слова. Думаю наверное можно даже будет пользоваться не wfstream, а просто fstream и самому написать операцию для вывода wchar_t. Write и ios_base::binary не помогают. Вот пример кода, который вроде работает. Единственная проблема это символ перевода строки '\n', который в файл пишется как 0x0D, 0x0A, а нам нужно 0x0D, 0x00, 0x0A, 0x00. При попытке же ручками записать 0x0D, 0x00, 0x0A, 0x00 получается 0x0D, 0x00, 0x0D, 0x0A, 0x00, т.е. 0x0A, автоматически превращается в 0x0D, 0x0A. Решение проблемы мне кажется в следущем: сначала формировать нужный буфер, потом парсить в нем 0x0D, 0x0A на 0x0D, 0x00, 0x0A, 0x00, а потом уже писать буфер в файл. Ну и конечно не забыть записать константу 0xFFFE в начало файла.
    Код (Text):
    1. wfstream dest;
    2. basic_string<TCHAR> str, str2;
    3.  
    4. dest.open (_TEXT("b.txt"));
    5. if(!dest.is_open ()) abort ();
    6. str2 += 0x00FF;
    7. str2 += 0x00FE;
    8. dest << str2;
    9. if((hSearch = FindFirstFile (_TEXT ("*.*"), &FileData)) != INVALID_HANDLE_VALUE)
    10. {
    11.     do
    12.     {
    13.         if(FileData. dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
    14.         {
    15.             basic_string<TCHAR> str1;
    16.             size_t j = _tcslen(FileData.cFileName);
    17.             for(size_t i = 0; i < j; i++)
    18.             {
    19.                 str1 += TCHAR(FileData.cFileName[i] & 0xFF);
    20.                 str1 += TCHAR(FileData.cFileName[i] >> 8);
    21.             }
    22.             dest << str1 << (TCHAR)0x000A;
    23.         }
    24.     }
    25.     while(FindNextFile(hSearch, &FileData));
    26.     FindClose(hSearch);
    27. }
     
  5. IceStudent

    IceStudent Active Member

    Публикаций:
    0
    Регистрация:
    2 окт 2003
    Сообщения:
    4.300
    Адрес:
    Ukraine
  6. slayer

    slayer New Member

    Публикаций:
    0
    Регистрация:
    2 июн 2004
    Сообщения:
    23
    IceStudent
    Спасибо, то что нужно.
    А оказалось С++ действительно не поддерживает UNICODE, он ничего не знает об UNICODE:). Он поддерживает расширенные символы, которые могут быть в кодировке UNICODE.
    p.s. Тему наверное можно закрывать