Проблема в понимании кодировок на C++

Суть в том, что есть код. Программа шифрования с помощью трёх ключей ( по коду понятно ) и она на кодировке 1251 выводит всё чётко и в пределах русского алфавита. Но стоит её запустить на Linux, так всё выходит за пределы русского алфавита и выводит мне бурду. ПРОШУ заметить, что я понимаю, что одна однобайтовая, а другая двухбайтовая кодировка. Вопрос состоит в том, как мне написать/переписать тоже самое под кодировку UTF-8? Мне нужно конкретное решение, очень прошу вас. Спасибо.

#include <iostream>  // Включение библиотеки для ввода и вывода данных
#include <string>    // Включение библиотеки для работы с строками
#include <windows.h> // Включение библиотеки для настройки кодовой страницы в Windows

using namespace std;

// Функция для шифрования текста
string encryptText(const string& text, const string& alphabet, const string& key1, const 
string& key2, const string& key3) {
string encryptedText = text; // Создание копии входного текста для шифрования
for (size_t i = 0; i < text.size(); i++) {
    size_t pos = alphabet.find(text[i]); // Находим позицию символа в алфавите
    if (pos != string::npos) {
        if (i % 3 == 0) { // Шифрование в зависимости от позиции символа
            encryptedText[i] = key1[pos];
        }
        else if (i % 3 == 1) {
            encryptedText[i] = key2[pos];
        }
        else {
            encryptedText[i] = key3[pos];
        }
    }
 }
return encryptedText;
}

// Функция для расшифровки текста
string decryptText(const string& text, const string& alphabet, const string& key1, const 
string& key2, const string& key3) {
string decryptedText = text; // Создание копии входного текста для расшифровки
for (size_t i = 0; i < text.size(); i++) {
    size_t pos = alphabet.find(text[i]); // Находим позицию символа в алфавите
    if (pos != string::npos) {
        if (i % 3 == 0) { // Расшифрование в зависимости от позиции символа
            decryptedText[i] = alphabet[key1.find(text[i])];
        }
        else if (i % 3 == 1) {
            decryptedText[i] = alphabet[key2.find(text[i])];
        }
        else {
            decryptedText[i] = alphabet[key3.find(text[i])];
        }
    }
}
return decryptedText;
}

int main() {
SetConsoleCP(1251);       // Установка кодовой страницы ввода в Windows (UTF-8)
SetConsoleOutputCP(1251); 

string alphabet = "абвгдеёжзийклмнопрстуфхцчшщъыьэюяАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ 
.,:;`|/()!?№^-=_";
string key1 = "жзийклмнопрстуфхцчшщъыьэюяабвгдеёЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ .,:;`|/()!? 
№^-=_АБВГДЕЁ";
string key2 = "опрстуфхцчшщъабвгдеёжзийклмныьэюяОПРСТУФХЦЧШЩЪЫЬЭЮЯ .,:;`|/()!?№^- 
=_АБВГДЕЁЖЗИЙКЛМН";
string key3 = "йцукенгшщзфывапролджэъхячёсмитьбюЭЖДЛОРПАВЫФЯЧСМИТЬБЮЪЁ .,:;`|/()!?№^- 
=_ХЗЩШГНЕКУЦЙ";

int choice;
string inputText;
string outputText;

while (true) {
    cout << "Выберите действие:" << endl;
    cout << "1. Зашифровать текст" << endl;
    cout << "2. Расшифровать текст" << endl;
    cout << "3. Выйти" << endl;
    cout << "Ваш выбор: ";
    cin >> choice;

    switch (choice) {
    case 1:
        // Вариант 1: Зашифровать текст
        cout << "Введите текст для шифрования: ";
        cin.ignore();  // Очистка буфера ввода, чтобы корректно считать строку с 
    пробелами
        getline(cin, inputText); // Считывание введенного текста
        outputText = encryptText(inputText, alphabet, key1, key2, key3); // Шифрование 
    текста
        cout << "Зашифрованный текст: " << outputText << endl << endl; // Вывод 
    зашифрованного текста
        break;

    case 2:
        // Вариант 2: Расшифровать текст
        cout << "Введите текст для расшифровки: ";
        cin.ignore(); // Очистка буфера ввода, чтобы корректно считать строку с 
    пробелами
        getline(cin, inputText); // Считывание введенного текста
        outputText = decryptText(inputText, alphabet, key1, key2, key3); // 
    Расшифрование текста
        cout << "Расшифрованный текст: " << outputText << endl << endl; // Вывод 
    расшифрованного текста
        break;

    case 3:
        // Вариант 3: Выйти из программы
        cout << "До свидания!" << endl; // Вывод прощального сообщения
        return 0; // Завершение программы

    default:
        // Если введен неправильный выбор
        cout << "Неверный выбор. Попробуйте еще раз." << endl << endl;
        break;
      }
   }

Ответы (1 шт):

Автор решения: avp

Поскольку ваш алгоритм encode/decode связан с заменой символа, найденного в одном алфавите на символ из той же позиции в другом алфавите, то при использвании алфавитов в utf-8, заданных в виде строк (как у вас в программе), поиск нужного символа замены потребует линейного просмотра строки (из-за того, что символы в utf имеют разную длину в байтах).

Поэтому мне представляется разумным закодировать алфавиты, выделив под каждый символ utf-8 четыре байта (это максимальная длина utf-8 символа). Тогда к символу в нужной позиции алфавита можно будет обращаться по вычисляемому индексу.

Для получения длины символа utf-8 в байтах используется таблица из 32 байт, которая адресуется старшими 5-ю битами первого байта символа (см. описание UTF-8).

Я сделал здесь только пример функции encode вместе с тестовым циклом в main (надеюсь, decode вы запрограммируете сами).

#include <stdio.h>
#include <string.h>
#include <stdlib.h>


int
utf_len (const char *c)
{
  static char s_len[32] = {1, 1, 1, 1,
                           1, 1, 1, 1,
                           1, 1, 1, 1,
                           1, 1, 1, 1,

                           1, 1, 1, 1,
                           1, 1, 1, 1,

                           2, 2, 2, 2,
                           3, 3, 4, 1};
  
  return s_len[(*c >> 3) & 0x1f];
}

// make array, 4 bytes for one utf symbol, end with 4 nil bytes
char *
cvt_abet (const char *a)
{
  char *res = (char *)calloc(strlen(a) + 1, 4);
  int j = 0;
  
  while (*a) {
    int l = utf_len(a);
    memcpy(res + j, a, l);
    j += 4;
    a += l;
  }

  return res;
}


// returns pos (in number of utf-8 symbols)
int
find_cvt_chr (const char *a, const char *c)
{
  int pos = 0;

  while (*a) {
    if (memcmp(a, c, utf_len(a)) == 0)
      return pos;
    
    pos++;
    a += 4;
  }

  return EOF;
}

// `ab`, `k1`, `k2`, `k3` is "cvt" arrays
char *
utf_enc (const char *txt, const char *ab,
         const char *k1, const char *k2, const char *k3)
{
  char *res = (char *)malloc(strlen(txt) + 1);
  int  j = 0;
  const char *key[3];
  key[0] = k1; key[1] = k2; key[2] = k3;

  for (int i = 0; *txt; i++) {
    int l = utf_len(txt);
    int ipos = find_cvt_chr(ab, txt);

    if (ipos == EOF)
      memcpy(res + j, txt, l), j += l;
    else {
      const char *s = key[i % 3];
      const char *c = s + ipos * 4;
      int lc = utf_len(c);
      memcpy(res + j, c, lc);
      j += lc;
    }
    
    txt += l;
  }

  res[j] = 0;
  return res;
}


int
main (int ac, char *av[])
{
  char str[1000];
  
  char *abet = cvt_abet("абвгдеёжзийклмнопрстуфхцчшщъыьэюяАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ .,:;`|/()!?№^-=_"),
    *key1 = cvt_abet("жзийклмнопрстуфхцчшщъыьэюяабвгдеёЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ .,:;`|/()!?№^-=_АБВГДЕЁ"),
    *key2 = cvt_abet("опрстуфхцчшщъабвгдеёжзийклмныьэюяОПРСТУФХЦЧШЩЪЫЬЭЮЯ .,:;`|/()!?№^-=_АБВГДЕЁЖЗИЙКЛМН"),
    *key3 = cvt_abet("йцукенгшщзфывапролджэъхячёсмитьбюЭЖДЛОРПАВЫФЯЧСМИТЬБЮЪЁ .,:;`|/()!?№^-=_ХЗЩШГНЕКУЦЙ");
  
  while (fgets(str, 1000, stdin)) {
    char *out = utf_enc(str, abet, key1, key2, key3);
    puts(out);
    free(out);
  }

  return puts("End") == EOF;
}

Компилируем и запускаем:

avp@avp-desktop:~/avp/hashcode$ g++ t-utf-encode.cpp && ./a.out
qwe абвгд 123
qwe/оцисе/123

rty  абвгд  123
rty/=йзркк=?123

End
avp@avp-desktop:~/avp/hashcode$

Если что-то непонятно, спрашивайте.

P.S.
хотя этот код успешно компилируется g++, при желании перепишите на что-то более крестоподобное

→ Ссылка