2

我已经在文件系统中搜索名称中不可移植的字符。对于那个 ich 使用 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

您的程序以 C 语言环境开始,它将字符串视为 ASCII(或未指定的 ASCII 兼容 8 位编码)。所以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_len1,我们得到c3结果。第二个给我们mb_len2 和inc4的 Unicode 代码点。Äwc

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

mbtowc()is 使用 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()某些库中某个可能依赖于未设置为某些语言环境的 deep 的某些使用。此外,区域设置敏感函数可能不是线程安全的和/或可重入的。

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++ 还支持在某些编码之间进行转换,特别是在各种 Unicode 编码(UTF-8、UTF-16 和 UTF-32)之间使用 wstring_convert 模板和一些标准的 codecvt 方面(codecvt_utf8、codecvt_utf8_utf16)。您还可以调整 codecvt_byname 以在charwchar_t语言环境编码之间进行转换,而不会直接与语言环境混淆。


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

于 2012-11-26T19:55:56.030 回答