2

我有这段代码可以将字母转换为大写:

// make this character upper
if(_istalpha(zChar) && !_istupper(zChar))
   pMsg->wParam = (WPARAM)_toupper(zChar);

它已经工作了多年。最近我被要求支持阿拉伯语,我的用户说字母被损坏了。是因为上面的代码。

有人用阿拉伯语告诉我大写字母不适用。我知道我可以测试我的程序设置以查看它们是否使用阿拉伯语并避免使用此代码。但是还有其他方法吗?

例如,我知道你_tsetlocale先打电话的日期。

更新:

找到这个关于toupper的主题,其中提到了语言环境设置!会试试的。

4

2 回答 2

2

正如您所发现的,CRTtoupper和 Win32等经典转换例程CharUpper相当愚蠢。它们通常来自于整个世界都被认为是 ASCII 的时候。

您需要的是对语言敏感的转换。这是一个计算成本更高的操作,但也很难正确实现。语言很难。因此,如果可能的话,您希望将责任转移给标准库。由于您使用的是 MFC,因此您显然针对的是 Windows 操作系统,这意味着您很幸运。您可以借助 Microsoft 本地化工程师的辛勤工作,获得与 shell 和其他操作系统组件保持一致的额外好处。

您需要调用的函数是LCMapStringEx(或者LCMapString如果您仍然针对的是 pre-Vista 平台)。该函数签名的复杂性有力地证明了正确处理语言感知字符串的复杂任务。

  • 首先,您需要选择一个语言环境。您通常需要用户的默认语言环境,您可以使用 指定LOCALE_NAME_USER_DEFAULT,但您可以在此处使用任何您想要的。
  • 对于标志,您将需要LCMAP_UPPERCASE | LCMAP_LINGUISTIC_CASING. 要执行反向操作,您可以使用LCMAP_LOWERCASE | LCMAP_LINGUISTIC_CASING. 这里还有许多其他有趣且有用的选项需要牢记。
  • 然后你有一个指向源字符串的指针,以及它的字符长度(代码单元)。
  • 以及一个指向接收结果的字符串缓冲区的指针,以及它的最大字符长度(代码单元)。
  • 最后三个参数可以简单地设置为 NULL 或 0。

把它们放在一起:

BOOL ConvertToUppercase(std::wstring& buffer)
{
    return LCMapStringEx(LOCALE_NAME_USER_DEFAULT  /* or whatever locale you want */,
                         LCMAP_UPPERCASE | LCMAP_LINGUISTIC_CASING,
                         buffer.c_str(),
                         buffer.length(),
                         &buffer[0],
                         buffer.length(),
                         NULL,
                         NULL,
                         0);
}

请注意,我在这里对缓冲区的内容进行了就地转换,因此假设大写字符串的长度与原始输入字符串的长度完全相同。这可能是正确的,但可能不是一个普遍安全的假设,因此您要么想要添加对此类错误的处理 ( ERROR_INSUFFICIENT_BUFFER) 和/或防御性地向缓冲区添加一些额外的填充。

如果您喜欢像现在这样使用 CRT 函数,并且它的朋友是/_totupper_l的包装器。请注意后缀,它表示这些是区域设置感知转换函数。它们允许您传递将在转换中使用的显式语言环境。LCMapStringLCMapStringEx_l

于 2016-12-03T08:41:58.310 回答
0

我假设您使用的是 UTF-8 字符串。在这种情况下,您的代码需要支持 UTF-8,即能够处理多字节字符。例如,如果双字节字符串中的第二个字符恰好与字母 'c' 具有相同的值,它将被您的代码拾取并转换为大写,从而产生完全不同的双字节字符。看看这个问题: Convert a unicode String In C++ To Upper Case

于 2016-12-05T18:16:00.303 回答