Проблема в понимании кодировок на 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 шт):
Поскольку ваш алгоритм 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++, при желании перепишите на что-то более крестоподобное