9

私は主に Unix ライクなシステム (ポータブル POSIX など) に興味があります。なぜなら、Windows はワイド文字に対して奇妙なことをするように見えるからです。

読み取りおよび書き込みワイド文字関数 (getwchar()および などputwchar()) は常に「正しいことを行います」。たとえば、utf-8 から読み取り、ロケールが設定されているときに utf-8 に書き込みますwcrtomb()。例を使用した文字列fputs()? 私のシステム (openSUSE 12.3) では、$LANGがに設定されen_GB.UTF-8ていて、正しいことをしているように見えます (出力を調べると、文字列が wchar_t を使用して格納され、ワイド文字関数を使用して記述されていても、UTF-8 のように見えます)。

ただし、これが保証されているかどうかはわかりません。たとえば、cprogramming.comは次のように述べています。

[ワイド文字] は出力に使用しないでください。偽のゼロ バイトや一般的な意味を持つその他の下位 ASCII 文字 (「/」や「\n」など) がデータ全体に散りばめられる可能性があるためです。

これは、ワイド文字の出力 (おそらくワイド文字出力関数を使用) が大混乱を引き起こす可能性があることを示しているようです。

C標準はコーディングについてまったく言及していないように見えるので、wchar_tを使用するときに誰が/いつ/どのようにコーディングを適用するのか本当にわかりません。したがって、私の質問は、基本的に、アプリケーションが使用されているエンコーディングについて知る必要がない場合に、ワイド文字のみを読み取り、書き込み、および使用することが適切であるかどうかです。文字列の長さとコンソールの幅 ( wcswidth()) だけが必要なので、テキストを扱うときはどこでも wchar_t を使用するのが理想的だと思います。

4

3 に答える 3

9

ワイド文字 stdio 関数の動作とロケールとの関係を管理する関連テキストは、POSIX XSH 2.5.2 ストリームの方向とエンコード規則からのものです。

http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_05_02

基本的に、ワイド文字 stdio 関数は、ストリームがワイド指向LC_CTYPEになった時点で(ロケール カテゴリごとに) 有効なエンコーディングで常に書き込みます。FILEこれは、最初にワイド stdio 関数が呼び出されたとき、またはfwide方向をワイドに設定するために使用されたことを意味します。そのため、ストリームの操作を開始するときに、適切なLC_CTYPEロケールが目的の「システム」エンコーディング (UTF-8 など) と一致している限り、すべて問題ありません。

ただし、見落としてはならない重要な考慮事項の 1 つは、同じストリームでバイト指向の操作とワイド指向の操作を混在させてはならないということです。FILEこの規則に従わないことは、報告可能なエラーではありません。未定義の動作が発生するだけです。多くのライブラリ コードstderrがバイト指向であると想定しているため (また、 についても同じ想定をしているものもあります)、標準ストリームでワイド指向の関数を使用することstdoutは絶対にお勧めしません。その場合、使用するライブラリ関数について十分に注意する必要があります。

本当に、ワイド指向の関数を使用する理由がまったく思い浮かびません。fprintf指定子を使用して、バイト指向のFILEストリームにワイド文字列を完全に送信でき%lsます。

于 2013-03-17T03:34:34.770 に答える
9

ロケールが正しく設定されている限り、ワイド文字関数を使用して、UTF-8 を使用するシステムで UTF-8 ファイルを処理する際に問題は発生しません。それらは物事を正しく解釈することができます。つまり、(入力と出力の両方で) 必要に応じて文字を 1 から 4 バイトとして扱います。次のような方法でテストできます。

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

int main()
{
    setlocale(LC_CTYPE, "en_GB.UTF-8");
    // setlocale(LC_CTYPE, ""); // to use environment variable instead
    wchar_t *txt = L"£Δᗩ";

    wprintf(L"The string %ls has %d characters\n", txt, wcslen(txt));
}

$ gcc -o loc loc.c && ./loc
The string £Δᗩ has 3 characters

マルチバイト文字列に対して標準関数 (特に文字関数) を不用意に使用すると、次のように機能しなくなります。

char *txt = "£Δᗩ";
printf("The string %s has %zu characters\n", txt, strlen(txt));

$ gcc -o nloc nloc.c && ./nloc
The string £Δᗩ has 7 characters

文字列は基本的に単なるバイト ストリームであるため、ここでも正しく出力されます。システムは UTF-8 シーケンスを想定しているため、完全に変換されています。もちろん、文字とバイトが同等ではないことを理解せずstrlenに、文字列のバイト数 7 (および ) を報告しています。\0

この点で、ASCII と UTF-8 の互換性により、注意さえすれば、UTF-8 ファイルを単純なマルチバイト C 文字列として扱うことで問題を解決できることがよくあります。

ある程度の柔軟性もあります。標準の C 文字列 (マルチバイト文字列として) をワイド文字列に簡単に変換できます。

char *stdtxt = "ASCII and UTF-8 €£¢";
wchar_t buf[100]; 
mbstowcs(buf, stdtxt, 20);

wprintf(L"%ls has %zu wide characters\n", buf, wcslen(buf));

Output:
ASCII and UTF-8 €£¢ has 19 wide characters

ストリームでワイド文字関数を使用すると、ワイド方向に設定されます。後で標準のバイト i/o 関数を使用する場合は、最初にストリームを再度開く必要があります。で使用しないことをお勧めするのは、おそらくこのためstdoutです。stdinただし、および(リンク先のコードを含む)でのみワイド文字関数を使用する場合stdoutは、問題はありません。

于 2013-03-16T23:21:12.413 に答える
-1

fputsASCII 以外では使用しないでください。

書き留めたい場合は、UTF8 と言って、utf8 文字列で使用される実際のサイズを返す関数を使用し、\0文字列内の悪質な ' ' を心配することなく、fwrite を使用して適切なバイト数を書き込みます。

于 2013-03-16T23:05:21.853 に答える