Преобразование кодировок

При использовании L-строк все символы кодируются двумя байтами. Не следует рассматривать L-строку, как строку в какой-то кодировке, например, в UTF-16. Думайте об L-строке как о строке в абстрактной кодировке, позволяющей закодировать более 65 тысяч символов. Когда мы говорим о строке в какой-либо кодировке, то мы всегда говорим о C-строке. По умолчанию во всех проектах мы настроили для C-строк кодировку windows-1251.

Используя функции mbstowcs() и wcstombs(), которые мы рассмотрели в предыдущем разделе, можно преобразовать строку из одной однобайтовой кодировки в другую. Кодировка символов C-строки указывается с помощью локали. В этом случае L-строка используется как промежуточная стадия. Преобразуем строку из кодировки windows-1251 в кодировку windows-866 (листинг 8.3).

Листинг 8.3. Преобразование строки из кодировки windows-1251 в кодировку windows-866

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <clocale>
#include <cwchar>
#include <cstdlib>
#include <cstring>
#include <process.h>

int main() {
   size_t count = 0;
   char cp866[256] = {0};
   wchar_t wstr[256] = {0};
   char cp1251[] = "строка"; // Исходная строка (windows-1251)
   for (int i = 0, len = (int)std::strlen(cp1251); i < len; ++i) {
      std::cout << (int)cp1251[i] << ' ';
   } // -15 -14 -16 -18 -22 -32
   std::cout << std::endl;

   // Преобразование C-строки в кодировке windows-1251 в L-строку
   _wsetlocale(LC_ALL, L"Russian_Russia.1251");
   count = std::mbstowcs(wstr, cp1251, 255);
   if (count == (size_t)(-1)) {
      std::cout << "Error 1" << std::endl;
      exit(1);
   }
   for (int i = 0, len = (int)std::wcslen(wstr); i < len; ++i) {
      std::cout << (int)wstr[i] << ' ';
   } // 1089 1090 1088 1086 1082 1072
   std::cout << std::endl;

   // Преобразование L-строки в C-строку в кодировке windows-866
   _wsetlocale(LC_ALL, L"Russian_Russia.866");
   count = std::wcstombs(cp866, wstr, 255);
   if (count == (size_t)(-1)) {
      std::cout << "Error 2" << std::endl;
      exit(1);
   }
   for (int i = 0, len = (int)std::strlen(cp866); i < len; ++i) {
      std::cout << (int)cp866[i] << ' ';
   } // -31 -30 -32 -82 -86 -96
   std::cout << std::endl;
   return 0;
}

Для преобразования кодировок в Windows можно воспользоваться следующими функциями из WinAPI (кодировки указаны для русской версии Windows):

  • CharToOemA() — преобразует строку source в кодировке windows-1251 в строку dest в кодировке windows-866. Прототип функции:
#include <windows.h> /* User32.lib */
WINBOOL CharToOemA(LPCSTR source, LPSTR dest);
  • CharToOemBuffA() — преобразует строку source в кодировке windows-1251 в строку dest в кодировке windows-866. Количество символов указывается в параметре length. Прототип функции:
#include <windows.h> /* User32.lib */
WINBOOL CharToOemBuffA(LPCSTR source, LPSTR dest, DWORD length);
  • OemToCharA() — преобразует строку source в кодировке windows-866 в строку dest в кодировке windows-1251. Прототип функции:
#include <windows.h> /* User32.lib */
WINBOOL OemToCharA(LPCSTR source, LPSTR dest);
  • OemToCharBuffA() — преобразует строку source в кодировке windows-866 в строку dest в кодировке windows-1251. Количество символов указывается в параметре length. Прототип функции:
#include <windows.h> /* User32.lib */
WINBOOL OemToCharBuffA(LPCSTR source, LPSTR dest, DWORD length);

Пример использования функций CharToOemA() и OemToCharA() приведен в листинге 8.4.

Листинг 8.4. Функции CharToOemA() и OemToCharA()

#include <iostream>
#include <clocale>
#include <cstring>
#include <windows.h>

int main() {
   std::setlocale(LC_ALL, "Russian_Russia.1251");
   char cp866[256] = {0};
   char cp1251[256] = {0};
   char str[] = "строка"; // Исходная строка в кодировке windows-1251

   // Преобразование windows-1251 в windows-866
   CharToOemA(str, cp866);
   for (int i = 0, len = (int)std::strlen(cp866); i < len; ++i) {
      std::cout << (int)cp866[i] << ' ';
   } // -31 -30 -32 -82 -86 -96
   std::cout << std::endl;

   // Преобразование windows-866 в windows-1251
   OemToCharA(cp866, cp1251);
   for (int i = 0, len = (int)std::strlen(cp1251); i < len; ++i) {
      std::cout << (int)cp1251[i] << ' ';
   } // -15 -14 -16 -18 -22 -32
   std::cout << std::endl;
   return 0;
}

Если вы используете компилятор VC++ в Eclipse и получили сообщение об отсутствии файла windows.h, то нужно в свойствах проекта указать путь к каталогу Include из Microsoft SDK. Если Microsoft SDK отсутствует на компьютере, то его нужно дополнительно установить.

Если в VC++ в Eclipse вы получили ошибку error LNK2019, то в свойствах проекта на вкладке C++ Compiler | Preprocessor нужно указать путь к каталогу Include в Microsoft SDK, а также добавить путь к каталогу Lib и библиотеку User32.lib на вкладке Linker | Libraries (рис. 8.1) или вставить в самое начало программы инструкцию:

#pragma comment(lib, "User32.lib")

Рис. 8.1. Указание пути к каталогу Lib и добавление библиотек

Для преобразования кодировок в Windows можно также воспользоваться следующими функциями из WinAPI:

  • MultiByteToWideChar() — преобразует C-строку lpMultiByteStr в L-строку lpWideCharStr. Прототип функции:
#include <windows.h> /* Kernel32.lib */
int MultiByteToWideChar(UINT codePage, DWORD dwFlags,
   LPCCH lpMultiByteStr, int cbMultiByte,
   LPWSTR lpWideCharStr, int cchWideChar);

Параметр codePage задает кодировку C-строки. Примеры указания кодировки: 866 (для кодировки windows-866), 1251 (для windows-1251), 65001 или CP_UTF8 (для UTF-8), 1200 (для UTF-16LE), 1201 (для UTF-16BE), 12000 (для UTF-32LE) и 12001 (для UTF-32BE). Полный список кодировок смотрите в документации.

В параметре dwFlags можно указать различные флаги (MB_PRECOMPOSED (1), MB_ERR_INVALID_CHARS (8), MB_COMPOSITE (2), MB_USEGLYPHCHARS (4)), задающие поведение функции при конверсии, или значение 0. Полный список и описание флагов смотрите в документации.

Параметр cbMultiByte задает количество байтов в C-строке lpMultiByteStr, подлежащих конверсии. Если C-строка завершается нулевым символом, то лучше указать значение –1. В этом случае размер строки вычисляется автоматически и в L-строку вставляется нулевой символ. Если указано конкретное число, то нулевой символ автоматически в L-строку не вставляется!

Параметр cchWideChar задает максимальный размер L-строки lpWideCharStr в символах. Если указать значение 0, то функция вернет требуемое количество символов. Если указано конкретное значение, то функция вернет количество записанных символов или значение 0 в случае ошибки;

  • WideCharToMultiByte() — преобразует L-строку lpWideCharStr в C-строку lpMultiByteStr. Требуемая кодировка C-строки указывается в параметре codePage. Прототип функции:
#include <windows.h> /* Kernel32.lib */
int WideCharToMultiByte(UINT codePage, DWORD dwFlags,
   LPCWCH lpWideCharStr, int cchWideChar,
   LPSTR lpMultiByteStr, int cbMultiByte,
   LPCCH lpDefaultChar, LPBOOL lpUsedDefaultChar);

В параметре dwFlags можно указать различные флаги (WC_COMPOSITECHECK (512), WC_DEFAULTCHAR (64), WC_DISCARDNS (16), WC_SEPCHARS (32), WC_NO_BEST_FIT_CHARS (1024)), задающие поведение функции при конверсии, или значение 0. Полный список и описание флагов смотрите в документации.

Параметр cchWideChar задает количество символов в L-строке lpWideCharStr, подлежащих конверсии. Если L-строка завершается нулевым символом, то лучше указать значение –1. В этом случае размер строки вычисляется автоматически и в C-строку вставляется нулевой символ. Если указано конкретное число, то нулевой символ автоматически в C-строку не вставляется!

Параметр cbMultiByte задает максимальный размер C-строки lpMultiByteStr в байтах. Если указать значение 0, то функция вернет требуемое количество байтов. Если указано конкретное значение, то функция вернет количество записанных байтов или значение 0 в случае ошибки.

В параметре lpDefaultChar можно задать адрес символа, который будет использоваться, если символ в L-строке не может быть преобразован (символ отсутствует в кодировке codePage). Если задан нулевой указатель, то будет использоваться символ по умолчанию. При использовании кодировки UTF-8 параметр lpDefaultChar должен иметь значение NULL.

Параметр lpUsedDefaultChar позволяет задать адрес логической переменной. Если параметр не равен значению NULL, то в переменную будет записано логическое значение Истина, если какого-либо символа нет в кодировке codePage, и 0 — в противном случае. При использовании кодировки UTF-8 параметр lpUsedDefaultChar должен иметь значение NULL.

Пример использования функций MultiByteToWideChar() и WideCharToMultiByte() приведен в листинге 8.5.

Листинг 8.5. Функции MultiByteToWideChar() и WideCharToMultiByte()

#include <iostream>
#include <clocale>
#include <cstring>
#include <cwchar>
#include <windows.h>

int main() {
   std::setlocale(LC_ALL, "Russian_Russia.1251");
   int count = 0;
   char cp866[256] = {0};
   char utf8[256] = {0};
   wchar_t wstr[256] = {0};
   wchar_t wstr2[256] = {0};
   char str[] = "строка str"; // Исходная строка (windows-1251)
   for (int i = 0, len = (int)std::strlen(str); i < len; ++i) {
      std::cout << (int)str[i] << ' ';
   } // -15 -14 -16 -18 -22 -32 32 115 116 114
   std::cout << std::endl;

   // Узнаем требуемый размер wstr
   count = MultiByteToWideChar(1251, 0, str, -1, wstr, 0);
   std::cout << "требуется " << count << std::endl; // требуется 11
   // Преобразование C-строки в кодировке windows-1251 в L-строку
   count = MultiByteToWideChar(1251, 0, str, -1, wstr, 255);
   std::cout << "записано " << count << std::endl;  // записано 11
   for (int i = 0, len = (int)std::wcslen(wstr); i < len; ++i) {
      std::cout << (int)wstr[i] << ' ';
   } // 1089 1090 1088 1086 1082 1072 32 115 116 114
   std::cout << std::endl;

   // Узнаем требуемый размер utf8
   count = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, utf8, 0,
                               NULL, NULL);
   std::cout << "требуется " << count << std::endl; // требуется 17
   // Преобразование L-строки в C-строку в кодировке UTF-8
   count = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, utf8, 255,
                               NULL, NULL);
   std::cout << "записано " << count << std::endl;  // записано 17
   for (int i = 0, len = (int)std::strlen(utf8); i < len; ++i) {
      std::cout << (int)utf8[i] << ' ';
   } // -47 -127 -47 -126 -47 -128 -48 -66 -48 -70 -48 -80 32 115 116 114
   std::cout << std::endl;

   // Узнаем требуемый размер wstr2
   count = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, wstr2, 0);
   std::cout << "требуется " << count << std::endl; // требуется 11
   // Преобразование C-строки в кодировке UTF-8 в L-строку
   count = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, wstr2, 255);
   std::cout << "записано " << count << std::endl;  // записано 11
   for (int i = 0, len = (int)std::wcslen(wstr2); i < len; ++i) {
      std::cout << (int)wstr2[i] << ' ';
   } // 1089 1090 1088 1086 1082 1072 32 115 116 114
   std::cout << std::endl;

   // Узнаем требуемый размер cp866
   char ch = '?';
   int flag = 1;
   // wstr[1] = L'\u0263'; // символа нет в windows-866
   count = WideCharToMultiByte(866, 0, wstr, -1, cp866, 0, &ch, &flag);
   std::cout << "требуется " << count << std::endl; // требуется 11
   // Преобразование L-строки в C-строку в кодировке windows-866
   count = WideCharToMultiByte(866, 0, wstr, -1, cp866, 255, &ch, &flag);
   std::cout << "записано " << count << std::endl;  // записано 11
   for (int i = 0, len = (int)std::strlen(cp866); i < len; ++i) {
      std::cout << (int)cp866[i] << ' ';
   } // -31 -30 -32 -82 -86 -96 32 115 116 114
   std::cout << std::endl;
   std::cout << flag << std::endl; // 0
   return 0;
}

Учебник C++ (MinGW-W64)
Учебник C++ (MinGW-W64) в формате PDF

Помощь сайту

ЮMoney (Yandex-деньги): 410011140483022

ПАО Сбербанк:
Счет: 40817810855006152256
Реквизиты банка:
Наименование: СЕВЕРО-ЗАПАДНЫЙ БАНК ПАО СБЕРБАНК
Корреспондентский счет: 30101810500000000653
БИК: 044030653
КПП: 784243001
ОКПО: 09171401
ОКОНХ: 96130
Скриншот реквизитов