2012-01-11 14 views
8

W C11 dodano nowy ciąg literału o przedrostku u8. Zwraca tablicę znaków z tekstem zakodowanym do UTF-8. Jak to w ogóle jest możliwe? Czy nie jest podpisany normalny char? Czy oznacza to, że ma nieco mniej informacji do użycia z powodu znaku bitowego? Moja logika przedstawiałaby, że ciąg tekstu UTF-8 musiałby być tablicą niepodpisanych znaków.W jaki sposób char [] może reprezentować ciąg znaków UTF-8?

+0

UTF-8 reprezentuje znaki używające więcej niż 8 bitów (co zawsze mnie myli, ponieważ UTF-16 ma 16 bitów). Ponadto, znak jest tylko grupą bitów, więc być może jest podpisany lub nie ma znaczenia, jeśli myślisz o wartości jako liczbie. Jeśli myślisz o tym, że jest (częścią) reprezentacji symbolu utf-8, to czy kompilator myśli, że obszar w pamięci reprezentuje liczbę podpisaną lub niepodpisaną, nie ma znaczenia. (To nie jest odpowiedź, tylko jak moja logika to interpretuje.) – Oliver

+0

@Oliver Co jest częścią, która Cię myli? UTFf-8 jest tak samo 8-bitowy, jak UTF-16 ma 16 bitów. –

+0

Znaki @MrLister UTF-16 zajmują 1 lub 2 bajty pamięci. Znaki UTF-8 mogą zajmować dowolną liczbę pamięci (zwykle od 1 do 6 bajtów). Moim zdaniem, "UTF-8" byłby 8-bitowym kodowaniem podobnym do ASCII. Prawdziwy UTF-8 byłby lepszy pod nazwą UTF-48 lub podobnym. A przynajmniej myślę, że tak to działa. Nigdy naprawdę nie rozumiałem kodowania znaków o zmiennej szerokości, kiedy robiłem C kilka lat temu, a teraz pracuję w szczęśliwszych językach, gdzie to naprawdę nie jest problemem ... – Oliver

Odpowiedz

5

nie jest normalną char podpisane?

Jest to zależne od tego, czy realizacja char jest signed lub unsigned.

Ponadto bit znaku nie jest "zagubiony", ale może być nadal używany do reprezentowania informacji, a char niekoniecznie ma rozmiar 8 bitów (może być większy na niektórych platformach).

+0

* "może być również bez znaku" * ... ale nie w tym samym czasie :-) –

+0

Standard mówi, że 'char' ma zawsze długość 1 bajta. Rozmiar bajtu może się jednak różnić. Użyj 'CHAR_BIT' (z' limits.h'), aby poznać faktyczny rozmiar 1 bajta. – jweyrich

1

No, trochę znak jest nieco mimo wszystko! A sama specyfikacja UTF-8 nie mówi, że znaki muszą być niepodpisane.

PS Wat voor kookwekker „n Naam?

6

Istnieje potencjalny problem tutaj:

Jeśli implementacja z CHAR_BIT == 8 wykorzystuje logowania wielkości reprezentację char (tak char jest podpisany), a następnie, gdy UTF-8 wymaga Bit-wzór 10000000, że to negatywne 0. Więc jeśli realizacja dalej nie obsługuje negatywny 0, wówczas dany ciąg znaków UTF-8 może zawierać nieprawidłową wartość (trap) z char, co jest problematyczne. Nawet jeśli to nie obsługuje negatywny zera, fakt, że nieco wzór 10000000 porównuje równa jako char do bitów 00000000 (the nul terminator) mogą spowodować problemy przy użyciu UTF-8 danych w char[].

Myślę, że oznacza to, że w przypadku implementacji C11 o wielkości znaku, char musi być niepodpisane. Zwykle jest to do wykonania, czy char jest podpisany lub niepodpisane, ale oczywiście jeśli char podpisaniu wyników w nie wdraża UTF-8 literały prawidłowo wówczas realizator musi tylko wybrać niepodpisany. Na marginesie, tak było w przypadku implementacji uzupełnień C++ w wersjach innych niż 2, ponieważ C++ pozwala na użycie char, a także unsigned char do uzyskiwania dostępu do reprezentacji obiektów. C tylko zezwala na unsigned char.

w 2 za uzupełnienie i dopełnienie 1S', wzorce bitowe wymagane dla UTF-8 dane są prawidłowe wartości signed char, więc realizacja jest wolny, aby char albo znakiem lub bez znaku i nadal być w stanie reprezentować UTF-8 ciągi w char[]. To dlatego, że wszystkie 256-bitowe wzorce są poprawnymi wartościami dopełniacza 2, a UTF-8 nie korzysta z bajtu 11111111 (1s 'uzupełnienie ujemnego zera).

+0

Twój post używa niewłaściwego założenia, a mianowicie, że implementacje byłyby wystarczająco głupie, aby umożliwić podanie wartości -0 dla znaków. Nigdy nie są. –

+2

@Mr Lister: Nie sądzę, że moja odpowiedź ma jakiekolwiek przypuszczenie co do tego, jakie implementacje faktycznie mają miejsce. Wylicza jedynie to, czego (nie) wolno robić, a zwłaszcza jedną reprezentację, która została niedawno wyklu czona przez wymóg w C11 do obsługi UTF-8. Ze względów praktycznych każda implementacja jest uzupełnieniem 2, ale standard nadal dopuszcza (głupie) alternatywy. –

+0

Myślę, że twój wpis jest bardzo wnikliwy, ale tutaj jest miejsce, w którym jestem zdezorientowany: Standard C++ 11 zezwala na użycie 'unsigned char' i' char' do aliasingu (patrz §3.10/15), a C11 nawet pozwala na * wszystko * typy znaków (patrz §6.5/7). Dla mnie oznacza to, że typy te muszą być w stanie odczytać bajt o wartości "11111111" (lub * dowolną * inną wartość bajtu). W języku C++ 11 można to rozwiązać, tworząc zwykły znak 'char' unsigned *, jeśli * 2's nie jest używane. Ale w C11 nie można tego nigdy rozstrzygnąć, jeśli uzupełnienie 2 * nie jest * używane, ponieważ aliasing musi działać z wszystkimi typami znaków (§6.5/7), to znaczy nawet jawnie ... – JohnCand

1

Podpisanie znaku nie ma znaczenia; utf8 może być obsługiwane tylko z operacjami shift i maskowania (które mogą być uciążliwe dla typów podpisanych, ale nie niemożliwe) Ale: utf8 potrzebuje co najmniej 8 bitów, więc "assert (CHAR_BIT> = 8);"

Aby zilustrować według punktu: poniższe fragmenty nie zawierają żadnych operacji arytmetycznych na wartości znaku, tylko przesunięcie maski &.

static int eat_utf8(unsigned char *str, unsigned len, unsigned *target) 
{ 
unsigned val = 0; 
unsigned todo; 

if (!len) return 0; 

val = str[0]; 
if ((val & 0x80) == 0x00) { if (target) *target = val; return 1; } 
else if ((val & 0xe0) == 0xc0) { val &= 0x1f; todo = 1; } 
else if ((val & 0xf0) == 0xe0) { val &= 0x0f; todo = 2; } 
else if ((val & 0xf8) == 0xf0) { val &= 0x07; todo = 3; } 
else if ((val & 0xfc) == 0xf8) { val &= 0x03; todo = 4; } 
else if ((val & 0xfe) == 0xfc) { val &= 0x01; todo = 5; } 
else { /* Default (Not in the spec) */ 
     if (target) *target = val; 
     return -1; } 


len--;str++; 
if (todo > len) { return -todo; } 

for(len=todo;todo--;) { 
     /* For validity checking we should also 
     ** test if ((*str & 0xc0) == 0x80) here */ 
     val <<= 6; 
     val |= *str++ & 0x3f; 
     } 

if (target) *target = val; 
return 1+ len; 
} 
+1

Należy pamiętać, że Standard _guarantees_ 'CHAR_BIT' ≥ 8. –

Powiązane problemy