1 Востаннє редагувалося koala (14.07.2014 21:08:18)

Тема: FAQ: Кодування і C++

На нинішньому етапі розвитку комп'ютерних технологій (ой як загнув :)) виникла чергова плутанина з кодуваннями. На форумах постійно з'являються запитання новачків "у мене виводяться кракозябри", "замість тексту ієрогліфи", "як виводити українською" і т.д., причому переважно ці питання пов'язані з C/C++. Тут я спробую відповісти на ці (і пов'язані) запитання і запропонувати способи вирішення проблем.

П: Що таке кодування?
В: Інформацію в комп'ютерах найлегше представляти числами. Щоб зберегти в комп'ютері літеру, треба домовитися про відповідність літер і чисел. Така відповідність і зветься кодуванням, бо ми кодуємо літери числами, наприклад А-1, Б-2 і т.д.

П: Які є варіанти латинських (англійських) кодувань?
В: Після деякого часу суперечок в англомовному світі прийшли до 7-бітного (тобто старший біт в байті з 8 біт - завжди 0) кодування ASCII. Оскільки сучасні комп'ютери походять від тих, то основою всіх сучасних кодувань є саме ASCII.

П: В різних мовах дуже багато різних символів; наприклад, китайських ієрогліфів близько 100 тисяч. Як же їх кодують, якщо в байті всього 8 біт і 256 значень?
В: Стара ідея полягала в тому, що до ASCII додавали ще 128 значень (зі старшим бітом 1) і кодували ними інші алфавіти, а програма (чи операційна система, якщо програма не уточнює) визначала, яке конкретно кодування застосовується, і чи число 200 означає "х", "Č" чи "И". Звісно, це не дозволяло використовувати одночасно кілька мов, і не задовільняло потреби ієрогліфічних Китая, Японії та Кореї. Тому виникла ідея багатобайтового кодування - юнікода, де різні символи займають різну кількість байтів (що ускладнює роботу з текстом). Втім, слід враховувати, що існує кілька варіантів представлення юнікода, хоча всі вони використовують одне й те саме кодування.

П: Які є варіанти кирилічних (зокрема, українських) кодувань?
В: Їх дуже багато. Зараз можна зустріти:

  • KOI8-U - різновид KOI8-R; відрізняється тим, що якщо занулити перший біт, більшість літер стануть схожими літерами ASCII, наприклад, "Привіт" стане "pRIW&T". При цьому, звісно, повністю губиться алфавітний порядок символів.

  • CP866 (інші назви - "альтернативне кодування", "кирилиця DOS") - розробка компанії Microsoft; відрізняється більш-менш алфавітним розташуванням літер (маленькі літери розірвані на дві групи, щоб зберегти символи псевдографіки, які активно використовувалися в DOS) і відсутністю питомо українських літер і, ї, є, ґ. Для забезпечення роботи цих літер робилися різні спроби ввести схожі кодування, але жодне з них не прижилося, і навіть зараз в консолі Windows 7 неможливо повноцінно використовувати літеру і.

  • CP1251 - стандартне кодування старих версій Windows(9x); сучасні версії також її розуміють. Алфавіт майже збережено (знову ж таки, "особливі" і, ї, є, ґ винесені за межі алфавіту).

  • ISO 8859-5 - затверджене міжнародним стандартом, алфавіт майже збережено, не містить літери ґ, використовується дуже мало.

  • Юнікод - як вже сказано, багатобайтовий. Для символів кирилиці виділені коди від U+0400 до U+052F.

П: Як C++ працює з кодуваннями?
В: В старих версіях це було залишено на програміста, стандартних функцій для роботи з кодуваннями не було. Звісно, таке становище дуже шкодило переносимості програм, і були розроблені стандартні засоби для роботи з різними кодуваннями, для C - бібліотека <locale.h> (clocale в C++) із функцією

char* setlocale (int category, const char* locale);

Ця функція змінює поведінку стандартних функцій C для роботи з рядками у відповідності до локалі і категорії, причому категорії бувають:

  • LC_COLLATE - впливає на функції strcoll and strxfrm

  • LC_CTYPE - впливає на функції з cctype, крім isdigit and isxdigit

  • LC_MONETARY - впливає на інформацію про грошові одиниці з функції localeconv.

  • LC_NUMERIC - впливає на форматування чисел при вводі-виводі (зокрема, на десяткову кому) і включає LC_MONETARY

  • LC_TIME - впливає на strftime

  • LC_ALL - включає все попереднє

Другий параметр - назва локалі - залежить від ОС; Windows розуміє прості назви на кшталт "Ukrainian" чи "Russian"; іншим системам треба давати більш точні вказівки типу "uk_UA.cp1251" чи "en_US.utf8" (у форматі мова_країна.кодування).
Для C++ була повністю перероблена бібліотека cctype (ctype.h), і тепер є бібліотека locale, що містить всі важливі функції з ctype і ще купу різних (там не так все просто, і в цьому FAQ просто нема місця все розкривати; читайте документацію).
Крім того, була додана підтримка юнікода: довгі символи (wchar_t), довгі рядки (L"日本語" буде кодовано в wchar_t, а не в char), бібліотеки <cwchar> (wchar.h) та <cwctype> (wctype.h), класс std::wstring і т.д.

(і от найголовніше)
П: То як мені змусити мою програму в Dev-C++ нормально працювати з українською (кириличною, російською) консоллю?
В: Dev-C++ написаний в сумісності з Windows 9x і не підтримує юнікода, тільки кодові сторінки (для України, нагадую, cp1251). Консоль Windows для сумісності зі старими програмами кодована в cp866. Локаль впливає на внутрішнє кодування і дає підказки системі, але прямо не впливає на ввід-вивід. Це глухий кут, з якого немає нормального виходу; можна запропонувати на вибір:

  • користуватися іншим редактором (наприклад, тільки для написання кирилиці, а в Dev-C++ воно буде "кракозябрами", хоча при виконанні програми все буде добре);

  • використовувати при виводі функції перетворення за допомогою системих функцій (зокрема, CharToOemBuff чи свою обгортку навколо нього);

  • погратися системними функціями SetConsoleCP та SetConsoleOutputCP (некультурний варіант - викликом system("chcp 1251>null");); це нормальний шлях для роботи в Visual C++, якщо у вас вийшло нормально вводити кирилицю в Dev-C++ в консолі за допомогою цього - відпишіться;

  • обмежитися встановленням локалі за допомогою setlocale, а вводити англійський текст;

  • забити на кирилицю і писати латинкою. Для студентів саме так і раджу робити.

2

Re: FAQ: Кодування і C++

koala написав:

погратися системними функціями SetConsoleCP та SetConsoleOutputCP (некультурний варіант - викликом system("chcp 1251>null");); це нормальний шлях для роботи в Visual C++, якщо у вас вийшло нормально вводити кирилицю в Dev-C++ в консолі за допомогою цього - відпишіться;

Цей варіант працює в консольному вікні з векторними шрифтами (Lucida Console) і працює неадекватно, якщо використовуються растрові шрифти чи повноекранний текстовий режим. Оскільки за замовчуванням консольне вікно запускається з растровими шрифтами, потрібно додатково лізти в налаштування консольного вікна, щоб побачити результат (або ж можна змінити шрифт у налаштуваннях за замовчуванням — тоді консольне вікно щоразу запускатиметься з заданим шрифтом).

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

3

Re: FAQ: Кодування і C++

koala написав:

CP866 (інші назви - "альтернативне кодування", "кирилиця DOS") - розробка компанії Microsoft; відрізняється більш-менш алфавітним розташуванням літер (маленькі літери розірвані на дві групи, щоб зберегти символи псевдографіки, які активно використовувалися в DOS) і відсутністю питомо українських літер і, ї, є, ґ.

Насправді літери Ї та Є в цьому кодуванні присутні, як і білоруська Ў. Ґ та І справді нема, українську/білоруську І, вочевидь, передбачалось передавати латинською.

В 90-х роках мало також деяке розповсюдження т.зв. українське ГОСТ-кодування (також відоме як RUSCII чи CP1125) — близьке до CP866, але з повним набором українських літер. Оскільки це кодування виникло незалежно від CP866 (хоча й мало того ж попередника — «альтернативне кодування», в якому було представлено лише російський алфавіт), українські літери в CP866 та RUSCII кодуються по-різному, що часто призводило до помилок при використанні одного кодування замість іншого й сприяло закріпленню думки, що українські літери в ДОС-кодуванні відсутні взагалі.

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