1

Тема: Українська мова, С++, консоль

Доброго дня!
Маю проблему з виводом тексту українською мовою. За якоїсь причини на моєму ПК навіть без встановленої setlocale та інших команд на зміну мови – українська працює, навіть і/ї/ґ, як в вск, так і в консолі! Та якщо запускати прогу на іншому пк, то виводиться битий текст (укр не працює).
Якщо я починаю вписувати setlocale(LC_ALL, "Ukrainian") , SetConsoleСP(1251), SetConsoleOutputCP(1251) – битий текст починає виводитись навіть на моєму пк, в вск і консолі.
Пробував дописувати #pragma execution_character_set("utf-8") та setlocale(LC_ALL, "Ukrainian") – в вск укр текст є, в консолі ні (на моєму пк просто виводе одну літеру Р і все, далі при вводі будь-чого завершає програму), на інших пк битий текст.
В консолі шрифти змінював (консолас/люціда) – все одно битий текст.

Код ось такий:

Прихований текст
#include <iostream>
#include <math.h>
#include <Windows.h>
#include <fstream>
#include <string>
#include <locale>

#pragma execution_character_set("utf-8")

using namespace std; // пакет імен стд
int main()
{
    setlocale(LC_ALL, "Ukrainian");
    // * змінні
    const double pi = asin(1.0) * 2; // арксинус від одиниці це пі/2, домножую на 2 і виходить пі
    double X = 0, Y = 0, steps = 0;
    int actions = 0, direction = 0, language = 0, engMenu = 0, ukrMenu = 0, exit = 0;
    // тут всюди виставляю 0, бо без прирівняння до - працює неправильно
    // * вибір мови
    do
    {
        cout << "Which language? " << endl
             << "1. Ukrainian" << endl
             << "2. English" << endl;
        cout << "Your language: ";
        cin >> language;
        // звичайне меню у циклі ду-вайл, яке буде запитувати ввести 1 чи 2 поки не ввести 1 чи 2
        switch (language)
        {
            // * ukrMenu
        case 1:
            cout << "Що ви хочете зробити? " << endl
                 << "1. Робота з текстовим файлом" << endl
                 << "2. Робота з консоллю" << endl
                 << "3. Перегляд інструкції" << endl
                 << "4. Зміна зовнішнього вигляду консолі" << endl
                 << "5. Вихід з програми" << endl;
            cout << "Вибрано пункт: ";
            cin >> ukrMenu;

            switch (ukrMenu)
            {
            // * ukrFile
            case 1:
            {
                ifstream cin("mapa.txt");   // відкриваю файл на читання
                ofstream cout("skarb.txt"); // відкриваю файл на вивід

                if (!cin.is_open())
                {
                    cerr << "You don't have «mapa.txt»!" << endl;
                    return 1;
                }
                // якщо файлу "мапа.тхт" не існує, то видає помилку і закриває прогу
                if (!cout.is_open())
                {
                    cerr << "You don't have «scarb.txt»!" << endl;
                    return 1;
                }
                // якщо файлу "скарб.тхт" не існує, то видає помилку і закриває прогу

                cin >> actions;

                for (int i = 1; i <= actions; i++)
                {
                    cin >> direction >> steps;

                    X = X + sin(pi / 4 * (direction - 1)) * steps; 

                    Y = Y + cos(pi / 4 * (direction - 1)) * steps;
                }

                if (abs(X) < 0.001)
                    X = 0;
                if (abs(Y) < 0.001)
                    Y = 0;

                cout.precision(3); // amount of numbers after the comma
                cout << fixed << "Скарб знаходиться за координатами: " << endl
                     << X << " " << Y << endl;

                cin.close();
                cout.close();
                cout << "Координату обраховано, закриваю програму..." << endl;

                break;
            }

                // * ukrConsole
            case 2:
            {
                cout << "Введіть кількість дій: ";
                cin >> actions;
                cout << "Кількість дій: " << actions << endl;

                for (int i = 1; i <= actions; i++)
                {
                    cout << "Введіть напрямок і кількість кроків: ";
                    cin >> direction >> steps;

                    X = X + sin(pi / 4 * (direction - 1)) * steps;
                    Y = Y + cos(pi / 4 * (direction - 1)) * steps;
                }

                if (abs(X) < 0.001)
                    X = 0;
                if (abs(Y) < 0.001)
                    Y = 0;

                cout.precision(3);
                cout << "Скарб знаходиться за координатами: " << X << " " << Y << endl;
                break;
            }
                // * ukrInstruction
                // ! хотілось би зробити якісь бордери для нормальної читабельності тексту
                // ? наче зробив більш-менш
            case 3:
            {
                cout << "Інструкція:" << endl
                     << "Знайти закопаний піратами скарб просто: все, що для цього потрібно – це карта. Як відомо, пірати зазвичай малюють карти від руки і описують алгоритм знаходження скаобу так:\n«Встаньте спиною близько самотньої пальми. Пройдіть тридцять кроків у бік лісу, потім сімнадцять кроків у бік озера, ..., нарешті десять кроків в бік великого кругляка. Скарб знаходиться під ним».\nВелика частина таких вказівок просто зводиться до проходження певної кількості кроків в одному з восьми напрямків (1 - північ, 2 - північний схід, 3 - схід, 4 - південний схід, 5 - південь, 6 - південний захід, 7 - захід, 8 - північний захід). Довжина кроку в будь-якому напрямку дорівнює 1." << endl
                     << "\nПри виборі відповідного пункту меню ви зможете: " << endl
                     << "\n1. Працювати з файлами. Програма зчитує дані з файлу mapa.txt. У нього ви маєте завчасно ввести необхідні для правильної роботи програми дані у такому форматі:\nУ перший рядок вводиться кількість вказівок (число від 1 до 40), далі у певну кількість рядків, відповідно кількості вказівок, через Enter вказується напрям руху (цифру від 1 до 8) та кількість кроків у цьому напрямку (від 1 до 1000). Вказівки мають вводитись у форматі «напрям_руху [пробіл] кількість_кроків». У файлі skarb.txt ви побачите відповідь – кінцеву координату скарбу." << endl
                     << "\n2. Робота з консоллю. Працюючи за допомогою консолі, відповідно до тексту-запиту, ви вводите необхідні дані (вказівки – від 1 до 40, напрям руху – цифру від 1 до 8, кількість кроків у цьому напрямку – від 1 до 1000). Формат вводу даних – «напрям_руху [пробіл] кількість_кроків». Кінцева координата теж виводиться у консоль." << endl
                     << "\n3. Перегляд інструкції до програми. При виборі цього пункту меню ви маєте можливість прочитати інтструкцію, яка пояснить вам як правильно користуватись програмою." << endl
                     << "\n4. Зміна зовнішнього вигляду консолі. Ви маєте можливість змінити зовнішній вигляд консолі: колір фону (обираєте потрібний номер з списку), колір шрифту (обираєте потрібний номер з списку), розмір шрифту." << endl
                     << "\n5. Вихід з програми. При виборі даного пункту здійснюється вихід з програми" << endl;
                break;
            }
            case 5:
            {
                cout << "Закриваю програму..." << endl;
                //? Краще зразу закривати чи через паузу?
                system("PAUSE");
                return 0;
            }
            default:
                cout << "Просто напишіть 1, 2, 3, 4 чи 5!" << endl;
            }
            system("PAUSE");
            return 0;

        // * engMenu
        case 2:
        {
            cout << "Do you want to work with text file or with console? " << endl
                 << "1. Work with files" << endl
                 << "2. Work with the console" << endl
                 << "3. View the instructions for the program" << endl
                 << "4. Changing the appearance of the console" << endl
                 << "5. Exit the program" << endl;
            cin >> engMenu;

            switch (engMenu)
            {
            // * engFile
            case 1:
            {
                ifstream cin("mapa.txt");
                ofstream cout("skarb.txt");

                if (!cin.is_open())
                {
                    cerr << "You don't have «mapa.txt»!" << endl;
                    return 1;
                }
                // якщо файлу "мапа.тхт" не існує, то видає помилку і закриває прогу
                if (!cout.is_open())
                {
                    cerr << "You don't have «scarb.txt»!" << endl;
                    return 1;
                }
                // якщо файлу "скарб.тхт" не існує, то видає помилку і закриває прогу

                cin >> actions;

                for (int i = 1; i <= actions; i++)
                {
                    cin >> direction >> steps;

                    X = X + sin(pi / 4 * (direction - 1)) * steps;

                    Y = Y + cos(pi / 4 * (direction - 1)) * steps;
                }

                if (abs(X) < 0.001)
                    X = 0;
                if (abs(Y) < 0.001)
                    Y = 0;

                cout.precision(3); // amount of numbers after the comma
                cout << fixed << "Coordinates of treasure: " << endl
                     << X << " " << Y << endl;

                cin.close();
                cout.close();
                cout << "The coordinate is calculated, closing the program..." << endl;

                break;
            }
            // * engConsole
            case 2:
            {
                setlocale(LC_ALL, "English");
                cout << "Enter amount of actions: ";
                cin >> actions;
                cout << "Amount of actions: " << actions << endl;

                for (int i = 1; i <= actions; i++)
                {
                    cout << "Enter direction and steps: ";
                    cin >> direction >> steps;

                    X = X + sin(pi / 4 * (direction - 1)) * steps;
                    Y = Y + cos(pi / 4 * (direction - 1)) * steps;
                }

                if (abs(X) < 0.001)
                    X = 0;
                if (abs(Y) < 0.001)
                    Y = 0;

                cout.precision(3);
                cout << "Treasure: " << X << " " << Y << endl;
                break;
            }
            // * engInstruction
            case 3:
            {
                cout << "Instruction: " << endl
                     << "Finding treasure buried by pirates is simple: all you need for this is a map. As you know, pirates usually draw maps by hand and describe the algorithm for finding a treasure as follows:\n«Stand with your back near a lonely palm tree. Walk thirty paces towards the forest, then seventeen steps towards the lake, ..., finally ten steps towards the big log. The treasure is under it».\nMost of these instructions simply boil down to taking a certain number of steps in one of eight directions (1 - north, 2 - north-east, 3 - east, 4 - southeast, 5 - south, 6 - southwest, 7 - west, 8 - northwest). The length of a step in any direction is equal to 1." << endl
                     << "\nBy choosing the appropriate menu item, you can: " << endl
                     << "\n1. Work with files. The program reads data from the mapa.txt file. In it, you must enter in advance the data necessary for the correct operation of the program in the following format:\nThe number of instructions is entered in the first line (a number from 1 to 40), then in a certain number of lines, according to the number of instructions, the direction of movement (a number from 1 to 8) and the number of steps in this direction (from 1 to 1000) are indicated through Enter. Directions should be entered in the format «direction_of_movement [space] number_of_steps». In the file skarb.txt you will see the answer - the final coordinate of the treasure." << endl
                     << "\n2. Work with the console. Working with the console, in accordance with the text-request, you enter the necessary data (directions - from 1 to 40, direction of movement - a number from 1 to 8, number of steps in this direction - from 1 to 1000). The data input format is «direction_of_movement [space] number_of_steps». The final coordinate is also output to the console." << endl
                     << "\n3. View the instructions for the program. When choosing this menu item, you have the opportunity to read an instruction that will explain to you how to use the program correctly." << endl
                     << "\n4. Changing the appearance of the console. You can change the appearance of the console: background color (select the desired number from the list), font color (select the desired number from the list), font size." << endl
                     << "\n5. Exit the program. Selecting this item exits the program" << endl;
                break;
            }
    // * engExit
            case 5:
            {
                cout << "Closing the program..." << endl;
                //? Краще зразу закривати чи через паузу?
                system("PAUSE");
                return 0;
            }
                    // * engDefault
            default:
                cout << "Just print 1, 2, 3, 4 or 5!" << endl;
            }
            break;
        }
               // * just default
        default:
            cout << "Just print 1 or 2!" << endl;
        }
    } while (language != 2);
    system("PAUSE");
    return 0;
}

Є якесь вирішення цієї проблеми без танців з бубоном на кожному окремому пк чи все ж доведеться прибрати можливість зміни мови і працювати лише з англійською?(

2

Re: Українська мова, С++, консоль

Яка ОС ?

3

Re: Українська мова, С++, консоль

Вінда 10

4 Востаннє редагувалося P.Y. (03.06.2023 19:09:20)

Re: Українська мова, С++, консоль

Відкрийте вікно консолі й наберіть команду chcp — виведеться номер поточної кодової сторінки. Виглядає так, що на різних ПК стоять різні кодові сторінки за замовчуванням, і незовсім зрозуміло, яка на Вашому. Або ж програма виводить за замовчуванням текст не через стандартний вивід, а через WinAPI, але чомусь це робить не завжди. До речі, сам текст програми в якому кодуванні?

Подякували: leofun011

5

Re: Українська мова, С++, консоль

Так, на моєму ПК 65001 кодова сторінка, на іншому ПК 866
Але хіба при SetConsoleСP(1251) SetConsoleOutputCP(1251) кодування не має змінюватись?

6

Re: Українська мова, С++, консоль

Щодо виводу Р в консолі, виглядає ось так:

Прихований текст

https://i.ibb.co/yy5xKVd/image.png

7 Востаннє редагувалося P.Y. (03.06.2023 19:41:31)

Re: Українська мова, С++, консоль

Кодова сторінка 65001 — це те ж саме, що UTF-8.
Тобто, це ще третє кодування, відмінне і від cp866, і від cp1251.
Початковий код програми, очевидно, теж в utf-8? Якщо так, то програма буде виводити текст кракозябликами і в cp1251, і в cp866.

Спробуйте в програмі встановлювати кодову сторінку не 1251, а 65001 — ймовірно, на інших ПК тоді теж працюватиме.

Втім, є нюанс. Реалізація кодової сторінки 65001 була забагованою, принаймні, ще в WindowsXP, тому ніхто нею не користувався. Якщо на іншому компі достатньо стара версія вінди, то, можливо, програма тоді взагалі на ньому вилітатиме.

Подякували: leofun011

8

Re: Українська мова, С++, консоль

Я так розумію, потрібно додати SetConsoleСP(65001) SetConsoleOutputCP(65001) чи щось інакше?

9

Re: Українська мова, С++, консоль

Гадаю, що так. У С++ я орієнтуюсь гірше, ніж у кодуваннях. Якщо SetConsoleCP(65001) робить те ж саме, що консольна команда chcp 65001, то, мабуть, це воно.

Подякували: Trap_o1

10

Re: Українська мова, С++, консоль

Вирішив проблему
Таки допомогло встановлення SetConsoleOutputCP(CP_UTF8); – тепер правильний вивід укр.мови як у консолі, так і у вск (разом з і, ї і т.д.)

11

Re: Українська мова, С++, консоль

Як я розумію, ви працюєте виключно з Віндою і виходити за її межі поки не збираєтесь. Якщо це так, то навіщо вам зайві присідання з utf-8? Використовуйте utf-16 напряму, це кодування Віндовс використовує, як нативне. Все, що вам потрібно це задати правильний режим та користуватись широкими символами. Наприклад:

#include <fcntl.h>
#include <io.h>
#include <windows.h>
#include <iostream>

int main()
{
    _setmode(_fileno(stdout), _O_U16TEXT);
    _setmode(_fileno(stdin), _O_U16TEXT);

    std::wcout << L"Привіт" << std::endl;
    std::wcout << L"Hello" << std::endl;
}

Так, ви будете залежати від msvc runtime, але зате жодних зайвих перетворень кодувань.

Подякували: leofun011

12

Re: Українська мова, С++, консоль

рішення-не юзати консоль взагалі. Або не юзати укр.мову

13

Re: Українська мова, С++, консоль

wander написав:

Як я розумію, ви працюєте виключно з Віндою і виходити за її межі поки не збираєтесь. Якщо це так, то навіщо вам зайві присідання з utf-8? Використовуйте utf-16 напряму, це кодування Віндовс використовує, як нативне. Все, що вам потрібно це задати правильний режим та користуватись широкими символами. Наприклад:

#include <fcntl.h>
#include <io.h>
#include <windows.h>
#include <iostream>

int main()
{
    _setmode(_fileno(stdout), _O_U16TEXT);
    _setmode(_fileno(stdin), _O_U16TEXT);

    std::wcout << L"Привіт" << std::endl;
    std::wcout << L"Hello" << std::endl;
}

Так, ви будете залежати від msvc runtime, але зате жодних зайвих перетворень кодувань.

Чи не складніше цей спосіб за просто один раз прописати SetConsoleOutputCP(CP_UTF8); ?
Так буде працювати з звичайним cout і не доведеться переписувати всі рядки в програмі на wcout та L""

14 Востаннє редагувалося wander (04.06.2023 15:58:53)

Re: Українська мова, С++, консоль

Trap_o написав:

Чи не складніше цей спосіб за просто один раз прописати SetConsoleOutputCP(CP_UTF8); ?
Так буде працювати з звичайним cout і не доведеться переписувати всі рядки в програмі на wcout та L""

Ну, тут з якої сторони подивитись :)
Адже ви ж не лише прописали SetConsoleOutputCP(CP_UTF8), правда? Ви ж ще як мінімум мали прописати набір символів виконання через #pragma execution_character_set або прапор компілятора. Інакше не запрацює. Це по-перше.
По-друге, як вже говорилось, utf-8 він же 65001 є дещо забагованим. Те, що це працює у вас, не дає жодних гарантій, що працюватиме всюди.
І на останок, ввід (української) у вас все ще поламаний, чи не так?) Якщо ви, звісно, не робили ще додаткових присідань.

15 Востаннє редагувалося P.Y. (04.06.2023 19:36:24)

Re: Українська мова, С++, консоль

ur_naz написав:

рішення-не юзати консоль взагалі. Або не юзати укр.мову

Віконні інтерфейси — це щось таке, що проблематично портувати між операційними системами, між різними версіями однієї операційної системи, між мовами програмування на одній операційній системі. Я не помилюсь, якщо скажу, що кожна бібліотека для цих штук являє собою тупикову гілку еволюції технологій, що в недалекому майбутньому перестане підтримуватись разом з усім кодом, написаним для неї. З точки зору портабельності коду, краще вже мати справу з зоопарком кириличних кодувань у консолі, ніж з віконними інтерфейсами. А з точки зору витрат ресурсів програміста, краще вже робити консольний ввід-вивід на голому асемблері, ніж вимальовувати ті кляті віконця на якійсь із високорівневих мов.

16 Востаннє редагувалося P.Y. (05.06.2023 00:00:05)

Re: Українська мова, С++, консоль

Основне, що треба знати про utf-8 — у цьому кодуванні використовуються символи різної довжини (одним байтом кодується лише базова латиниця та інше базове ASCII, всі решта символів — дво- і більше -байтні). При цьому, тип char вміщує в себе лише один байт. Тому, якщо код програми пишеться в utf-8, то щось ось таке:

char c='я';

видасть помилку, бо літери кирилиці займають по два байти, і вже сама символьна константа 'я' синтаксично  неправильна. Якщо ж зробити так:

char c="я"[0];

чи взагалі спробувати взяти таким способом окрему літеру з рядка в utf-8, що містить кириличний текст, то такий код буде синтаксично правильним, але у змінну c потрапить лише половина літери, один байт із двох. Тобто, працювати з кирилицею так само, як з латиницею чи цифрами, вже не вийде. Скопіювати кириличну літеру з рядка в рядок можна, але треба розуміти, що ця кирилична літера — з точки зору програми, два символи, а не один, і працювати з нею треба як з послідовністю з двох символів.

Напряму́ працювати з кириличним текстом в utf-8 — надто морочливо. Краще або справді працювати з UTF-16, wchar_t й рядками довгих символів, або перекодувати в якесь із однобайтних кириличних кодувань (cp1251 чи менш поширене в наш час cp21866 (воно ж KOI8-RU) підходять для української цілком, cp866 підходить дещо гірше) — з однобайтним можна й далі використовувати char і звичайні рядки, кириличні літери в однобайтних кириличних кодуваннях займають один байт і можуть записуватись у char так само, як латинські.