Что такое Unicode и как с ним работать в C++?

«Что такое Unicode и как с ним работать в C++?» — вопрос из категории C++ Core, который задают на 25% собеседований C/C++ Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Unicode — это универсальный стандарт кодирования символов, который ставит в соответствие каждому символу из большинства письменных систем мира уникальный числовой код (кодовую точку, code point). В отличие от ASCII (128 символов), Unicode охватывает более 140 000 символов, включая исторические шрифты, эмодзи и специальные знаки.

Основные кодировки (форматы хранения байтов):

  • UTF-8: Переменная длина (1-4 байта). Обратно совместима с ASCII (первые 128 символов кодируются одним байтом). Стандарт для веба и многих систем. Является кодировкой по умолчанию для строковых литералов во многих компиляторах.
  • UTF-16: Использует 2 или 4 байта (суррогатные пары для символов за пределами Basic Multilingual Plane). Нативный для Windows API и Java.
  • UTF-32: Фиксированная длина (4 байта на кодовую точку). Прост для обработки, но неэффективен по памяти.

Работа с Unicode в современном C++ (C++11 и выше):

#include <iostream>
#include <string>
#include <locale>
#include <codecvt> // Для конвертации (устарело в C++17, но пока используется)

int main() {
    // Строковые литералы с префиксами:
    std::string utf8_str = u8"Привет, 世界! 😊"; // UTF-8 (char)
    std::u16string utf16_str = u"Привет, 世界! 😊"; // UTF-16 (char16_t)
    std::u32string utf32_str = U"Привет, 世界! 😊"; // UTF-32 (char32_t)
    std::wstring wide_str = L"Привет, 世界! 😊"; // Широкая строка (wchar_t, зависит от платформы)

    std::cout << "UTF-8 output: " << utf8_str << std::endl;

    // Конвертация UTF-8 в UTF-16 (используя устаревший, но пока распространенный codecvt)
    std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> converter;
    std::u16string converted = converter.from_bytes(utf8_str);

    // **Важно:** Для корректного вывода в консоль Windows может потребоваться
    // изменить кодовую страницу консоли или использовать API WriteConsoleW.
    // На Linux/macOS с UTF-8 локалью вывод обычно работает корректно.

    // Итерация по кодовым точкам UTF-8 (упрощенно):
    for (char c : utf8_str) {
        // Но так мы итерируем по байтам, а не по символам!
        // Для посимвольной обработки нужны библиотеки (ICU, Boost.Nowide) или C++20 <text_encoding>.
    }
    return 0;
}

Проблемы и рекомендации для C++:

  1. std::string и std::wstring не знают о кодировке. Они просто хранят последовательности char/wchar_t. Ответственность за корректную интерпретацию лежит на программисте.
  2. Консольный ввод/вывод может некорректно отображать Unicode в зависимости от настроек терминала/консоли.
  3. Для сложных операций (нормализация, разбиение на графемы, сортировка) используйте специализированные библиотеки, такие как International Components for Unicode (ICU).
  4. C++20 вводит новые типы (std::u8string) и улучшенную поддержку текста, но полная библиотека пока не реализована во всех компиляторах.

Правило для современных кроссплатформенных проектов: Храните и обрабатывайте текст внутри программы в UTF-8 (используя std::string), конвертируя в другие кодировки только на границах ввода/вывода (файлы, сеть, UI).