mbtowc在osx上总是返回一个字节

时间:2012-11-26 18:26:48

标签: c++ macos unicode

我在文件系统中搜索不可移植的名字中的字符。为此,使用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只返回所有char只有1?

Heribert

2 个答案:

答案 0 :(得分:2)

您的程序从C语言环境开始,该语言环境将字符串视为ASCII(或者,未指定的ASCII兼容的8位编码)。因此mbtowc()只是将字符串中的第一个字节复制到wchar_t中。您需要使用使用UTF-8的语言环境调用setlocale(LC_CTYPE, locale),因为您的源代码是以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_len1,结果为c3。第二个为mb_len提供了c4Äwc中的c3 84 00 1, 000000c3 2, 000000c4 的Unicode代码点。

{{1}}

答案 1 :(得分: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有一个'扩展语言环境支持'库(标题<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 facets(codecvt_utf8,codecvt_utf8_utf16)在某些编码之间进行转换,特别是在各种Unicode编码(UTF-8,UTF-16和UTF-32)之间进行转换。您还可以调整codecvt_byname以在charwchar_t语言环境编码之间进行转换,而不会直接与语言环境混淆。


当然,如果您确实需要在编码之间进行转换,这一切都很重要。目前尚不清楚这是否只需要“在文件系统中搜索不可移植的名字中的字符”。如果您有一个您认为合法的代码点列表(或非法列表),那么直接搜索UTF-8字符串中这些代码点的UTF-8编码应该不难,不需要进行转换。 / p>