Тема: 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, а вводити англійський текст;
забити на кирилицю і писати латинкою. Для студентів саме так і раджу робити.