Статья 7. Unicode на практике: Linux, инструменты, исходники ICU¶
1. Unicode-инструменты в Linux¶
uniutils — пакет специализированных утилит¶
Доступность
Пакет uniutils есть в репозиториях Ubuntu/Debian, но отсутствует в Parrot OS, Kali и некоторых других дистрибутивах. Если sudo apt install uniutils выдаёт Unable to locate package — используйте Python-альтернативу ниже.
uniname — вывести Unicode-имена байт/символов файла или stdin:
echo "Привет" | uniname
# Offset Name Code Point UTF-8
# 0 CYRILLIC CAPITAL LETTER PE U+041F D0 9F
# 2 CYRILLIC SMALL LETTER R U+0440 D1 80
# ...
unidesc — описание символов (категория, скрипт):
unicode — интерактивный инструмент поиска¶
# Поиск по имени
unicode acute
unicode "latin small"
# Информация по кодовой точке
unicode U+1F30D
unicode 0x1F30D
# Все символы из блока
unicode --block=Cyrillic | head -30
hexdump / xxd — анализ байтов файла¶
# Посмотреть байты UTF-8
echo "Мир 🌍" | hexdump -C
# 00000000 d0 9c d0 b8 d1 80 20 f0 9f 8c 8d 0a |...... ...|
echo "Мир 🌍" | xxd
# 00000000: d09c d0b8 d180 20f0 9f8c 8d0a ...... .....
# xxd в обратную сторону: hex → binary
echo "48656c6c6f" | xxd -r -p
# Hello
iconv — конвертация кодировок¶
# Список поддерживаемых кодировок
iconv -l | grep -iE 'utf|koi|cp125|win'
# Конвертация файла
iconv -f windows-1251 -t utf-8 input.txt > output.txt
iconv -f koi8-r -t utf-8 old_file.txt > new_file.txt
# Конвертация с игнорированием ошибок
iconv -f latin1 -t utf-8 -c input.txt > output.txt
# Обнаружение кодировки (через file)
file -i document.txt
# document.txt: text/plain; charset=utf-8
Локали и glibc¶
# Текущая локаль
locale
# LANG=ru_RU.UTF-8
# LC_COLLATE=ru_RU.UTF-8
# ...
# Все доступные локали
locale -a | grep -i ru
# Где хранятся файлы локалей
ls /usr/share/i18n/locales/ | grep ru
# Файл локали: /usr/share/i18n/locales/ru_RU
# Правила коллации: /usr/share/locale/ru_RU.UTF-8/LC_COLLATE
# Пересборка локалей
sudo locale-gen ru_RU.UTF-8
sudo update-locale LANG=ru_RU.UTF-8
# Сортировка с учётом локали через sort
echo -e "ёж\nЕль\nелка" | LC_ALL=ru_RU.UTF-8 sort
Python в командной строке для Unicode-анализа¶
# Версия Unicode
python3 -c "import unicodedata; print(unicodedata.unidata_version)"
# Имя символа
python3 -c "import unicodedata; print(unicodedata.name('🌍'))"
# Категория
python3 -c "import unicodedata; print(unicodedata.category('А'))"
# Hex-дамп строки
python3 -c "s='Привет'; print(s.encode('utf-8').hex())"
# Байты → строка
python3 -c "print(bytes.fromhex('d09fd180d0b8d0b2d0b5d182').decode('utf-8'))"
2. Структура исходников ICU¶
Репозиторий: https://github.com/unicode-org/icu
icu/
├── icu4c/ # C++ реализация (главная)
│ ├── source/
│ │ ├── common/ # Базовые операции: UTF-8/16, нормализация, свойства
│ │ │ ├── unicode/
│ │ │ │ ├── unistr.h # UnicodeString класс
│ │ │ │ ├── normalizer2.h # Нормализация API
│ │ │ │ ├── uchar.h # Свойства символов (u_charType, u_charName…)
│ │ │ │ └── uscript.h # Скрипты
│ │ │ ├── normalizer2.cpp # Реализация NFC/NFD/NFKC/NFKD
│ │ │ ├── uniset.cpp # UnicodeSet (\p{} паттерны)
│ │ │ └── uchar.cpp # u_charType и др.
│ │ ├── i18n/ # Locale-sensitive: коллация, форматирование
│ │ │ ├── ucol.cpp # Collation API (ucol_open, ucol_strcoll…)
│ │ │ ├── collation.h # Типы весов: CollationData, DUCET
│ │ │ ├── collationiterator.cpp # Итерация для получения весов
│ │ │ ├── collationkeys.cpp # Построение sort keys
│ │ │ ├── rulebasedcollator.cpp # Tailoring
│ │ │ └── regexcmp.cpp # Unicode regex engine
│ │ ├── data/ # Данные Unicode (скомпилированные из CLDR + UCD)
│ │ │ ├── coll/
│ │ │ │ ├── root.txt # DUCET (корневые правила)
│ │ │ │ ├── ru.txt # Русские правила коллации
│ │ │ │ └── de.txt # Немецкие правила
│ │ │ └── misc/
│ │ │ └── icuver.txt # Версия ICU и Unicode
│ │ └── test/ # Тесты (полезны как примеры)
│ └── include/
└── icu4j/ # Java реализация
Как читать правила коллации в ICU¶
Файл icu4c/source/data/coll/root.txt — это DUCET в формате ICU resource bundle:
root{
RuleBasedCollator{
Version{ "56.0" }
Sequence{
"[normalization on]"
"[caseLevel off]"
"&[before 1] [first regular] < \u0660 = \u06F0" // Arabic digits
"& b < c, C" // b < c, c = C (на одном уровне, разный регистр)
}
}
}
Русский файл ru.txt добавляет правила для ё/е:
ru {
%%CollationElements {
Sequence {
"& \u0415 << \u0401" // Е << Ё (на вторичном уровне)
"& \u0435 << \u0451" // е << ё
}
}
}
Ключевые функции в ucol.cpp¶
// Открыть коллатор для локали
UCollator* ucol_open(const char* loc, UErrorCode* status);
// Сравнить две строки
UCollationResult ucol_strcoll(const UCollator* coll,
const UChar* source, int32_t sourceLength,
const UChar* target, int32_t targetLength);
// Получить sort key (для кэширования)
int32_t ucol_getSortKey(const UCollator* coll,
const UChar* source, int32_t sourceLength,
uint8_t* result, int32_t resultLength);
3. Python regex модуль vs стандартный re¶
Стандартный re поддерживает только базовые Unicode-классы (\w, \d). Модуль regex (pip install regex) поддерживает полные Unicode-свойства.
Сравнение возможностей¶
| Возможность | re | regex |
|---|---|---|
\p{Script=Cyrillic} | ✗ | ✓ |
\p{Letter} / \p{L} | ✗ | ✓ |
\p{Block=CJK_Unified_Ideographs} | ✗ | ✓ |
\p{Emoji} | ✗ | ✓ |
Grapheme cluster \X | ✗ | ✓ |
| Fullcase matching | ✗ | ✓ |
Atomic groups (?>...) | ✗ | ✓ |
Possessive quantifiers ++ | ✗ | ✓ |
Синтаксис свойств символов¶
import regex
# По скрипту
regex.findall(r'\p{Script=Cyrillic}+', text)
regex.findall(r'\p{Cyrillic}+', text) # сокращённо
# По категории
regex.findall(r'\p{Lu}+', text) # uppercase letters
regex.findall(r'\p{Letter}+', text) # все буквы
regex.findall(r'\p{L}+', text) # то же, сокращённо
# По блоку
regex.findall(r'\p{Block=Emoticons}', text)
# По свойству
regex.findall(r'\p{Emoji}+', text)
regex.findall(r'\p{White_Space}+', text)
# Графемные кластеры
regex.findall(r'\X', text) # каждый кластер = один элемент
4. Анализ Unicode-состава текста на Python¶
Несколько строк для базовой статистики — категории, скрипты, эмодзи:
import unicodedata
from collections import Counter
text = "Hello Привет مرحبا 🌍🎉 café 中文"
# Подсчёт по категориям General Category
cats = Counter(unicodedata.category(c) for c in text if c != ' ')
for cat, n in sorted(cats.items(), key=lambda x: -x[1]):
print(f" {cat}: {n}")
import regex # pip install regex
text = "Hello Привет مرحبا 🌍🎉"
print(regex.findall(r'\p{Script=Latin}+', text)) # ['Hello']
print(regex.findall(r'\p{Script=Cyrillic}+', text)) # ['Привет']
print(regex.findall(r'\p{Script=Arabic}+', text)) # ['مرحبا']
print(regex.findall(r'\p{Emoji_Presentation}', text)) # ['🌍', '🎉']
print(regex.findall(r'\X', '👨👩👧')) # ['👨\u200d👩\u200d👧'] — 1 кластер
5. Полезные однострочники для отладки Unicode¶
# Найти не-ASCII символы в файле
grep -P '[^\x00-\x7F]' file.txt
# Найти файлы с BOM
grep -rl $'\xef\xbb\xbf' .
# Конвертировать NFD-имена файлов в NFC (macOS → Linux)
python3 -c "
import os, unicodedata
for f in os.listdir('.'):
nfc = unicodedata.normalize('NFC', f)
if f != nfc:
os.rename(f, nfc)
print(f'renamed: {f!r} → {nfc!r}')
"
# Показать hex Unicode для строки
python3 -c "
s = input('Введите строку: ')
for ch in s:
print(f'U+{ord(ch):04X} = {ch!r}')
"
# Статистика символов файла по категориям
python3 -c "
import sys, unicodedata
from collections import Counter
text = open(sys.argv[1]).read()
cats = Counter(unicodedata.category(c) for c in text)
for cat, n in sorted(cats.items()):
print(f'{cat}: {n}')
" myfile.txt