Breite Zeichen mit printf anzeigen

Lesezeit: 5 Minuten

Benutzeravatar von vmonteco
vmonteco

Ich versuche zu verstehen, wie printf mit breiten Zeichen funktioniert (wchar_t).

Ich habe die folgenden Codebeispiele erstellt:

Probe 1:

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

int     main(void)
{
    wchar_t     *s;

    s = (wchar_t *)malloc(sizeof(wchar_t) * 2);
    s[0] = 42;
    s[1] = 0;
    printf("%ls\n", s);
    free(s);
    return (0);
}

Ausgang :

*

Hier ist alles in Ordnung : mein Charakter (*) wird korrekt angezeigt.

Probe 2:

Ich wollte eine andere Art von Charakter zeigen. Auf meinem System wchar_t scheinen auf 4 Bytes codiert. Also habe ich versucht, das folgende Zeichen anzuzeigen:
É

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

int     main(void)
{
    wchar_t     *s;

    s = (wchar_t *)malloc(sizeof(wchar_t) * 2);
    s[0] = 0xC389;
    s[1] = 0;
    printf("%ls\n", s);
    free(s);
    return (0);
}

Aber es gibt diesmal keine Ausgabe, ich habe es mit vielen Werten aus dem Abschnitt “Codierung” (vgl. vorherigen Link) versucht s[0] (0xC389, 201, 0xC9) … Aber ich bekomme das nie hin É Zeichen angezeigt. Ich habe es auch mit versucht %S Anstatt von %ls.

Wenn ich versuche, printf so aufzurufen: printf("<%ls>\n", s) das einzige gedruckte Zeichen ist '<'wird die Anzeige abgeschnitten.

Warum habe ich dieses Problem? Wie soll ich es machen?

  • Gibt es einen Grund, warum Sie dynamisch zuweisen, anstatt ein Array aus zwei Elementen zu deklarieren?

    – Irgendein Programmierer-Typ

    14. November 2016 um 13:51 Uhr

  • Versuchen Sie, mit zu lesen scanf("%1ls") a "É" und berichte welchen Wert für printf("%lX\n", (unsigned long) s[0]) du erhältst.

    – chux – Wiedereinsetzung von Monica

    14. November 2016 um 16:47 Uhr


  • @chux printf("%ld\n", (unsigned long int) L'É'); gibt mir 201.

    – vmonteco

    16. November 2016 um 11:49 Uhr

  • Schlagen Sie vor, das Ergebnis von „Lesen mit scanf("%1ls") ein “É”. Ihr Kommentar gibt an, was der Quellcode für ein ‘É’ hält. Uns interessiert, wie der Code mit der I/O umgeht, die sich in der Zeichenkodierung unterscheiden kann.

    – chux – Wiedereinsetzung von Monica

    16. November 2016 um 15:02 Uhr


  • Auf meinem System ist der Rückgabewert von scanf("%1ls", s); ist -1 (s[0] nicht festgelegt), die stackoverflow.com/a/40600658/2410359 unterstützt

    – chux – Wiedereinsetzung von Monica

    16. November 2016 um 15:09 Uhr

Benutzeravatar von Tim
Tim

Warum habe ich dieses Problem?

Stellen Sie sicher, dass Sie dies überprüfen errno und der Rückgabewert von printf!

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

int main(void)
{
    wchar_t *s;
    s = (wchar_t *) malloc(sizeof(wchar_t) * 2);
    s[0] = 0xC389;
    s[1] = 0;

    if (printf("%ls\n", s) < 0) {
        perror("printf");
    }

    free(s);
    return (0);
}

Siehe die Ausgabe:

$ gcc test.c && ./a.out
printf: Invalid or incomplete multibyte or wide character

Wie repariert man

Zunächst einmal ist das Standardgebietsschema eines C-Programms C (auch bekannt als POSIX), die nur ASCII ist. Sie müssen einen Anruf hinzufügen setlocalespeziell setlocale(LC_ALL,"").

Wenn dein LC_ALL, LC_CTYPE oder LANG Umgebungsvariablen sind nicht so eingestellt, dass sie UTF-8 zulassen, wenn sie leer sind, müssen Sie explizit ein Gebietsschema auswählen. setlocale(LC_ALL, "C.UTF-8") funktioniert auf den meisten Systemen – C ist Standard, und die UTF-8 Teilmenge von C wird allgemein umgesetzt.

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

int main(void)
{
    wchar_t *s;
    s = (wchar_t *) malloc(sizeof(wchar_t) * 2);
    s[0] = 0xC389;
    s[1] = 0;

    setlocale(LC_ALL, "");

    if (printf("%ls\n", s) < 0) {
        perror("printf");
    }

    free(s);
    return (0);
}

Siehe die Ausgabe:

$ gcc test.c && ./a.out
쎉

Der Grund, warum das falsche Zeichen ausgedruckt wird, ist weil wchar_t steht für ein Breitzeichen (z. B. UTF-32), nicht für ein Multibyte-Zeichen (z. B. UTF-8). Beachten Sie, dass wchar_t ist in der GNU-C-Bibliothek immer 32 Bit breit, aber der C-Standard verlangt dies nicht. Wenn Sie das Zeichen mit der initialisieren UTF-32BE codieren (bzw 0x000000C9), dann wird es korrekt ausgedruckt:

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

int main(void)
{
    wchar_t *s;
    s = (wchar_t *) malloc(sizeof(wchar_t) * 2);
    s[0] = 0xC9;
    s[1] = 0;

    setlocale(LC_ALL, "");

    if (printf("%ls\n", s) < 0) {
        perror("printf");
    }

    free(s);
    return (0);
}

Ausgabe:

$ gcc test.c && ./a.out
É

Beachten Sie, dass Sie auch die festlegen können LC (lokale) Umgebungsvariablen über die Kommandozeile:

$ LC_ALL=C.UTF-8
$ ./a.out
É

Ein Problem besteht darin, dass Sie versuchen, UTF-8, bei dem es sich um ein Einzelbyte-Codierungsschema handelt, als Multibyte-Codierung zu codieren. Für UTF-8 verwenden Sie plain char.

Beachten Sie auch, dass Sie, weil Sie versuchen, die UTF-8-Sequenz in einen Multi-Byte-Typ zu kombinieren Endianität (Byte-Reihenfolge) Probleme (in memory 0xC389 könnte gespeichert werden als 0x89 und 0xC3in dieser Reihenfolge). Und dass der Compiler auch Ihre Nummer vorzeichenerweitert (falls sizeof(wchar_t) == 4 und du schaust zu s[0] in einem Debugger könnte es sein 0xFFFFC389).

Ein weiteres Problem ist das Terminal oder die Konsole, die Sie zum Drucken verwenden. Vielleicht unterstützt es einfach nicht UTF-8 oder die anderen Codierungen, die Sie ausprobiert haben?

Ich habe einen einfachen Weg gefunden, breite Zeichen zu drucken. Ein wichtiger Punkt ist setlocale()

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

int main(int argc, char *argv[])
{
    setlocale(LC_ALL, "");
    // setlocale(LC_ALL, "C.UTF-8"); // this also works

    wchar_t hello_eng[] = L"Hello World!";
    wchar_t hello_china[] = L"世界, 你好!";
    wchar_t *hello_japan = L"こんにちは日本!";
    printf("%ls\n", hello_eng);
    printf("%ls\n", hello_china);
    printf("%ls\n", hello_japan);

    return 0;
}

1432720cookie-checkBreite Zeichen mit printf anzeigen

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy