1

Тема: Глюк при роботі Regex з українською абеткою

Пишу код, який має прибирати всі нелітери зі слова. Це потрібно для сортування за абеткою.
Натрапив на цікаву поведінку Regex при обробці української абетки.
Ось код для тесту(має зникати лише апостроф після літери «В»):

string start_word {"АБВ'ГҐДЕЄЖЗІИЇЙКЛМНОПРСТУФХЦЧШЩЬЮЯабвгґдеєжзіиїйклмнопрстуфхцчшщьюя"};
string result_word;
//regex re {"(\W)"};
//regex re {"[^А-ЩЬЮЯҐЄІЇа-щьюяґєії]"};
regex re {"[^АБВГҐДЕЄЖЗІИЇЙКЛМНОПРСТУФХЦЧШЩЬЮЯабвгґдеєжзіиїйклмнопрстуфхцчшщьюя]"};

string replacement {""};
result_word = regex_replace(start_word, re, replacement);
cout << result_word << endl;

Перша формула Regex("(\W)") взагалі не працює чомусь, як я зрозумів.
Треба лише вказувати діапазон літер (A-Z, наприклад).
Хоча в Пайтон така формула добре працювала для будь-якої абетки.
Друга формула працює лише частково.
Якщо запустити цей код і поглянути на вивід до консолі,
то можна побачити, що після літери «п»(лише маленької) чомусь зникає кілька літер.
Замість них з'являється знак питання в квадратику.
Якщо використати третю формулу, усе працює чудово.

Чому діапазон «а-щ» не працює як слід?
Чи є в Regex для C++ короткий запис усіх літер(нелітер) будь-якої абетки?

2

Re: Глюк при роботі Regex з українською абеткою

Імовірно, якісь труднощі з кодуваннями. Третій Python використовує як внутрішній формат юнікодівські рядки — там кожна українська літера має свій фіксований код, є можливість автоматично розрізняти літери для всього юнікоду. У C/C++ за замовчуванням використовуються рядки байтів, і в різних кодуваннях діапазони кириличних літер можуть відрізнятися. Скоріш за все, бібліотека, що реалізує регекси, вважає літерами лише англійські літери базового ASCII, решту слід розбирати вручну (у другому пітоні, та й загалом при роботі регексів з рядками байтів у пітоні, виникають такі ж проблеми).

Діапазон  а-щ. У ДОС-кодування (cp866) діапазон кириличних літер розривається між «п» та «р» — між ними ввіткнули символи малювання рамок. Але роботі програми це б не завадило — усі кириличні літери, що потрапляють у цей діапазон у юнікоді, потрапляють у цей же діапазон ДОС-кирилиці. Або ж, можливо, там у вас utf-8, двобайтні кириличні коди якого ця бібліотека обробляє як однобайтне кодування, тому виходить щось непередбчачуване. Коротше, треба дивитись, що там у вас за кодування в консолі, і що за кодування в редакторі коду.

Подякували: Teg Miles1

3

Re: Глюк при роботі Regex з українською абеткою

P.Y. написав:

Імовірно, якісь труднощі з кодуваннями. Третій Python використовує як внутрішній формат юнікодівські рядки — там кожна українська літера має свій фіксований код, є можливість автоматично розрізняти літери для всього юнікоду. У C/C++ за замовчуванням використовуються рядки байтів, і в різних кодуваннях діапазони кириличних літер можуть відрізнятися. Скоріш за все, бібліотека, що реалізує регекси, вважає літерами лише англійські літери базового ASCII, решту слід розбирати вручну (у другому пітоні, та й загалом при роботі регексів з рядками байтів у пітоні, виникають такі ж проблеми).

Діапазон  а-щ. У ДОС-кодування (cp866) діапазон кириличних літер розривається між «п» та «р» — між ними ввіткнули символи малювання рамок. Але роботі програми це б не завадило — усі кириличні літери, що потрапляють у цей діапазон у юнікоді, потрапляють у цей же діапазон ДОС-кирилиці. Або ж, можливо, там у вас utf-8, двобайтні кириличні коди якого ця бібліотека обробляє як однобайтне кодування, тому виходить щось непередбчачуване. Коротше, треба дивитись, що там у вас за кодування в консолі, і що за кодування в редакторі коду.

У мене всюди UTF-8. Працюю з Code Blocks, OS Manjaro.

4 Востаннє редагувалося wander (09.09.2023 17:42:21)

Re: Глюк при роботі Regex з українською абеткою

Vitaliy_Danmer написав:

Чи є в Regex для C++ короткий запис усіх літер(нелітер) будь-якої абетки?

Почнімо з того, що C++ std::regex - це прекол, а не regex. Я б його не використовував.

Vitaliy_Danmer написав:

Чому діапазон «а-щ» не працює як слід?
У мене всюди UTF-8.

Спробуйте у регекс передати опцію collate, якщо я все вірно підозрюю, то проблема може бути у тому, що регекс невірно обробляє [а-щьюяґєії] діапазони символів з локалі.

std::regex re {"[^А-ЩЬЮЯҐЄІЇа-щьюяґєії]", std::regex::collate}; // << мало б бути ОК
Подякували: Teg Miles, leofun01, plusxx3

5

Re: Глюк при роботі Regex з українською абеткою

wander написав:
Vitaliy_Danmer написав:

Чи є в Regex для C++ короткий запис усіх літер(нелітер) будь-якої абетки?

Почнімо з того, що C++ std::regex - це прекол, а не regex. Я б його не використовував.

Vitaliy_Danmer написав:

Чому діапазон «а-щ» не працює як слід?
У мене всюди UTF-8.

Спробуйте у регекс передати опцію collate, якщо я все вірно підозрюю, то проблема може бути у тому, що регекс невірно обробляє [а-щьюяґєії] діапазони символів з локалі.

std::regex re {"[^А-ЩЬЮЯҐЄІЇа-щьюяґєії]", std::regex::collate}; // << мало б бути ОК

З collate все одно виникає ця помилка.
А що скажете про wxRegEx з wxWidgets?

6

Re: Глюк при роботі Regex з українською абеткою

Тоді найбільш імовірне джерело проблеми — те, що регекси обробляють це кодування як однобайтне, тоді як кожна кирилична літера насправді складається з двох байтів. Проблему можна обійти, уникаючи роботи з кириличними літерами як з окремими символами (напр., списки літер у квадратних дужках насправді роблять не те, що ви думаєте — замість "[абв]" треба використовувати "а|б|в", бо кожна кирилична літера — насправді пара символів типу char).

Або ж, можливо, регекси в цій бібліотеці вміють працювати і з рядками широких символів (wchar_t) — тоді можна перекодовувати рядки з utf-8 у широкосимвольний формат, використовувати широкосимвольні регекси для маніпуляцій з ним, потім перекодовувати назад в utf-8. Як саме це робиться в С++, на жаль, не скажу, але загальний підхід такий.

Ще можливий варіант — перекодовувати utf-8 у якесь із однобайтних кодувань з повною українською кирилицею, напр., cp1251 (регекс теж треба перекодовувати в це кодування) — тоді регекс-бібліотека, розрахована на char, зможе повноцінно працювати з українськими літерами, але може виникнути несумісність з тими юнікодівськими символами, яких у цьому кодуванні нема.

Подякували: Teg Miles, leofun012

7 Востаннє редагувалося wander (09.09.2023 18:06:28)

Re: Глюк при роботі Regex з українською абеткою

Vitaliy_Danmer написав:
wander написав:
Vitaliy_Danmer написав:

Чи є в Regex для C++ короткий запис усіх літер(нелітер) будь-якої абетки?

Почнімо з того, що C++ std::regex - це прекол, а не regex. Я б його не використовував.

Vitaliy_Danmer написав:

Чому діапазон «а-щ» не працює як слід?
У мене всюди UTF-8.

Спробуйте у регекс передати опцію collate, якщо я все вірно підозрюю, то проблема може бути у тому, що регекс невірно обробляє [а-щьюяґєії] діапазони символів з локалі.

std::regex re {"[^А-ЩЬЮЯҐЄІЇа-щьюяґєії]", std::regex::collate}; // << мало б бути ОК

З collate все одно виникає ця помилка.
А що скажете про wxRegEx з wxWidgets?

Не знайомий з wxWidgets, але мені здається будь-який регекс кращий за std::regex :D
До того ж, як я розумію, ви і так працюєте з wxWidgets, то логічніше було б саме його і використовувати.
Я зробив швидку перевірку, отут наче все працює. Ще як варіант можна спробувати переїхати на широкі символи. Але здається std::regex має проблеми з юнікодом, щось таке пригадується, але вже не згадаю з чим саме, тре доки піднімати..

Подякували: Teg Miles, leofun012

8 Востаннє редагувалося Teg Miles (09.09.2023 19:09:49)

Re: Глюк при роботі Regex з українською абеткою

wander написав:
Vitaliy_Danmer написав:
wander написав:

Почнімо з того, що C++ std::regex - це прекол, а не regex. Я б його не використовував.

Спробуйте у регекс передати опцію collate, якщо я все вірно підозрюю, то проблема може бути у тому, що регекс невірно обробляє [а-щьюяґєії] діапазони символів з локалі.

З collate все одно виникає ця помилка.
А що скажете про wxRegEx з wxWidgets?

Не знайомий з wxWidgets, але мені здається будь-який регекс кращий за std::regex :D
До того ж, як я розумію, ви і так працюєте з wxWidgets, то логічніше було б саме його і використовувати.
Я зробив швидку перевірку, отут наче все працює. Ще як варіант можна спробувати переїхати на широкі символи. Але здається std::regex має проблеми з юнікодом, щось таке пригадується, але вже не згадаю з чим саме, тре доки піднімати..

Я не хотів використовувати wxRegEx, бо не знав як проміжні результати на консоль вивести.
Спочатку думав, що Regex C++ та wxWidgets приблизно однакові.
Тепер знаю про wxStreamToTextRedirector, тому буду користуватися wxRegEx.
wxRegEx, здається, з Perl списаний, принаймні частково.
Але лишається проблема з нелатинськими абетками.
При обробці українських слів видає крякозябри, хоча заміну робить правильно.
Там використовуються змінні формату wxString.

9

Re: Глюк при роботі Regex з українською абеткою

Vitaliy_Danmer написав:

Я не хотів використовувати wxRegEx, бо не знав як проміжні результати на консоль вивести.
Спочатку думав, що Regex C++ та wxWidgets приблизно однакові.
Тепер знаю про wxStreamToTextRedirector, тому буду користуватися wxRegEx.
wxRegEx, здається, з Perl списаний, принаймні частково.
Але лишається проблема з нелатинськими абетками.
При обробці українських слів видає крякозябри, хоча заміну робить правильно.
Там використовуються змінні формату wxString.

Гм, цікаво..

https://docs.wxwidgets.org/trunk/overview_string.html написав:

wxString Internal Representation

By default, wchar_t is used under all platforms, but wxWidgets can be compiled with wxUSE_UNICODE_UTF8=1 to use UTF-8 instead.

Можливо через це проблеми з виведенням українських літер в консоль, ну і тре дивитись, що wxStreamToTextRedirector робить. На лінуксі вам не потрібні широкі символи (насправді їх ніде краще не використовувати).

Подякували: Teg Miles, leofun012

10

Re: Глюк при роботі Regex з українською абеткою

wander написав:
Vitaliy_Danmer написав:

Я не хотів використовувати wxRegEx, бо не знав як проміжні результати на консоль вивести.
Спочатку думав, що Regex C++ та wxWidgets приблизно однакові.
Тепер знаю про wxStreamToTextRedirector, тому буду користуватися wxRegEx.
wxRegEx, здається, з Perl списаний, принаймні частково.
Але лишається проблема з нелатинськими абетками.
При обробці українських слів видає крякозябри, хоча заміну робить правильно.
Там використовуються змінні формату wxString.

Гм, цікаво..

https://docs.wxwidgets.org/trunk/overview_string.html написав:

wxString Internal Representation

By default, wchar_t is used under all platforms, but wxWidgets can be compiled with wxUSE_UNICODE_UTF8=1 to use UTF-8 instead.

Можливо через це проблеми з виведенням українських літер в консоль, ну і тре дивитись, що wxStreamToTextRedirector робить. На лінуксі вам не потрібні широкі символи (насправді їх ніде краще не використовувати).

Хіба при роботі з японською мовою широкі символи не знадобляться?

11

Re: Глюк при роботі Regex з українською абеткою

Не знадобляться. У будь-якому випадку, я гадаю, ваш підхід з сортування за абеткою, який працює для української, не спрацює для японської.

12

Re: Глюк при роботі Regex з українською абеткою

wander написав:

Не знадобляться. У будь-якому випадку, я гадаю, ваш підхід з сортування за абеткою, який працює для української, не спрацює для японської.

Для японської звичайне сортування добре працює, бо там немає ані апострофів, ані наголосів.
Але щоб шукати через латинські літери по японському словнику треба писати пошук для транслітерації Хепберна.
Ось це wxUSE_UNICODE_UTF8=1 не допомогло, додавав його до setup.h.
Там, до речі, лише wxUSE_UNICODE є. Нічого іншого пов'язаного з юнікодом немає.

13

Re: Глюк при роботі Regex з українською абеткою

wander написав:
Vitaliy_Danmer написав:

Я не хотів використовувати wxRegEx, бо не знав як проміжні результати на консоль вивести.
Спочатку думав, що Regex C++ та wxWidgets приблизно однакові.
Тепер знаю про wxStreamToTextRedirector, тому буду користуватися wxRegEx.
wxRegEx, здається, з Perl списаний, принаймні частково.
Але лишається проблема з нелатинськими абетками.
При обробці українських слів видає крякозябри, хоча заміну робить правильно.
Там використовуються змінні формату wxString.

Гм, цікаво..

https://docs.wxwidgets.org/trunk/overview_string.html написав:

wxString Internal Representation

By default, wchar_t is used under all platforms, but wxWidgets can be compiled with wxUSE_UNICODE_UTF8=1 to use UTF-8 instead.

Можливо через це проблеми з виведенням українських літер в консоль, ну і тре дивитись, що wxStreamToTextRedirector робить. На лінуксі вам не потрібні широкі символи (насправді їх ніде краще не використовувати).

Швидше за все, проблема в wxStreamToTextRedirector. Він сприймає лише англійську абетку.
Якщо додавати текст до wxTextCtrl вручну або через код напряму, то все працює добре.
Лише через wxStreamToTextRedirector крякозябри виходять.

14 Востаннє редагувалося wander (10.09.2023 13:25:53)

Re: Глюк при роботі Regex з українською абеткою

Vitaliy_Danmer написав:

Швидше за все, проблема в wxStreamToTextRedirector. Він сприймає лише англійську абетку.
Якщо додавати текст до wxTextCtrl вручну або через код напряму, то все працює добре.
Лише через wxStreamToTextRedirector крякозябри виходять.

Насправді підхід з wxStreamToTextRedirector виглядає дуже костильно. А, якийсь умовний utf8_string не працює? З виставленим wxUSE_UNICODE_UTF8=1 прапорцем мало б працювати.

wxString test = "Український текст";
cout << test.utf8_string() << endl;

Хоча, якщо я виставив wxUSE_UNICODE_UTF8=1, то я б очікував, що їхній c_str теж працюватиме, щоб без зайвих копіювань wxString -> std::string.

wxString test = "Український текст";
cout << test.c_str() << endl;

Тобто, я б очікував, що такий код буде повністю валідним, але я не певен. Якось у них все дуже складно.

15

Re: Глюк при роботі Regex з українською абеткою

wander написав:
Vitaliy_Danmer написав:

Швидше за все, проблема в wxStreamToTextRedirector. Він сприймає лише англійську абетку.
Якщо додавати текст до wxTextCtrl вручну або через код напряму, то все працює добре.
Лише через wxStreamToTextRedirector крякозябри виходять.

Насправді підхід з wxStreamToTextRedirector виглядає дуже костильно. А, якийсь умовний utf8_string не працює? З виставленим wxUSE_UNICODE_UTF8=1 прапорцем мало б працювати.

wxString test = "Український текст";
cout << test.utf8_string() << endl;

Хоча, якщо я виставив wxUSE_UNICODE_UTF8=1, то я б очікував, що їхній c_str теж працюватиме, щоб без зайвих копіювань wxString -> std::string.

wxString test = "Український текст";
cout << test.c_str() << endl;

Тобто, я б очікував, що такий код буде повністю валідним, але я не певен. Якось у них все дуже складно.

Не працює.

16 Востаннє редагувалося wander (10.09.2023 13:55:55)

Re: Глюк при роботі Regex з українською абеткою

Тоді, я б взяв дебагер в руки та дивився, що зберігається в умовному wxString, char чи wchar_t (можливо прапорець wxUSE_UNICODE_UTF8=1 з тих чи інших причин ігнорується). І, як символи закодовані, чи схоже це на utf-8 і тому подібне. Бо причини чому

Vitaliy_Danmer написав:

Не працює

може буде з десяток. До того ж я не бачу вашого коду цілком і не можу знати, що саме ви там робите, а починати грати у гру "вгадай, чому не працює", бажання немає )

17

Re: Глюк при роботі Regex з українською абеткою

wander написав:

Тоді, я б взяв дебагер в руки та дивився, що зберігається в умовному wxString, char чи wchar_t (можливо прапорець wxUSE_UNICODE_UTF8=1 з тих чи інших причин ігнорується). І, як символи закодовані, чи схоже це на utf-8 і тому подібне. Бо причини чому

Vitaliy_Danmer написав:

Не працює

може буде з десяток. До того ж я не бачу вашого коду цілком і не можу знати, що саме ви там робите, а починати грати у гру "вгадай, чому не працює", бажання немає )

Ось, що я роблю:

search_result_window = new wxTextCtrl(result_box_label,
                                          wxID_ANY,
                                          wxEmptyString,
                                          wxDefaultPosition,
                                          wxDefaultSize,
                                          wxTE_MULTILINE|wxTE_READONLY);

wxString word {wxT("М'яч")};
wxRegEx re("[^A-za-zА-ЩЬЮЯҐЄІЇА́а-щьюяґєії]", wxRE_ADVANCED + wxRE_ICASE);
re.ReplaceAll(&word, wxT("G"));
wxStreamToTextRedirector redirect(search_result_window);
cout << word << endl;