1

Тема: Не працює порівняння керилиці в С++.

Написана програмка на C++ котра робить транслітерацію український літер в латинські і навпаки.
Однак коли вводиться  з клавіатури текст на латиниці топрограма працює нормально, а коли кирилицею
то текст не пертворюється. Операційна система ManjaroLinux(CInamon)IDE Code::BLocks компілятор GCC
термінал "Gnome terminal".Мені здається що проблема від бувається на етапі порівняння двох змінних котрі містять
кириличні символи.
P.S. Я знаю що можна перевести вміст змінної char в змінну іnt а потім порівнювати коди сисстеми ANSCII і зробити вивід відповідних символів по коду але мене цікавить чи неможна зробити якісь маніпуляції з <locale>так щоб порівняння працювало.

2

Re: Не працює порівняння керилиці в С++.

Можна, звісно. Робіть.

3

Re: Не працює порівняння керилиці в С++.

pluszz написав:

Написана програмка на C++ котра робить транслітерацію український літер в латинські і навпаки.
Однак коли вводиться  з клавіатури текст на латиниці топрограма працює нормально, а коли кирилицею
то текст не пертворюється. Операційна система ManjaroLinux(CInamon)IDE Code::BLocks компілятор GCC
термінал "Gnome terminal".Мені здається що проблема від бувається на етапі порівняння двох змінних котрі містять
кириличні символи.
P.S. Я знаю що можна перевести вміст змінної char в змінну іnt а потім порівнювати коди сисстеми ANSCII і зробити вивід відповідних символів по коду але мене цікавить чи неможна зробити якісь маніпуляції з <locale>так щоб порівняння працювало.

А код буде?
Чи це ми телепатично маємо дізнатися, яка у вас проблема.

pluszz написав:

Я знаю що можна перевести вміст змінної char в змінну іnt а потім порівнювати коди сисстеми ANSCII і зробити вивід відповідних символів по коду

Чар і так ціле число, що ви хочете в int переводити?
std::string має свій "розумний" порівнятор
https://en.cppreference.com/w/cpp/strin … ng/compare

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

4

Re: Не працює порівняння керилиці в С++.

А ось власне код

#include <iostream>
#include <stdio.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <locale>


using namespace std;

int getch(void)// зчитування з клавіатури одного символа
    {
        struct termios oldt, newt;
        int ch;

        tcgetattr(STDIN_FILENO, &oldt);
        newt = oldt;
        newt.c_lflag &= ~(ICANON | ECHO);
        tcsetattr(STDIN_FILENO, TCSANOW, &newt);
        ch = getchar();
        tcsetattr(STDIN_FILENO, TCSANOW, &oldt);

        return ch;
    }

bool kbhit()//функція котра дає сигнал true пoки нажата клавіша
    {
        termios term;
        tcgetattr(0, &term);

        termios term2 = term;
        term2.c_lflag &= ~ICANON;
        tcsetattr(0, TCSANOW, &term2);

        int byteswaiting;
        ioctl(0, FIONREAD, &byteswaiting);

        tcsetattr(0, TCSANOW, &term);

        return byteswaiting > 0;
    }
void clear (void)//очистка консолі
    {
        cout << "\033[0d\033[2J";
    }
string stInputLine="\0";//рядок вводу
string stOutputLine="\0";//рядок виводу
char fstInput()//Функція вводу
    {
         setlocale(LC_ALL, "Ukrainian");//Українська локалізація
         char stInputLineElement='\0';//змінна котрій почерзі присвоюється символ введений з клави

                while(true)//поки істина
                {
                          if(kbhit())//якщо натинута будь яка клавіша...
                          {
                                    stInputLineElement=getch();//...присвоїти змінній введений з клавіатури символ
                                    stInputLine+=stInputLineElement;//і добавити вміст змінної до ря дка вводу
                                    break;
                          }
                          else
                          {
                             break;
                          }
                }

            return stInputLineElement;// повертаємо main введений символ
        }
class TranliterationClass
{

public:
void Tranliteration(char stInputSymbol)//
{

    setlocale(LC_ALL, "Ukrainian");//Українська локаль
    string sTinpSymbol="\0";//проміжний символ для переводу char d string обнуляється зкаждим циклом;
    sTinpSymbol+=stInputSymbol;//присвоїти аргумент до змінної
    //масив укр символів
    string chSymbolMass[90]  {"кс"," ","\n","'","А","а","Б","б","В","в","Г","г","Ґ","ґ","Д","д","Е","е","Є","є","Ж","ж","З","з","И","и","І","і","Ї","ї","Й","й","К","к","Л","л","М","м","Н","н","О","о","П","п","Р","р","С","с","Т","т","У","у","Ф","ф","Х","х","Ц","ц","Ч","ч","Ш","ш","Щ","щ","ь","Ю","ю","Я","я",".",",",";",":","-","_","=","+","(",")","1","2","3","4","5","6","7","8","9","0"};
    //масив трансліт символів
    string chTranslitMass[90]{"x"," ","\n","'","A","a","B","b","V","v","G","g","G`","g`","D","d","E","e","Ye","ye","Zh","zh","Z","z","Y","y","I","i","Yi","yi","J","j","K","k","L","l","M","m","N","n","O","o","P","p","R","r","S","s","T","t","U","u","F","f","H","h","C","c","Ch","ch","Sh","sh","Shch","shch","`","Yu","yu","Ya","ya",".",",",";",":","-","_","=","+","(",")","1","2","3","4","5","6","7","8","9","0"};
        for(int i=0;i<89;i++)//цикл провірки
            {
                if(sTinpSymbol==chSymbolMass[i])//якщо введений символ відповідає символу в "і" масиві укр символів(цей код не працює)
                    {
                        stOutputLine+=chTranslitMass[i];//...то до рядка виводу додати символ "і" з трансліт масиву
                    }

                if(sTinpSymbol==chTranslitMass[i])//якщо введений символ відповідає символу в "і" масиві трансліт символів(а цей код працює)
                    {
                        stOutputLine+=chSymbolMass[i];//...то до рядка виводу додати символ "і" з трансліт масиву
                    }
            }
        cout<<stOutputLine<<endl;//вивести на екран рядок виводу
}
};

int main( int nNumberOfArgs , char* pszArgs[ ] )
    {
        for(;;)//безкінеечний цикл
        {
            clock_t start, stop;
                 unsigned long t;//Таймер для стабілізації виводу (щоб немигали літери)
                start = clock();
                for(t=0;t<100000L;t++)
                stop =clock();
            clear();//Очистка екрану
        

        TranliterationClass tc;
        cout<<"Введіть текст кирилецею"<<endl;
        char stInputLineElement=fstInput();//змінна котрій функція повертає значення ко жного введеного знаку
        tc.Tranliteration(stInputLineElement);//виклик функції котрій належить транслітерація знаків
                                            //з передачею введеного знака в якості аргумента
        cout<<":::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::"<<endl;// понти для "приезжих"

        cout<<stInputLine<<endl;//вивід рядка вводу


    }


        return 0;
    }

5

Re: Не працює порівняння керилиці в С++.

Ось такий можна "перекладач" забабахати на utf-8, відповідно це лише для unix.

#include <iostream>
#include <codecvt>
#include <locale>
#include <string>
#include <map>

int main() {
    std::map<std::string, std::string> symbolic_table{
        {"а", "a"},  {"б", "b"},  {"в", "v"},  {"г", "h"},    {"д", "d"}, 
        {"е", "e"},  {"є", "ye"}, {"ж", "zh"}, {"з", "z"},    {"и", "y"}, 
        {"і", "i"},  {"ї", "yi"}, {"й", "ye"}, {"к", "k"},    {"л", "l"}, 
        {"м", "m"},  {"н", "n"},  {"о", "o"},  {"п", "p"},    {"р", "r"}, 
        {"с", "s"},  {"т", "t"},  {"у", "u"},  {"ф", "f"},    {"х", "h"}, 
        {"ц", "c"},  {"ч", "ch"}, {"ш", "sh"}, {"щ", "shch"}, {"ь", "'"}, 
        {"ю", "yu"}, {"я", "ya"}, {" ", " "}
    };    
    std::u32string input{U"привіт світе"};
    
    std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> converter;
    for(char32_t cyrillic_symbol : input) {
        auto table_it = symbolic_table.find(converter.to_bytes(cyrillic_symbol));
        std::cout << table_it->second;
    }
}
Подякували: pluszz1

6

Re: Не працює порівняння керилиці в С++.

adziri написав:

Ось такий можна "перекладач" забабахати на utf-8, відповідно це лише для unix.

Якщо не важко можна обьяснення, або ссилочку на яких принципах воно там працює бо шось моя не мудра макітра ніяк не хоче того поняти.

7 Востаннє редагувалося wander (26.04.2019 12:08:56)

Re: Не працює порівняння керилиці в С++.

pluszz написав:
adziri написав:

Ось такий можна "перекладач" забабахати на utf-8, відповідно це лише для unix.

Якщо не важко можна обьяснення, або ссилочку на яких принципах воно там працює бо шось моя не мудра макітра ніяк не хоче того поняти.

Ну, як завжди, трохи через дупу.
Перша проблема у нас з тим, що взагалом стандарт С++,
не дає точної спеціалізації розміру фундаментальних типів,
і каже, що нехай вони всі будуть залежати від архітектури,
окрім типу char, він завжди буде 1 байт, згідно стандарту.
Як ви напевне розумієте в 1 байт влізе не дуже багато даних,
цифри, латинський алфавіт (включаючи капс), та ще деякі символи.
Кирилиці місця не вистачить. Проте utf-8 підтримує набагато більше
і у юніксі ми можемо у стрічку (std::string) писати, напр, кирилицею,
але не тре думати, що це безкоштовно, ми відповідно тратимо
на один символ більше одного байту, на одну букву українського
алфавіту буде йти мінімум 2 байти, відповідно, точно не знаючи
розміру одного символу, як ми можемо перебрати кожну букву стрічки?
Ось тут і тре піднапрягтися, нам потрібно кожен символ декодувати
із декількох чарів в один більш "широкий" чар, яким є char32_t, який як я думаю
видно з назви займає 32 біти, тобто 4 байти, для нас цього має хватити
з головою, я думаю char16_t теж би підійшов. Власне от і все,
wstring_convert - займається декодуванням, а у циклі ми просто підставляємо
букву з української стрічки, знаходимо її у нашому словнику і беремо звідти її
перекладений варіант.

Подякували: leofun01, pluszz2

8 Востаннє редагувалося wander (26.04.2019 12:24:13)

Re: Не працює порівняння керилиці в С++.

typedef struct {
    char raw_string_[4];
} wide_char_t;

std::ostream& operator<<(std::ostream& os, wide_char_t const& wstr) { 
    os << wstr.raw_string_; 
    return os;
}
    
 
int main() {
    char str[25]{"Привіт"};
    std::cout << str[0] << '\n';
    
    wide_char_t wstr[25]{ {"П"}, {"р"}, {"и"}, {"в"}, {"і"}, {"т"} };
    std::cout << wstr[0] << '\n';
}

Якось так, у першому варіанті очікувано отримує не букву "П", на відміну від 2-го.
Це доречі, як варіант, щоб не займатися перекодовуванням, можна самому це
обробляти, достатньо правильно перегрузити operator<< і operator>>.

Подякували: leofun01, pluszz2

9

Re: Не працює порівняння керилиці в С++.

adziri написав:
typedef struct {
    char raw_string_[4];
} wide_char_t;

std::ostream& operator<<(std::ostream& os, wide_char_t const& wstr) { 
    os << wstr.raw_string_; 
    return os;
}
    
 
int main() {
    char str[25]{"Привіт"};
    std::cout << str[0] << '\n';
    
    wide_char_t wstr[25]{ {"П"}, {"р"}, {"и"}, {"в"}, {"і"}, {"т"} };
    std::cout << wstr[0] << '\n';
}

.

То шось правда не зтої опери.

10 Востаннє редагувалося wander (19.12.2020 22:33:49)

Re: Не працює порівняння керилиці в С++.

pluszz написав:

То шось правда не зтої опери.

Все воно з тої опери..
Я про те, що у вас по суті лише два варіанти. Тобто, можна кожен символ зберігати у масиві чарів або перекодовувати в один, але більш "широкий" чар. Якщо декодувати, то треба трошки розуміти як працює utf-8,у utf-8 символ має так звані значущі (тобто ті, які містять власне сам код символу) розряди, але проблема у тому, що ці розряди займають не всю розрядну сітку байтів. Для двобайтового символу схема така: 110xxxxx 10xxxxxx, де x - це значущі розряди, в першому байті їх 5, а в другому 6. Наприклад візьмемо український символ "в", він буде складатися з 2-х окремих байтів, ось так 11010000 10110010, тобто значущі біти тут - це 10000 і 110010, потім нам потрібно ці два окремі байти схлопнути в один, наприклад, широкий 4-х байтовий чар, відповідно ці два вхідні байти мають магією
перетворитися в 10000110010, це і буде буква "в" у, наприклад, якомусь char32. Проте займатися декодуванням трохи складно, на словах може і виглядає легко, проте це лише виглядає :)
Я у попередньому пості запропонував користуватися масивом з декількох чарів. Тобто у нас стрічка буде двовимірним масивом, де один символ це два чари (байти), і відповідно ми можемо легко ітеруватися по нашій стрічці, це як по аналогії з цілими, у нас є велике число, але воно не поміщається у тип short, бо на нашій архітектурі він займає 2 байти, тобто вихід це
зберігати число у масиві з двох short'ів, або взяти один int, який займає 4 байти одразу.

Наприклад так:
(цього разу я взяв не 4, а 2 байти, бо так трохи легше, але можна спокійно зробити і 4 і 8, проте відповідно якісь ще ширші символи типу китайських ієрогліфів не пройдуть, їм буде замало місця, тому китайці, сорє, але не сьогодні)

Прихований текст
#include <iostream>

namespace details {

template <std::size_t N> 
class wchar final {
public:    
    wchar() noexcept = default;
    wchar(char const* const str) noexcept {
        raw_string_[0] = str[0];
        raw_string_[1] = str[1];
    }
    void fill_zero() noexcept {
        raw_string_[0] = '\0'; 
        raw_string_[1] = '\0';
    }
    bool empty() const noexcept {
        if (raw_string_[0] == '\0' && raw_string_[1] == '\0') {
            return true;
        }
        return false;
    }
    char const& operator[](std::size_t const index) const noexcept {
        return raw_string_[index];
    }
    char& operator[](std::size_t const index) noexcept {
        return const_cast<char&>(static_cast<wchar<N> const&>(*this)[index]);
    }
private:
    char raw_string_[N];
};
using wchar16_t = wchar<2>;

std::ostream& operator<<(std::ostream& os, wchar16_t const& wchar) { 
    os << wchar[0] << wchar[1]; 
    return os;
}

template <std::size_t N>
std::istream& operator>>(std::istream& is, wchar16_t(&wstr)[N]) { 
    std::int32_t i = 0;
    for (; !is.eof(); ++i) {
        std::int32_t first_byte = is.get();
        std::int32_t next_byte = 0;        
        switch ((first_byte & 0xf0) >> 4) {
            case 0xc:
                [[fallthrough]];
            case 0xd:
                next_byte = is.get();
                break;
        }
        wstr[i][0] = first_byte;
        wstr[i][1] = next_byte;
    }
    wstr[i].fill_zero();
    return is;
}
    
bool compare_wchar(wchar16_t const first, wchar16_t const second) noexcept {
    if (std::int32_t first_sum  = first[0]  + first[1], 
                     second_sum = second[0] + second[1];
                     first_sum == second_sum) {
        return true;
    }
    return false;
}
    
struct symbolic_wtable final {
    wchar16_t key;
    wchar16_t value;
    
    wchar16_t find(wchar16_t const wsymbol) noexcept {
        if (compare_wchar(key, wsymbol)) {
            return value;
        }
        return wchar16_t{};
    }
};

} // namespace details

int main() {
    details::symbolic_wtable table[33]{
        {"а", "a"},  {"б", "b"},  {"в", "v"},  {"г", "h"},    {"д", "d"}, 
        {"е", "e"},  {"є", "ye"}, {"ж", "zh"}, {"з", "z"},    {"и", "y"}, 
        {"і", "i"},  {"ї", "yi"}, {"й", "ye"}, {"к", "k"},    {"л", "l"}, 
        {"м", "m"},  {"н", "n"},  {"о", "o"},  {"п", "p"},    {"р", "r"}, 
        {"с", "s"},  {"т", "t"},  {"у", "u"},  {"ф", "f"},    {"х", "h"}, 
        {"ц", "c"},  {"ч", "ch"}, {"ш", "sh"}, {"щ", "shch"}, {"ь", "'"}, 
        {"ю", "yu"}, {"я", "ya"}, {" ", " "}
    }; 
    
    details::wchar16_t wstr[25];
    std::cin >> wstr; // ввели слово "привіт"
    std::cout << wstr[0] << '\n'; // тут вивели букву "п"

    for (std::size_t i = 0; !wstr[i].empty(); ++i) {
        for (std::size_t j = 0; j < 33; ++j) {
            std::cout << table[j].find(wstr[i]);
        }
    }
}

Протестити можна тут:
https://wandbox.org/permlink/dymunUcNGkmnix2L
Задавайте питання по коду, відповім як зможу.

Подякували: leofun01, pluszz, Lata3

11

Re: Не працює порівняння керилиці в С++.

Для себе питанням вважаю закритим велика подяка adziri (https://replace.org.ua/user/8912/)

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

12

Re: Не працює порівняння керилиці в С++.

Все добре, хочу лише дещо уточнити.

adziri написав:

Перша проблема у нас з тим, що взагалом стандарт С++,
не дає точної спеціалізації розміру фундаментальних типів,
і каже, що нехай вони всі будуть залежати від архітектури,
окрім типу char, він завжди буде 1 байт, згідно стандарту.

По стандартам C/C++ за визначенням sizeof(char) == 1. Просто «1», а не «1 байт».
Скільки там бітів — залежить від реалізації, довідатися можна через CHAR_BIT/std::numeric_limits<unsigned char>::digits
І саме стандарти C/C++ цілком припускають архітектуру з sizeof(int) == sizeof(char) == 1, але при цьому CHAR_BIT == 32

Інша справа, що переважна більшість систем розраховані на (а POSIX явно вимагає) CHAR_BIT == 8.

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

13 Востаннє редагувалося wander (30.04.2019 17:05:36)

Re: Не працює порівняння керилиці в С++.

ReAl написав:

Все добре, хочу лише дещо уточнити.

adziri написав:

Перша проблема у нас з тим, що взагалом стандарт С++,
не дає точної спеціалізації розміру фундаментальних типів,
і каже, що нехай вони всі будуть залежати від архітектури,
окрім типу char, він завжди буде 1 байт, згідно стандарту.

По стандартам C/C++ за визначенням sizeof(char) == 1. Просто «1», а не «1 байт».
Скільки там бітів — залежить від реалізації, довідатися можна через CHAR_BIT/std::numeric_limits<unsigned char>::digits
І саме стандарти C/C++ цілком припускають архітектуру з sizeof(int) == sizeof(char) == 1, але при цьому CHAR_BIT == 32

Інша справа, що переважна більшість систем розраховані на (а POSIX явно вимагає) CHAR_BIT == 8.

char завжди байт, але не завжди октет. Байт є найменшою адресною одиницею пам'яті (у більшості визначень), октет - 8-бітна одиниця пам'яті.
Все вірно макрос CHAR_BIT в limit.h визначає розмір байта для платформи, і він не завжди 8 біт. Є платформи з 16-бітовими і 32-розрядними байтами, отже char займе більше бітів, але це все ще байт. Оскільки необхідний діапазон для char становить щонайменше від -127 до 127 (або від 0 до 255), він буде, принаймні, 8 бітним на всіх платформах.

ISO/IEC 9899:TC3
6.5.3.4 The sizeof operator
...
The sizeof operator yields the size (in bytes) of its operand, which may be an expression or the parenthesized name of a type. [...]
When applied to an operand that has type char, unsigned char, or signed char, (or a qualified version thereof) the result is 1. [...]

Подякували: leofun01, ReAl2

14

Re: Не працює порівняння керилиці в С++.

Тьху, переклинило. Саме так.

Сам завжди пояснюю, що байт != 8 біт == октет, і недаремно там. де це важливо, пишуть саме «октет», а тут на місці «байт» побачив «8 біт» :D

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