2

移植できない名前の文字をファイルシステムで検索しました。そのためには、mbtowc 関数を使用してすべての文字をチェックします。

OSXで私が試した:

// OSX の場合

#include <iostream>

using namespace std;

int main(int argc, const char * argv[])
{
string s1 = "Ä";
size_t len = s1.length();           // will be 2, ok


const char* s1c = s1.c_str();       // 0xC3 0x84 0x00, ok

char a = s1[0];                     // 0xc3, ok
char b = s1[1];                     // 0x84, ok

mbtowc(NULL,NULL,0);                // reset

wchar_t wc;
int mb_len = mbtowc(&wc,s1c,len);   // mb_len = 1, wc=0xc3 00 00 00
                                    // why only one byte?
                                    // how can i get the right Wchar???
char mb2[10];
int mblen2 = wctomb(mb2,wc);        // mblen2 = 1; mb2 = 0xC3

string s2 = string(mb2);            // len = 1 only 0xC3


return 0;
}

すべての文字に対して mbtows が 1 だけを返すのはなぜですか?

ヘリベルト

4

2 に答える 2

2

プログラムは、文字列を ASCII (または未指定の ASCII 互換の 8 ビット エンコーディング) として扱う C ロケールで開始します。そのmbtowc()ため、文字列の最初のバイトをwchar_t. setlocale(LC_CTYPE, locale)ソースは UTF-8 でエンコードされているため、文字列定数も同様になるため、UTF-8 を使用するロケールで呼び出す必要があります。

setlocale(LC_CTYPE, "")ユーザーの現在のロケール設定を使用するため、ユーザーが提供するファイルを読み取る場合に適しています。ただし、誰かが UTF-8 ロケールを使用しないマシンでプログラムを実行しようとすると、例が壊れる可能性があります。代わりにsetlocale(LC_CTYPE, "UTF-8")、常に UTF-8 を使用するロケールである を使用できます (標準化されているとは思いませんが、少なくとも私の Mac OS X および Linux ボックスには存在します)。

以下に例を示します (今回は、少し単純にするために、C++ ではなく純粋な C を使用しています)。何が起こっているかを示すためにいくつかのprintfsを追加しました。mbtowc()呼び出しの前後で同じように実行されsetlocale()ます。

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

void test_mbtowc(char *s) {
  size_t len = strlen(s);
  wchar_t wc;

  mbtowc(NULL,NULL,0);
  int mb_len = mbtowc(&wc,s,len);
  printf("%d, %08x\n", mb_len, wc);
}

int main()
{
  char *s = "Ä";

  printf("%02hhx %02hhx %02hhx\n", s[0], s[1], s[2]);
  test_mbtowc(s);

  setlocale(LC_CTYPE, "UTF-8");
  test_mbtowc(s);

  return 0;
}

これが出力です。ご覧のとおり、文字列は UTF-8 でエンコードされています。mbtowc最初の単にコピーを呼び出すと、最初のバイトが単純にコピーされます。mb_lenであり、結果として1得られます。c32 番目は、 inの Unicode コードポイントであるmb_len2 と を示します。c4Äwc

c3 84 00
1, 000000c3
2, 000000c4
于 2012-11-26T19:23:38.273 に答える
1

mbtowc()これは、C ロケールを使用して、変換するエンコーディングを決定します。C ロケールは常に として始まりますが"C"、基本文字セット (ASCII でサポートされる抽象文字レパートリーのサブセット) 以外の文字をサポートすることは保証されていません。

OS X はデフォルトでどこでも UTF-8 を使用するため、mbtowc()期待するエンコーディング間の変換は行われません。

C ロケールを、適切なエンコーディングを使用するロケールに設定できます。C++ プログラムでこれを行う場合は、おそらく C++ グローバル ロケールを設定して行う必要があります (これにより、C ロケールが設定されます)。

std::locale::global(std::locale("en_US.UTF-8")); // locale names are not portable

ただし、ロケールをいじるのは一般的には良いことではありません。グローバル ロケールは本質的にグローバル変数であり、それを使用しない通常の理由がすべてあります。これには幅広い影響があります。たとえば、sprintf()特定のロケールに設定されていないことに依存する可能性のあるライブラリのどこかで深い使用に影響を与える可能性があります。また、ロケールに依存する関数は、スレッド セーフではないか、再入可能でない可能性があります。

OS X には、グローバル ロケールを使用する代わりに、追加のロケール パラメータを使用するロケール依存関数のバージョンを含む「拡張ロケール サポート」ライブラリ (header <xlocale.h>) があります。*_lこれにより、グローバル ロケールに関する問題の多くが修正されます。OS X で標準の C++ ロケール機能の多くを実装するためにも使用されていると思います。

locale_t loc = newlocale(LC_ALL_MASK, "en_US.UTF-8", NULL);
char buf[MB_CUR_MAX_L(loc)];
mbstate_t state = {};  
wcrtomb_l(buf, L'A', &state, loc);
freelocale(loc);

既知のエンコーディング間の変換のみが必要な場合は、ロケールをまったく使用する必要がない場合があります。iconv は、多数のエンコーディング セット間で直接変換できる API です。C++ は、特定のエンコーディング間の変換、特に wstring_convert テンプレートといくつかの標準 codecvt ファセット (codecvt_utf8、codecvt_utf8_utf16) を使用したさまざまな Unicode エンコーディング (UTF-8、UTF-16、および UTF-32) 間の変換もサポートしています。charロケールを直接いじることなく、とwchar_tロケール エンコーディングの間で変換するように codecvt_byname を適応させることもできます。


もちろん、これはすべて、エンコーディング間の変換が本当に必要な場合にのみ重要です。これが「移植できない名前の文字をファイルシステムで検索する」ためだけに必要であることは明らかではありません。合法と見なすコードポイントのリスト (または違法なもののリスト) がある場合、UTF-8 文字列でそれらのコードポイントの UTF-8 エンコーディングを直接検索することはそれほど難しくないはずです。変換は必要ありません。

于 2012-11-26T19:55:56.030 に答える