2016-02-16 13 views
5

Próbuję sformatować wynik niektórych ciągów wc za pomocą specyfikatora szerokości i funkcji printf. Mam jednak problem z uzyskaniem pożądanego zachowania. Wydaje się, że za każdym razem, gdy printf napotka znak å, ä lub ö, szerokość zarezerwowana dla ciągu zmniejszy się o jedną pozycję.Specyfikator szerokości w printf nie działa poprawnie z akcentowanymi znakami.

Kod-fragment zilustrować:

#include <stdio.h> 

int main(void) 
{ 
    printf(">%-10s<\n", "aoa"); 
    printf(">%-10s<\n", "aäoa"); 
    printf(">%-10s<\n", "aäoöa"); 
    printf(">%-10s<\n", "aäoöaå"); 

    return 0; 
} 

wyjścia w mojego systemu Linux bash powłoki.

>aoa  < 
>aäoa  < 
>aäoöa < 
>aäoöaå < 

Szukam porady, jak sobie z tym poradzić. Co chcę jest dla wszystkich ciągów we fragmencie powyżej do wydrukowania w ciągu 10 char szerokim zakresie przestrzeni wyściełane tak:

>aoa  < 
>aäoa  < 
>aäoöa  < 
>aäoöaå < 

Doceniam również żadnego wglądu, dlaczego tak się dzieje lub komentarz, jeśli nie jest to problem z innymi konfiguracjami.

+0

Czy używasz kodowania UTF-8? Te znaki wymagają 2 bajtów, a 'printf' może nie być znane z UTF-8. – user694733

+1

http://stackoverflow.com/questions/15528359/printing-utf-8-strings-with-printf-wide-vs-multibyte-string-literals – 123

+0

@ user694733 Tak, użyłem utf-8 –

Odpowiedz

4

dlaczego tak się dzieje?

Spójrz na The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets

jako alternatywa dla szerokich znaków i na UTF-8, można użyć tej funkcji, aby policzyć liczbę znaków spoza ASCII, a następnie można dodać wynik do specyfikatora szerokości printf:

#include <stdio.h> 

int func(const char *str) 
{ 
    int len = 0; 

    while (*str != '\0') { 
     if ((*str & 0xc0) == 0x80) { 
      len++; 
     } 
     str++; 
    } 
    return len; 
} 

int main(void) 
{ 
    printf(">%-*s<\n", 10 + func("aoa"), "aoa"); 
    printf(">%-*s<\n", 10 + func("aäoa"), "aäoa"); 
    printf(">%-*s<\n", 10 + func("aäoöa"), "aäoöa"); 
    printf(">%-*s<\n", 10 + func("aäoöaå"), "aäoöaå"); 
    return 0; 
} 

wyjściowa:

>aoa  < 
>aäoa  < 
>aäoöa  < 
>aäoöaå < 
+1

Chociaż sądzę, że używanie wprintf jest bardziej sensowne w dłuższej perspektywie, skończyło się na używaniu Twojej sugestii. Link był złoty. –

6

używać łańcuchów szerokości znaków i wprintf:

#include <cwchar> 
#include <locale.h> 

int main(void) 
{ 
    // seems to be needed for the correct output encoding 
    setlocale(LC_ALL, ""); 

    wprintf(L">%-10ls<\n", L"aoa"); 
    wprintf(L">%-10ls<\n", L"aäoa"); 
    wprintf(L">%-10ls<\n", L"aäoöa"); 
    wprintf(L">%-10ls<\n", L"aäoöaå"); 

    return 0; 
} 
+0

Użyłem literalnych ciągów w tym przykładzie, aby był krótki. W moim rzeczywistym problemie otrzymuję ciągi znaków z struct. Przypuszczam, że musiałbym konwertować te ciągi na szerokie ciągi znaków za pomocą [mbstowcs()] (http://linux.die.net/man/3/mbstowcs) czy coś takiego? Chodzi mi o to, że oczywiście nie mogę zrobić 'wprintf (L">% - 10ls <\n", Lsome-> członek); ' –

2

Alter Mann's accepted answer jest wzdłuż odpowiednich linii, z tym że jeden s nie mógł po prostu kodować funkcji niestandardowej, aby zliczyć liczbę bajtów w ciągu wielobajtowym, które nie kodują widocznego znaku: Kod powinien być zlokalizowany pod numerem setlocale(LC_ALL, "") lub podobnym, a strlen(str) - mbstowcs(NULL, str, 0) zliczać liczbę bajtów w ciągu znaków, które nie zawierają koduj widoczny znak.

setlocale() to standard C (C89, C99, C11), ale również zdefiniowany w POSIX.1. mbstowcs() to standardowe C99 i C11, a także zdefiniowane w POSIX.1. Oba są również implementowane w bibliotekach Microsoft C, więc działają praktycznie wszędzie.

Rozważmy następujący przykład program, który drukuje ciągi C, podane w linii poleceń:

#include <stdlib.h> 
#include <string.h> 
#include <locale.h> 
#include <stdio.h> 

/* Counts the number of (visible) characters in a string */ 
static size_t ms_len(const char *const ms) 
{ 
    if (ms) 
     return mbstowcs(NULL, ms, 0); 
    else 
     return 0; 
} 

/* Number of bytes that do not generate a visible character in a string */ 
static size_t ms_extras(const char *const ms) 
{ 
    if (ms) 
     return strlen(ms) - mbstowcs(NULL, ms, 0); 
    else 
     return 0; 
} 

int main(int argc, char *argv[]) 
{ 
    int arg; 

    /* Default locale */ 
    setlocale(LC_ALL, ""); 

    for (arg = 1; arg < argc; arg++) 
     printf(">%-*s< (%zu bytes; %zu chars; %zu bytes extra in wide chars)\n", 
       (int)(10 + ms_extras(argv[arg])), argv[arg], 
       strlen(argv[arg]), ms_len(argv[arg]), ms_extras(argv[arg])); 

    return EXIT_SUCCESS; 
} 

Jeśli skompilować powyżej example i uruchomieniu

./example aaa aaä aää äää aa€ a€€ €€€ a ä € 

program będzie wyjściowej

>aaa  < (3 bytes; 3 chars; 0 bytes extra in wide chars) 
>aaä  < (4 bytes; 3 chars; 1 bytes extra in wide chars) 
>aää  < (5 bytes; 3 chars; 2 bytes extra in wide chars) 
>äää  < (6 bytes; 3 chars; 3 bytes extra in wide chars) 
>aa€  < (5 bytes; 3 chars; 2 bytes extra in wide chars) 
>a€€  < (7 bytes; 3 chars; 4 bytes extra in wide chars) 
>€€€  < (9 bytes; 3 chars; 6 bytes extra in wide chars) 
>a   < (1 bytes; 1 chars; 0 bytes extra in wide chars) 
>ä   < (2 bytes; 1 chars; 1 bytes extra in wide chars) 
>€   < (3 bytes; 1 chars; 2 bytes extra in wide chars) 
>   < (4 bytes; 1 chars; 3 bytes extra in wide chars) 

Jeśli ostatni < nie nie są zgodne z innymi, ponieważ używana czcionka nie jest dokładnie ustalona - szerokość: emotikona jest szersza niż zwykłe znaki, takie jak Ä, to wszystko. Wini czcionkę.

Ostatnią postacią jest U + 1F608 UŚMIECHNA TWARZ Z TORAMI, z Emoticons unicode block, na wypadek, gdyby system operacyjny/przeglądarka/czcionka nie mogła go wyświetlić. W Linuksie wszystkie powyższe > i < są poprawnie rozmieszczone we wszystkich terminalach, jakie mam, w tym w konsoli (nie graficzna konsola systemowa), chociaż czcionka konsoli nie ma glifu dla emotikonu, a zamiast tego pokazuje go jako diament.

W przeciwieństwie do Alter Mann's answer, podejście to jest przenośne i nie przyjmuje żadnych założeń dotyczących tego, jaki zestaw znaków jest faktycznie używany przez bieżącego użytkownika.

+0

Dobra odpowiedź, masz całkowitą rację co do przenośności, moja funkcja zakłada UTF8. –

Powiązane problemy