web-dev-qa-db-fra.com

Impression de chaînes UTF-8 avec printf - littéraux de chaîne larges ou multioctets

Dans des déclarations comme celles-ci, où les deux sont entrées dans le code source avec le même codage (UTF-8) et les paramètres régionaux sont correctement configurés, y a-t-il une différence pratique entre eux?

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

Et par conséquent, y a-t-il une raison de préférer l'un à l'autre lors de la sortie? J'imagine que le second fonctionne un peu moins bien, mais a-t-il un avantage (ou un inconvénient) sur un littéral multi-octets?

EDIT: Il n'y a aucun problème avec l'impression de ces chaînes. Mais je n'utilise pas les fonctions de chaîne large, car je veux pouvoir également utiliser printf etc. La question est donc de savoir si ces façons d'imprimer sont différentes (compte tenu de la situation décrite ci-dessus), et si oui, la seconde a-t-elle un avantage?

EDIT2: Suite aux commentaires ci-dessous, je sais maintenant que ce programme fonctionne - ce que je pensais pas possible:

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

EDIT: J'ai fait d'autres recherches en regardant ce qui se passe avec les deux types. Prenez une chaîne plus simple:

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

Le compilateur génère un code différent. La chaîne large est:

.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 ""

Alors que le second est:

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

Et en regardant les encodages Unicode, le second est du simple UTF-8. La représentation large des caractères est UTF-32. Je me rends compte que cela dépendra de la mise en œuvre.

Alors peut-être que la représentation large des caractères des littéraux est plus portable? Mon système n'imprimera pas directement les encodages UTF-16/UTF-32, il est donc automatiquement converti en UTF-8 pour la sortie.

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

affiche la chaîne littérale (const char*, les caractères spéciaux sont représentés par caractères multi-octets ). Bien que vous puissiez voir la sortie correcte, il existe d'autres problèmes que vous pourriez rencontrer en travaillant avec des caractères non ASCII comme ceux-ci. Par exemple:

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

les sorties 9 8, puisque chacun de ces caractères spéciaux est représenté par 2 chars.

Lorsque vous utilisez le préfixe L, vous avez le littéral composé de caractères larges (const wchar_t*) et %ls le spécificateur de format entraîne la conversion de ces caractères larges en caractères multi-octets (UTF-8). Notez que dans ce cas, les paramètres régionaux doivent être définis de manière appropriée, sinon cette conversion peut entraîner la sortie non valide:

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

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

mais alors que certaines choses peuvent devenir plus compliquées lorsque vous travaillez avec des personnages larges, d'autres peuvent devenir beaucoup plus simples et plus simples. Par exemple:

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

affichera 5 4 comme on pourrait s'y attendre naturellement.

Une fois que vous décidez de travailler avec des chaînes larges, wprintf peut être utilisé pour imprimer des caractères larges directement. Il convient également de noter ici que dans le cas de la console Windows, le mode de traduction de stdout doit être explicitement défini sur l'un des modes Unicode en appelant _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;
}
27
LihO