$ locale charmap
UTF-8
W moim obecnym środowisku zestaw znaków to UTF-8, co oznacza, że znaki są kodowane od 1 do 4 bajtów na znak (chociaż ponieważ oryginalna definicja UTF-8 dopuszczała punkty kodowe do 0x7fffffff, większość narzędzi rozpoznaje UTF- 8 bajtów sekwencji do 6 bajtów).
W tym zestawie znaków wszystkie znaki z Unicode są dostępne, a a
jest kodowany jako wartość bajtu 65, a 乕
jako 3 bajty 228 185 149 i na przykład é
jako sekwencja dwóch bajtów 195 169.
$ printf 乕 | wc -mc
1 3
$ printf a | wc -mc
1 1
Teraz:
$ export fr_FR.iso885915@euro
$ locale charmap
ISO-8859-15
Zmodyfikowałem swoje środowisko, w którym zestaw znaków to teraz ISO-8859-15 (inne rzeczy, takie jak język, symbol waluty, format daty zostały również zmodyfikowane, a zbiór ustawień regionalnych nazywany jest ustawieniami regionalnymi ). Muszę uruchomić nowy emulator terminala w tym środowisku, aby dostosować renderowanie znaków do nowych ustawień regionalnych.
ISO-8859-15 jest jednobajtowym zestawem znaków, co oznacza, że ma tylko 256 znaków (w rzeczywistości nawet mniej niż te, które są rzeczywiście objęte). Ten szczególny zestaw znaków jest używany w językach Europy Zachodniej, ponieważ obejmuje większość jego języków (i symbol euro).
Ma a
znak o wartości bajtu 65, jak w UTF-8 lub ASCII, ma również é
znak (jak powszechnie używany na przykład w języku francuskim lub hiszpańskim), ale o wartości bajtu 233 nie ma znaku 乕.
W takim środowisku wc -c
i wc -m
zawsze da taki sam rezultat.
W Ubuntu, podobnie jak w większości współczesnych systemów uniksowych, domyślnie jest to UTF-8, ponieważ jest to jedyny obsługiwany zestaw znaków (i kodowanie) obejmujący cały zakres Unicode.
Istnieją inne wielobajtowe kodowania znaków, ale nie są one tak dobrze obsługiwane w Ubuntu i musisz przejść przez obręcze, aby móc wygenerować ustawienia narodowe za ich pomocą, a jeśli to zrobisz, przekonasz się, że wiele rzeczy nie działa poprawnie.
Tak więc w Ubuntu zestawy znaków są jednobajtowe lub UTF-8.
Teraz jeszcze kilka notatek:
W UTF-8 nie wszystkie sekwencje bajtów tworzą prawidłowe znaki. Na przykład wszystkie znaki UTF-8, które nie są znakami ASCII, są tworzone z bajtów, które mają ustawiony 8. bit, ale tylko pierwszy z nich ma ustawiony 7. bit.
Jeśli masz sekwencję bajtów z 8. bitowym zestawem, z których żaden nie ma 7. bitowego zestawu, to nie można go przetłumaczyć na znak. I wtedy zaczynają się problemy i niespójności, ponieważ oprogramowanie nie wie, co z nimi zrobić. Na przykład:
$ printf '\200\200\200' | wc -mc
0 3
$ printf '\200\200\200' | grep -q . || echo no
no
wc
i grep
nie znajdź tam żadnej postaci, ale:
$ x=$'\200\200\200' bash -c 'echo "${#x}"'
3
bash
znajduje 3. Gdy nie może odwzorować sekwencji bajtów na znak, uważa każdy bajt za znak.
Może się jeszcze bardziej skomplikować, ponieważ w Unicode występują punkty kodowe, które są niepoprawne jako znaki, a niektóre, które nie są znakami , i w zależności od narzędzia ich kodowanie UTF-8 może, ale nie musi, być traktowane jako znak.
Inną rzeczą, którą należy wziąć pod uwagę, jest różnica między charakterem a grafhem oraz sposób ich renderowania.
$ printf 'e\u301\u20dd\n'
é⃝
$ printf 'e\u301\u20dd' | wc -mc
3 6
Tam mamy 3 znaki jako 6 bajtów renderowane jako jeden grafhem, ponieważ mamy 3 znaki połączone razem (jeden znak podstawowy, łączący ostry akcent i łączący się okrąg).
Implementacja GNU, wc
taka jak w Ubuntu, ma -L
przełącznik informujący o szerokości wyświetlania najszerszej linii na wejściu:
$ printf 'e\u301\u20dd\n' | wc -L
1
Przekonasz się również, że niektóre postacie zajmują 2 komórki w tym obliczeniu szerokości, jak nasza 乕
postać z góry:
$ echo 乕 | wc -L
2
Podsumowując: bardziej zwięźle, bajt, charakter i grafhem niekoniecznie są takie same.