web-dev-qa-db-ja.com

printfを使用したUTF-8文字列の印刷-ワイド文字列リテラルとマルチバイト文字列リテラル

これらのステートメントで、両方が同じエンコード(UTF-8)でソースコードに入力され、ロケールが適切に設定されている場合、実際の違いはありますか?

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

結果として、出力を行う際に一方を他方よりも優先する理由はありますか? 2番目の方がかなりパフォーマンスが悪いと思いますが、マルチバイトリテラルよりも利点(または欠点)はありますか?

編集:これらの文字列の印刷に問題はありません。ただし、printfなども使用できるようにするため、ワイド文字列関数は使用していません。したがって、問題は、上記の状況を考慮して、これらの異なる印刷方法であり、そうであれば、2番目の方法には利点がありますか?

EDIT2:以下のコメントに従って、私はこのプログラムが機能することを知っています-私はそれが不可能だと思った:

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

EDIT:2つのタイプで何が起こっているのかを見て、さらに調査を行いました。より単純な文字列を使用します。

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

コンパイラは異なるコードを生成しています。ワイド文字列は次のとおりです。

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

2番目は:

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

そして、Unicodeエンコーディングを見ると、2番目はプレーンなUTF-8です。ワイド文字表現はUTF-32です。これは実装に依存することを理解しています。

それでは、おそらくリテラルのワイド文字表現はより移植性が高いのでしょうか?私のシステムはUTF-16/UTF-32エンコーディングを直接印刷しないため、出力用にUTF-8に自動的に変換されます。

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

文字列リテラルを出力します(const char*、特殊文字はmultibyte文字として表されます)。正しい出力が表示される場合がありますが、これらのような非ASCII文字を操作しているときに対処する可能性のある他の問題があります。例えば:

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

これらの特殊文字はそれぞれ2つのcharsで表されるため、9 8を出力します。

Lプレフィックスを使用すると、ワイド文字(const wchar_t*)および%ls形式指定子で構成されるリテラルがあり、これらのワイド文字はマルチバイト文字(UTF-8)に変換されます。 。この場合、ロケールを適切に設定する必要があります。そうしないと、この変換により出力が無効になる可能性があります。

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

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

しかし、ワイド文字を使用すると作業がより複雑になる場合もありますが、他の作業はより単純で簡単になる場合があります。例えば:

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

当然のことながら、5 4を出力します。

ワイド文字列を使用することに決めたら、 wprintf を使用してワイド文字を直接印刷できます。 Windowsコンソールの場合、stdoutの変換モードは、 _setmode を呼び出して、Unicodeモードのいずれかに明示的に設定する必要があることにも注意してください。

#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