2013-03-20 8 views
18

W podobnych słowach, gdzie oba są wprowadzane do kodu źródłowego z tym samym kodowaniem (UTF-8), a ustawienia regionalne są poprawne, czy jest jakiś praktyczna różnica między nimi?Drukowanie ciągów UTF-8 z literami printf - szeroki kontra wielobajtowy

printf("ο Δικαιοπολις εν αγρω εστιν\n"); 
printf("%ls", L"ο Δικαιοπολις εν αγρω εστιν\n"); 

W związku z tym czy istnieje jakikolwiek powód, aby preferować jeden nad drugim podczas wykonywania wydruków? Wyobrażam sobie, że druga gra trochę gorzej, ale czy ma jakąś przewagę (lub wadę) nad literałem wielobajtowym?

EDYCJA: Nie ma problemów z drukowaniem tych ciągów. Ale nie używam funkcji szerokich ciągów znaków, ponieważ chcę również móc używać printf itd. Pytanie brzmi więc, czy te sposoby drukowania są inne (biorąc pod uwagę sytuację opisaną powyżej), a jeśli tak, to czy druga ma jakąś przewagę?

EDIT2: Po komentarze poniżej, teraz wiem, że ten program działa - co moim zdaniem nie było to możliwe:

int main() 
{ 
    setlocale(LC_ALL, ""); 
    wprintf(L"ο Δικαιοπολις εν αγρω εστιν\n"); // wide output 
    freopen(NULL, "w", stdout);     // lets me switch 
    printf("ο Δικαιοπολις εν αγρω εστιν\n"); // byte output 
} 

Edit3: Zrobiłem kilka dalszych badań przez patrząc na to, co się dzieje z tymi dwoma typami. Prostszy ciąg:

wchar_t *wides = L"£100 π"; 
char *mbs = "£100 π"; 

Kompilator generuje inny kod. Szeroki ciąg jest:

.string "\243" 
.string "" 
.string "" 
.string "1" 
.string "" 
.string "" 
.string "0" 
.string "" 
.string "" 
.string "0" 
.string "" 
.string "" 
.string " " 
.string "" 
.string "" 
.string "\300\003" 
.string "" 
.string "" 
.string "" 
.string "" 
.string "" 

Podczas gdy druga jest:

.string "\302\243100 \317\200" 

i patrząc na kodowania Unicode, drugi to zwykły UTF-8. Szeroka reprezentacja znaków to UTF-32. Rozumiem, że będzie to zależne od wdrożenia.

Czyli szersza reprezentacja znakowa literałów jest bardziej przenośna? Mój system nie będzie bezpośrednio drukować kodowań UTF-16/UTF-32, więc jest automatycznie konwertowany na UTF-8 dla wyjścia.

+0

Mówiłeś oba przykłady są wprowadzane z UTF-8. W drugim wierszu przykładowym, jeśli ten tekst jest w rzeczywistości UTF-8, a nie szerokim kodowaniem, prawdopodobnie nie powinieneś mieć prefiksu L, dlatego użyjesz '% s' zamiast'% ls'. Albo nadal nie rozumiem tego pytania. –

+0

@AdrianMcCarthy - oba ciągi w kodzie źródłowym to UTF-8, tak. Ale literał łańcuchowy jest zawsze wielobajtowy - "Literał ciągu znaków jest sekwencją zero lub więcej znaków wielobajtowych zamkniętych w podwójnych cudzysłowach, jak w" xyz ". Szeroki ciąg literału jest taki sam, z wyjątkiem prefiksowanym literą L. " od normy. – teppic

+0

AFAIR, wszystkie znaki spoza podstawowego zestawu znaków źródłowych (który jest * podzbiorem * US-ASCII-7) wywołują zachowanie zdefiniowane przez implementację, tzn. Wszystko tutaj omówione jest efektywnie zależne od używanego kompilatora. Jeśli naprawdę chcesz grać bezpiecznie (i przenośnie), musiałbyś uciekać się do \ u ... i \ U ... – DevSolar

Odpowiedz

20
printf("ο Δικαιοπολις εν αγρω εστιν\n"); 

drukuje ciąg dosłowne (const char*, znaki specjalne są reprezentowane wielobajtowych znaków). Chociaż możesz zobaczyć poprawne wyniki, istnieją inne problemy, z którymi możesz mieć do czynienia podczas pracy ze znakami spoza ASCII, takimi jak te. Np

char str[] = "αγρω"; 
printf("%d %d\n", sizeof(str), strlen(str)); 

wyjścia 9 8, ponieważ każdy z tych znaków specjalnych jest reprezentowane przez 2 char s.

Podczas korzystania z prefiksu L masz dosłowne składający się z szerokich znaków (const wchar_t*) i %ls Format specifier powoduje te szerokie znaki są konwertowane na wielobajtowych znaków (UTF-8). Zauważ, że w tym przypadku, narodowe powinny być odpowiednio ustawione inaczej ta konwersja może prowadzić do wyjścia jest nieprawidłowy:

#include <stdio.h> 
#include <wchar.h> 
#include <locale.h> 

int main(void) 
{ 
    setlocale(LC_ALL, ""); 
    printf("%ls", L"ο Δικαιοπολις εν αγρω εστιν"); 
    return 0; 
} 

ale podczas gdy niektóre rzeczy mogą się bardziej skomplikowana, gdy pracuje z szerokich znaków, innych rzeczy może się znacznie prostsze i bardziej proste.Na przykład:

wchar_t str[] = L"αγρω"; 
printf("%d %d", sizeof(str)/sizeof(wchar_t), wcslen(str)); 

wyjścia woli 5 4 jak można by oczekiwać, naturalnie.

Gdy zdecydujesz się na pracę z szerokimi ciągami znaków, wprintf może być użyty do bezpośredniego drukowania szerokich znaków . Warto również zauważyć tutaj, że w przypadku konsoli systemu Windows, tryb tłumaczenie stdout należy jawnie ustawić na jednym z trybów Unicode wywołując _setmode:

#include <stdio.h> 
#include <wchar.h> 

#include <io.h> 
#include <fcntl.h> 
#ifndef _O_U16TEXT 
    #define _O_U16TEXT 0x20000 
#endif 

int main() 
{ 
    _setmode(_fileno(stdout), _O_U16TEXT); 
    wprintf(L"%s\n", L"ο Δικαιοπολις εν αγρω εστιν"); 
    return 0; 
} 
+0

To ja :) 'wprintf' również konwertuje na multibajt, ale interesują mnie standardowe funkcje. – teppic

+0

@teppic: Zobacz teraz moją odpowiedź. Powinien być wreszcie bardziej satysfakcjonujący, chyba :) – LihO

+5

UTF-16 jest ** nie ** "szeroki", a szkoda, że ​​ten kawałek mitu wciąż istnieje. Jest więcej niż 2^16 znaków Unicode, a kodowanie UTF-16 z ** zmienną ** szerokości jednego lub dwóch 16-bitowych jednostek kodu. Jeśli chcesz "szeroki", musisz uciekać się do UTF-32. Nie wpadajmy w tę pułapkę myślenia, że ​​'n' bit powinno wystarczyć każdemu, * znowu *. – DevSolar

Powiązane problemy