$ locale charmap
UTF-8
У моєму теперішньому середовищі набір символів - UTF-8, тобто символи кодуються від 1 до 4 байтів на символ (хоча тому, що в оригінальному визначенні UTF-8 дозволений код символу вказує до 0x7fffffff, більшість інструментів розпізнає UTF- 8 послідовностей байт до 6 байт).
У цьому наборі символів доступні всі символи з Unicode, a a
кодується як значення байта 65, a 乕
як 3 байти 228 185 149 і é
, наприклад, два байтові послідовності 195 169.
$ printf 乕 | wc -mc
1 3
$ printf a | wc -mc
1 1
Зараз:
$ export fr_FR.iso885915@euro
$ locale charmap
ISO-8859-15
Я змінив своє середовище, де набір символів тепер ISO-8859-15 (інші речі, такі як мова, символ валюти, формат дати, також були змінені; колекція цих регіональних налаштувань називається локальним ). Мені потрібно запустити новий емулятор терміналу в цьому середовищі, щоб він адаптував його передачу символів до нової локалі.
ISO-8859-15 - це однобайтовий набір символів, що означає, що він містить лише 256 символів (насправді навіть менше, ніж фактично охоплені). Цей набір символів використовується для мов Західної Європи, оскільки він охоплює більшість мов (та символ євро).
Він має a
символ зі значенням байта 65, як у UTF-8 або ASCII, він також має é
символ (як зазвичай використовується у французькій або іспанській мовах), але зі значенням байту 233, він не має символу 乕.
У такому середовищі wc -c
і wc -m
завжди дасть однаковий результат.
У Ubuntu, як і в більшості сучасних Unix-подібних систем, за замовчуванням зазвичай це UTF-8, оскільки це єдиний підтримуваний набір символів (і кодування), який охоплює весь діапазон Unicode.
Інші багатобайтові кодування символів існують, але вони не так добре підтримуються в Ubuntu, і вам доведеться пройти обручі, щоб можна було генерувати локаль з ними, і якщо ви це зробите, ви виявите, що багато речей не працювати належним чином.
Отже, по суті на Ubuntu, набори символів є або однобайтовими, або UTF-8.
Тепер ще кілька приміток:
У UTF-8 не всі послідовності байт утворюють дійсні символи. Наприклад, всі символи UTF-8, які не є ASCII, формуються з байтів, у яких всі 8-й бітовий набір, але де тільки перший має 7-й бітовий набір.
Якщо у вас є послідовність байтів з 8-м бітовим набором, жоден з яких не має 7-й бітовий набір, то це неможливо перекласти символу. І ось тоді у вас виникають проблеми і невідповідності, оскільки програмне забезпечення не знає, що з ними робити. Наприклад:
$ printf '\200\200\200' | wc -mc
0 3
$ printf '\200\200\200' | grep -q . || echo no
no
wc
і grep
не знайдіть там жодного персонажа, але:
$ x=$'\200\200\200' bash -c 'echo "${#x}"'
3
bash
знаходить 3. Коли він не може відобразити послідовність байтів для символу, він розглядає кожен байт символу.
Це може стати ще складнішим, оскільки в Unicode є кодові точки, які є недійсними як символи, а також деякі, які не є символами , і залежно від інструменту їх кодування UTF-8 може або не може розглядатися як символ.
Інша річ, яку слід взяти до уваги, - це різниця між символом та графемою та способом їх відображення.
$ printf 'e\u301\u20dd\n'
é⃝
$ printf 'e\u301\u20dd' | wc -mc
3 6
Там ми кодуємо 3 символи в 6 байтах, відображених як одна графема, тому що у нас є 3 символи разом (один базовий символ, поєднуючий гострий наголос і поєднуюче коло).
Реалізація GNU, wc
як знайдено в Ubuntu, має -L
перемикач, щоб повідомити вам ширину відображення найширшої лінії на вході:
$ printf 'e\u301\u20dd\n' | wc -L
1
Ви також виявите, що деякі символи займають 2 комірки в такому обчисленні ширини, як наш 乕
символ зверху:
$ echo 乕 | wc -L
2
На закінчення: у більш дикому слові байт, символ та графема необов’язково однакові.