C++ mbtowc在osx上始终返回一个字节

C++ mbtowc在osx上始终返回一个字节,c++,macos,unicode,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_st

我在文件系统中搜索了不可移植名称中的字符。为此,我使用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;
}
#包括
使用名称空间std;
int main(int argc,const char*argv[]
{
字符串s1=“Ä”;
size_t len=s1.length();//将为2,好吗
const char*s1c=s1.c_str();//0xC3 0x84 0x00,确定
字符a=s1[0];//0xc3,ok
char b=s1[1];//0x84,ok
mbtowc(NULL,NULL,0);//重置
wchar_t wc;
int mb_len=mbtowc(&wc,s1c,len);//mb_len=1,wc=0xc300
//为什么只有一个字节?
//我怎样才能得到正确的Wchar???
char-mb2[10];
int mblen2=wctumb(mb2,wc);//mblen2=1;mb2=0xC3
字符串s2=字符串(mb2);//仅限len=1 0xC3
返回0;
}
为什么mbtows只返回所有字符1


Heribert

您的程序在C语言环境中启动,该语言环境将字符串视为ASCII(或者,未指定的ASCII兼容8位编码)。因此,
mbtowc()
只需将字符串中的第一个字节复制到
wchar\u 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来显示发生了什么。它在调用

setlocale()
之前和之后运行相同的
mbtowc()

mbtowc()。C语言环境总是以
“C”
开头,这不能保证支持基本字符集(ASCII支持的抽象字符集的子集)之外的任何字符

默认情况下,OSX在其他任何地方都使用UTF-8,因此
mbtowc()
不会在您期望的编码之间转换

您可以将C语言环境设置为使用适当编码的语言环境。如果你在C++程序中这样做,你应该通过设置C++全局语言环境(这将依次设置C语言环境): 然而,搞乱区域设置通常不是一件好事。全局语言环境本质上是一个全局变量,有所有反对使用它的正常理由。它具有广泛的影响,例如,它可以影响某些库中的
sprintf()
深度的使用,而这些库可能依赖于未设置为某些区域设置。此外,区域设置敏感函数可能不是线程安全和/或可重入的

OS X有一个“扩展的语言环境支持”库(header
),其中包含
*\u l
版本的语言环境敏感函数,这些函数采用额外的语言环境参数,而不是使用全局语言环境。这解决了全局区域设置的许多问题。我相信它甚至被用来在OS x.< /p>中实现大部分标准的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++还支持使用WSTRIGIN转换模板和一些标准的CODEVVT刻面(CODECDVTUTUF8,CODECDVTUTUTF8UTUF16)在某些编码之间,特别是在各种Unicode编码(UTF-8,UTF-16和UTF-32)之间进行转换。您还可以调整codevt_byname以在
char
wchar_t
区域设置编码之间进行转换,而不直接与区域设置混淆



当然,只有当您确实需要在编码之间转换时,所有这些才重要。目前还不清楚是否需要在文件系统中搜索不可移植的名称。如果你有一个你认为合法的代码点列表(或者是非法列表),那么直接搜索UTF-8字符串中的代码点的UTF-8编码就不那么困难了。无需转换。

谢谢,这就是问题所在,在这里说明。谢谢,我将使用这种方式。本地化扩展也可以在其他系统上使用,也可以使用win(Visula Studio 2010:名称前带有“uu”)。顺便说一下,MB_CUR_MAX取决于当前控制台。当程序启动时,consloe将为C,值为1,这将导致缓冲区溢出。MB_LEN_MAX返回所有编码的最大长度(大部分为6)。@user1854272是的,关于
MB_CUR_MAX
你是对的。有一个宏
MB\u CUR\u MAX\u L()
用于
\u L
函数。
c3 84 00
1, 000000c3
2, 000000c4
std::locale::global(std::locale("en_US.UTF-8")); // locale names are not portable
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);