← Назад к вопросам

Что такое Unicode?

2.0 Middle🔥 111 комментариев
#Язык C++

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое Unicode

Unicode — это универсальный стандарт кодирования символов, который позволяет представлять практически все символы всех языков мира в едином формате.

История и мотивация

До Unicode существовал ASCII (American Standard Code for Information Interchange) — 127 символов, в основном английских букв и спецсимволов. Когда появилась потребность работать с другими языками (русский, китайский, арабский), начали создавать региональные кодировки:

  • KOI8-R для русского
  • Shift-JIS для японского
  • GB2312 для китайского

Проблема: один документ не может содержать русский и китайский текст одновременно.

Решение: Unicode — один стандарт для всех.

Основные концепции

1. Code Point (Позиция)

Каждому символу присваивается уникальный номер (code point) — от U+0000 до U+10FFFF.

A → U+0041
Z → U+005A
а → U+0430  (русское)
中 → U+4E2D  (китайское)
🎉 → U+1F389  (emoji)

2. Encodings (Кодировки)

Code points нужно кодировать в байты для хранения. Есть три главных кодировки:

UTF-8 (рекомендуется для backend)

Переменная длина: 1-4 байта на символ
ДиапазонБайтыФормат
U+0000-U+007F10xxxxxxx
U+0080-U+07FF2110xxxxx 10xxxxxx
U+0800-U+FFFF31110xxxx 10xxxxxx 10xxxxxx
U+10000-U+10FFFF411110xxx 10xxxxxx 10xxxxxx 10xxxxxx

Примеры кодирования:

A (U+0041)      → 01000001 (1 байт)
Ђ (U+0402)      → 11010000 10000010 (2 байта)
中 (U+4E2D)     → 11100100 10111000 10101101 (3 байта)
🎉 (U+1F389)    → 11110000 10011111 10001110 10001001 (4 байта)

Преимущества UTF-8:

  • ASCII символы занимают 1 байт (обратная совместимость)
  • Самоопределяется (легко синхронизироваться в потоке)
  • Самый экономный для англоязычного контента
  • Стандарт de facto в интернете и backends

C++ пример:

#include <iostream>
#include <string>

int main() {
    std::string hello_world = "Hello Мир 世界";
    // UTF-8 строка
    std::cout << hello_world << std::endl;
    
    // Количество байт (не символов!)
    std::cout << "Bytes: " << hello_world.size() << std::endl;  // > 11
    
    return 0;
}

UTF-16 (используется в Windows, Java)

Постоянная/переменная длина: 2 или 4 байта

Характеристики:

  • Более компактна для не-ASCII символов
  • BOM (Byte Order Mark) для определения endianness
  • Варианты: UTF-16LE, UTF-16BE
  • Используется в Windows API, Java, C#

C++ пример:

#include <iostream>
#include <codecvt>

int main() {
    std::wstring wide_str = L"Hello Мир";
    // Размер в широких символах
    std::cout << "Wide chars: " << wide_str.size() << std::endl;
    
    return 0;
}

UTF-32 (редко используется)

Фиксированная длина: 4 байта на символ

Преимущества:

  • Легко индексировать (каждый символ — 4 байта)
  • Простая логика обработки

Недостатки:

  • Неэкономная для хранения
  • Занимает много места
  • Используется редко

Различие: Символ vs Byte vs Code Point

#include <string>
#include <iostream>

int main() {
    std::string utf8 = "Привет";  // Русский текст
    
    // strlen или .size() — количество БАЙТ
    std::cout << "Bytes: " << utf8.size() << std::endl;        // 12
    
    // Подсчёт символов сложнее
    int chars = 0;
    for (char c : utf8) {
        // Считаем только стартовые байты UTF-8
        if ((c & 0xC0) != 0x80) chars++;
    }
    std::cout << "Characters: " << chars << std::endl;          // 6
    
    return 0;
}

Комбинирующие символы (Combining Characters)

Некоторые символы состоят из базового символа + диакритических знаков:

é → e (U+0065) + ◌́ (U+0301)

Это усложняет подсчёт символов:

// "é" может быть:
// 1. U+00E9 (один code point)
// 2. U+0065 + U+0301 (два code points)

// Для правильного подсчета используй графемные кластеры

Проблемы с Unicode в C++

1. UTF-8 в Windows консоли

// Windows консоль по умолчанию не поддерживает UTF-8
// Нужно явно переключить кодовую страницу
#include <windows.h>
SetConsoleCP(CP_UTF8);
SetConsoleOutputCP(CP_UTF8);

2. Сравнение и сортировка

// Байтовое сравнение != лингвистическое
std::string s1 = "ä";
std::string s2 = "a";
s1 < s2;  // Зависит от кодировки, может быть неправильно

// Для правильной сортировки нужна локализация

3. Регулярные выражения

#include <regex>

std::string text = "Привет123мир";
std::regex r("[a-z]+");  // Не найдёт русские буквы
// Нужна поддержка Unicode в regex библиотеке

Best Practices для backend разработки

1. Всегда используй UTF-8

// Явно указывай кодировку
const std::string config = u8"config.json";  // u8 префикс для UTF-8

2. Хранение в базах данных

-- PostgreSQL
CREATE TABLE users (
    name VARCHAR(255) CHARACTER SET utf8mb4  -- UTF-8 для 4-байт символов
);

3. API endpoints

// FastAPI, REST
// Всегда отправляй
Content-Type: application/json; charset=utf-8

4. Логирование

#include "spdlog/spdlog.h"

spdlog::info("User: {}", user_name);  // spdlog поддерживает UTF-8

5. Валидация

bool is_valid_utf8(const std::string& str) {
    const unsigned char* bytes = (const unsigned char*)str.c_str();
    
    while (*bytes) {
        if ((*bytes & 0x80) == 0) {
            bytes += 1;  // ASCII
        } else if ((*bytes & 0xE0) == 0xC0) {
            bytes += 2;  // 2-byte
        } else if ((*bytes & 0xF0) == 0xE0) {
            bytes += 3;  // 3-byte
        } else if ((*bytes & 0xF8) == 0xF0) {
            bytes += 4;  // 4-byte
        } else {
            return false;  // Невалидный UTF-8
        }
    }
    return true;
}

Итоговые выводы

  • Unicode — универсальный стандарт для всех символов мира
  • UTF-8 — рекомендуемая кодировка для backend (web, APIs, databases)
  • Code point — номер символа (U+0041 для 'A')
  • Byte — физическое хранилище (UTF-8 использует 1-4 байта)
  • Backend разработчик должен понимать: UTF-8, BOM, нормализацию, валидацию
  • Всегда явно указывай кодировку в API, базах, логах