在C++中将宽字符字符串转换为小写

7

如何在C++中将wchar_t字符串从大写转换为小写?

该字符串包含日语、中文、德语和希腊字符的混合。

我考虑使用towlower...

http://msdn.microsoft.com/en-us/library/8h19t214%28VS.80%29.aspx

.. 但文档说:

towlower的大小写转换是与区域设置相关的。只有当前区域设置相关的字符才会在大小写方面发生变化。

编辑:也许我应该描述一下我的做法。我从用户那里收到一个Unicode搜索查询。它最初是以UTF-8编码的,但我将其转换为宽字符(可能在措辞上我有些错误)。我的调试器(VS2008)在“变量快速查看”中正确显示了日语、德语等字符。我需要遍历另一组Unicode数据,并查找搜索字符串的匹配项。当搜索区分大小写时,这对我来说不是问题,但在不区分大小写的情况下,这就更加棘手了。我(也许是天真地)解决问题的方法是将所有输入数据和输出数据转换为小写,然后进行比较。


3
另一种方法是使用忽略大小写的比较算法。大小写并不是你唯一的问题。如果未对字符串进行规范化,则例如重音符号(如éÕ)可以被视为一个字符,或被视为几个单独的字符(如'e~O)。在比较之前进行适当的规范化(NFC/NFD/NFKC/NFKD)对于你的情况至关重要。 - Abel
Abel,请将其发布为正确的答案,以便可以按照应有的方式进行投票。在这种情况下,这几乎是唯一正确的答案… - Pavel Minaev
4个回答

10

如果您的字符串包含所有这些字符,则代码集必须基于Unicode。如果实现正确,Unicode (第4章“字符属性”)定义了字符属性,包括字符是否为大写字母以及小写映射等。

在此前提下,来自<wctype.h>towlower()函数是正确的工具。如果它不能完成任务,则需要与供应商讨论质量问题(Quality of Implementation)。如果供应商没有响应,则可以考虑使用其他库。在这种情况下,您可以考虑使用ICU(国际Unicode组件)。


2
Unicode的大小写映射仍然部分依赖于所在地区,正如您提供的文档中所述。引用:“SpecialCasing.txt-包含其他大写映射,将一个字符映射到多个字符,例如“ß”映射到“SS”。还包括上下文相关映射,并带有标志以使其与常规映射区分开来,_以及一些地区相关的映射_。”因此,“tolower”无法避免具体到所在地区。 - Pavel Minaev
@Pavel 这个过程被称为“Unicode字符串的规范化”,它确保ßss被视为相等(取决于所选择的规范化形式),而Unicode包含了与语言无关的算法,以此来满足对于区域设置或应用程序特定处理的需求。 - Abel
2
@Abel:规范化并不是一个完整的解决方案。例如,在某些拉丁语言中,变音符号会在大写字母上消失,在其他语言中则不会。除非你知道文本所使用的语言,否则无法判断。当然,还有臭名昭著的土耳其无点“i”问题 - 对于土耳其语,您希望 İ 转换为小写的 iI 转换为小写的 ı,但对于任何其他拉丁字母表语言,您希望 I 转换为小写的 i - Pavel Minaev
@Pavel:这是一个很好的阐述,我完全同意。规范化并不完美,它更像是一种简单粗暴的方法,但在许多情况下都有帮助。也许现在是讨论中包含链接到Unicode排序算法的好时机,该算法对此进行了全面讨论(远不止大小写转换):http://unicode.org/reports/tr10/,以及Unicode大小写映射:http://unicode.org/reports/tr21/tr21-5.html。 - Abel
@JonathanLeffler:ICU很有趣,但可能过于复杂了。我可能会选择处理UnicodeData.txt [编译成二进制并过滤掉不相关的部分]。 - user877329

3
你手头上有一个棘手的问题。日语环境无法将德语转换,反之亦然。还有一些语言没有大小写的概念(我想这里的toupper和friends会无效)。那么,你能把字符串分成同一语言的单独单词块吗?如果可以,那么你就可以将这些块转换并串起来。

日语和其他东亚表意文字的语言是主要没有大写字母的语言的例子。 - Jonathan Leffler
5
不仅如此,而且不同的语言可能对特定字母的大小写有不同的看法。在不了解语言的情况下,没有单一的算法可以正确地处理任意随机Unicode字符串的大小写。 - Pavel Minaev
1
尽管我同意这种评估,Unicode包括与语言环境无关的大写/小写属性,其使用方法在3.13“默认大小写操作”下进行描述,这些属性在缺乏特定语言的个性化时应该被使用,这是标准规定的。 - Abel
它确实可以。问题在于它对于99%的情况都是正确的,但你会有1%的错误。这可能是一个问题,也可能不是。一般来说,当你用它来处理代码中的标识符,甚至文件名时,它已经足够好了。 - Pavel Minaev
@Pavel:这意味着你不能总是做对,但你可以始终如一地做到。我知道在土耳其语中将'I'转换为'i'是错误的,但如果你只是为了比较而规范化字符串,而不是打印结果,那么它可能完全没问题。 - David Thornley
@David:可能无法正常工作。比如在原始文档中有文本“Diyarbakır”,用户输入了“DİYARBAKIR”搜索字符串。您使用默认的Unicode大小写规则将两个字符串转换为小写;第一个变成了“diyarbakır”,第二个变成了“diyarbakir”。现在它们不匹配,如果文本是土耳其语,则它们应该匹配。 - Pavel Minaev

1

这个Stack Overflow的回答展示了如何使用facets来处理多种语言环境。如果你在Windows上,可以考虑使用win32 API函数;如果你能够使用C++.NET(托管C++),则可以使用char.ToLowerstring.ToLower函数,它们都是Unicode兼容的。


0

请查看<wchar.h>中的_wcslwr_lMSDN)。

您应该能够在每个区域设置上运行该函数以处理输入。


你应该能够在每个语言环境的输入上运行该函数。如果集合中的两个语言环境将同一字符映射为不同的字符,该怎么办? - Pavel Minaev
如其他评论所述,为了避免这些情况,您必须知道字符串每个部分的语言。真的没有绕过这一点的方法。我只是建议使用不同的函数来更轻松地解决在当前区域设置上运行操作的问题。 - Jon Seigel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接