Часто задаваемые вопросы и ответы по C/C++/Visual C++
Последнее обновление: 27.08.2003
FAQ по C/C++/Visual C++
Работа с сетью
Корректная перекодировка KOI -> WIN etc.
Составители: SUnteXx, Leprecon
Корректная перекодировка KOI -> WIN etc.
A: (Eugene Yavorsky)
Оригинальная ссылка: нету

Для корректной перекодировки, необходимо пользоваться установленными в системе кодовыми таблицами и функциями MultiByteToWideChar()/WideCharToMultiByte(). Поскольку перекодировка идет через Юникод, предположительно будет максимальное соответствие (а не так, как, к примеру, в Фаре, когда все перекодировки ведутся через 866 таблицу - откуда потери несуществующих в ней символов, даже если они есть в исходной и конечной таблицах).

Методом научного тыка (EnumSystemCodePages), первоначально под W2K, обнаружены следующие (инетересующие русскоговорящего человека) таблицы (цитирую хидер):

#define CP_KOI8R 20866
#define CP_KOI8U 21866
#define CP_ISO_8859_5 28595

Несмотря на то, что при использовании EnumSystemCodePages() под 98/95 Виндой они не включаются в перечисление (по крайней мере, у меня), тем не менее, при установленном IE4.0 и выше MultiByteToWideChar()/WideCharToMultiByte() с успехом работает с этими константами.

В принципе, можно сделать универсальную табличную перекодировку, если сохранить перекодировочные таблицы Юникод -> [интересующие таблицы], и затем, без вызова MultiByteToWideChar/WideCharToMultiByte "на месте" посимвольно преобразовавать строки. Все хочу сделать, да лень.

Ну, ближе к телу, председатель!
Функция примитивнейшая, но мне другой и не надо было. Кто хочет круче - пусть доработает.

======== грызем CODEPAGE.H ==========
#ifndef __CODEPAGE_H__
#define __CODEPAGE_H__

// Константы для кодовых страниц
#define CP_KOI8       20866
#define CP_KOI8R      20866
#define CP_KOI8U      21866
#define CP_ISO_8859_5 28595

// Исключительно для удобства
#define CP_1251       1251

// Прототип функции-обертки
BOOL ConvertCP(UINT uiIN, UINT uiOUT, LPSTR lpString);

// Инлайновые обертки для сокращения кода

// Самая нужная из них
inline BOOL ConvertCP(UINT uiIN, UINT uiOUT, CString& szString){
  BOOL bRet = ConvertCP(uiIN, uiOUT, szString.GetBuffer(szString.GetLength()+1));
  szString.ReleaseBuffer();
  return bRet;
}

// Аналогичные можно определить по вкусу, смотря какое
// преобразованеи используется чаще
inline BOOL ConvertToKOI8(UINT uiIN, LPSTR lpString){
  return(ConvertCP(uiIN, CP_KOI8R, lpString));
}

inline BOOL ConvertFromKOI8(UINT uiOUI, LPSTR lpString){
  return(ConvertCP(CP_KOI8R, uiOUI, lpString));
}


inline BOOL ConvertToKOI8(UINT uiIN, CString& szString){
  return(ConvertCP(uiIN, CP_KOI8R, szString));
}

inline BOOL ConvertFromKOI8(UINT uiOUI, CString& szString){
  return(ConvertCP(CP_KOI8R, uiOUI, szString));
}

#endif
======== кончили грызть CODEPAGE.H ==========

======== грызем CODEPAGE.CPP ==========
#include "stdafx.h"
#include "codepage.h"


#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

// Преобразование кодировки "на месте"
BOOL ConvertCP(UINT uiIN, UINT uiOUT, LPSTR lpString){
  if(!lpString) return FALSE; // Получили NULL - нехорошо, ошибочка
  if(!lpString[0]) return TRUE; // Пустая строка - OK, преобразования не надо

  // Сперва определяем необходимый размер промежуточного буфера
  int nRet = MultiByteToWideChar(uiIN, MB_PRECOMPOSED, lpString, -1, NULL, 0);
  if(nRet == 0) return FALSE;

  // Создаем его
  WCHAR* pwcBuffer = new WCHAR[nRet+2];

  // Преобразование uiIN -> Юникод
  nRet = MultiByteToWideChar(uiIN, MB_PRECOMPOSED, lpString, -1, pwcBuffer, nRet+2);
  if(nRet ==

    delete[] pwcBuffer;
    return FALSE;
  }

  // Преобразование Юникод -> uiOUT.
  // Неизвестные символы заменяем "#". Кому такое поведение не нравится, может
  // "поиграть" с параметрами WideCharToMultiByte()
  nRet = WideCharToMultiByte(uiOUT, 0, pwcBuffer, -1, lpString, strlen(lpString)+1, "#", NULL);
  // Освобождаем память
  delete[] pwcBuffer;

  if(nRet == 0) return FALSE;

  return TRUE;
}
======== надоело грызть CODEPAGE.CPP ==========

P.S. Кстати, в ФАКе "Перевод из DOS кодировки в Windows и наоборот" неверен, т.к. не преобразовываются не-русские кириллические символы (и буква Ё - Е с двумя точками сверху, если не видно). Оно, конечно, красиво и коротко, но неверно.

Надо так:
file://из ДОС в Windows
char* Decode_DOS_to_Win(char * str)
{
    unsigned char *cstr=str; // "unsigned" - чтоб избежать предупреждений комп-ра
    for(; *cstr; cstr++)
    {
        if(*cstr<128) continue; // не фиг топтаться дальше, если не нужно конвертить!

        if(*cstr>=128 && *cstr<=175)
            *cstr+=64;
        else if(*cstr>=224 && *cstr<=239)
            *cstr+=16;
        else if(*cstr==252) // №
            *cstr=185;
        else if(*cstr==240) // Ё, Е с двумя точками - Йо
            *cstr=168;
        else if(*cstr==241) //  Ё, е с двумя точками - йо
            *cstr=184;
        else if(*cstr==242) // Є, украинское Йе
            *cstr=170;
        else if(*cstr==243) // є, украинское йе
            *cstr=186;
        else if(*cstr==244) // Ї, украинское Йи
            *cstr=175;
        else if(*cstr==245) // ї, украинское йи
            *cstr=191;
        else if(*cstr==246) // Ў, беларусская У с хвостиком вверху - черт знает, как читается
            *cstr=161;
        else if(*cstr==247) // ў, беларусская У с хвостиком вверху - черт знает, как читается
            *cstr=162;
        else if(*cstr==248) // знак градуса
            *cstr=176;
        else if(*cstr==250) // маленькая центровая точка
            *cstr=183;
        else
            *cstr='?'; // некий символ, которым заменять несуществующие в CP1251
    }
    return str;
}
file://----------------------------------------------------

file://из Windows в ДОС
char* Decode_Win_to_DOS(char * str)
{
    unsigned char *cstr=str;
    for(;*cstr;cstr++)
    {
        if(*cstr<128) continue; // не фиг топтаться дальше, если не нужно конвертить!

        if(*cstr>=240)
            *cstr-=16;
        else if(*cstr>=192)
            *cstr-=64;
        else if(*cstr==185) // №
            *cstr=252;
        else if(*cstr==168) // Ё, Е с двумя точками - Йо
            *cstr=240;
        else if(*cstr==184) //  Ё, е с двумя точками - йо
            *cstr=241;
        else if(*cstr==170) // Є, украинское Йе
            *cstr=242;
        else if(*cstr==186) // є, украинское йе
            *cstr=243;
        else if(*cstr==175) // Ї, украинское Йи
            *cstr=244;
        else if(*cstr==191) // ї, украинское йи
            *cstr=245;
        else if(*cstr==178) // I, украинское И
            *cstr=73;            // в английскую, т.к. в ДОСе нет
        else if(*cstr==179) // i, украинское и
            *cstr=105;         // в английскую, т.к. в ДОСе нет
        else if(*cstr==161) // Ў, беларусская У с хвостиком вверху - черт знает, как читается
            *cstr=246;
        else if(*cstr==162) // ў, беларусская У с хвостиком вверху - черт знает, как читается
            *cstr=247;
        else if(*cstr==176) // знак градуса
            *cstr=248;
        else if(*cstr==183) // маленькая центровая точка
            *cstr=250;
        else
            *cstr='?'; // некий символ, которым заменять несуществующие в CP1251
    }
    return str;
}
====== капец ========
Содержание Обсудить на форуме « Предыдущая статья | Следующая статья »
Перейти к FAQ:  
FAQ составлен по материалам Форума на Исходниках.Ру.
Copyright © 2002 by Sources.ru. All rights reserved.