我在文件系统中搜索不可移植的名字中的字符。为此,使用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
答案 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_len
为1
,结果为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以在char
和wchar_t
语言环境编码之间进行转换,而不会直接与语言环境混淆。
当然,如果您确实需要在编码之间进行转换,这一切都很重要。目前尚不清楚这是否只需要“在文件系统中搜索不可移植的名字中的字符”。如果您有一个您认为合法的代码点列表(或非法列表),那么直接搜索UTF-8字符串中这些代码点的UTF-8编码应该不难,不需要进行转换。 / p>