gpt4 book ai didi

c++ - 如何使用任意语言环境比较 “basic_string”

转载 作者:行者123 更新时间:2023-12-01 20:03:55 26 4
gpt4 key购买 nike

我将重新发布我今天早些时候提交的问题,但现在我引用一个具体示例来回应我收到的反馈。可以在here中找到原始问题(请注意,这不是家庭作业):

我只是试图确定C++是否使不可能对basic_string对象执行(有效)的不区分大小写的比较,该比较也考虑了任意locale对象。例如,似乎无法编写如下有效的函数:

bool AreStringsEqualIgnoreCase(const string &str1, const string &str2, const locale &loc);

根据我目前的理解(但有人可以确认这一点),此函数必须为给定的 ctype::toupper()调用 collate::compare()locale(通常使用 use_facet()进行提取)。但是,由于 collate::compare()特别需要4个指针args,因此您需要为需要比较的每个字符传递这4个args(首先调用 ctype::toupper()之后),或者将两个字符串都首先转换为大写字母,然后对 collate::compare()进行一次调用。

第一种方法显然效率低下(每个测试的char要传递4个指针),第二种方法要求您将两个字符串全部转换为大写(要求分配内存,并且无需将两个字符串都复制/转换为大写)。我对此是否正确,即不可能高效地执行此操作(因为 collate::compare()周围没有办法)。

最佳答案

试图以一致的方式与世界上所有的书写系统打交道的烦恼之一是,实际上您认为您对字符所了解的任何东西实际上都是正确的。这使得很难进行“不区分大小写的比较”之类的事情。确实,进行任何形式的区域感知比较都是棘手的,而且不区分大小写也很棘手。

但是,在某些约束下,有可能实现。可以使用常规编程实践(和一些静态数据的预计算)“有效地”实现所需的算法,但是它不能像不正确的算法一样有效地实现。通常有可能在速度的正确性上取舍,但结果并不令人满意。错误但快速的语言环境实现可能会吸引那些正确实现语言环境的用户,但是对于语言环境会产生意外结果的部分受众来说,显然是不令人满意的。

词典编排不适用于人类

对于具有大小写的语言,大多数语言环境(“C”语言环境除外)已按预期方式处理字母大小写,即仅在考虑所有其他差异之后才使用大小写差异。也就是说,如果按语言环境的整理顺序对单词列表进行排序,则列表中仅在情况不同的单词将是连续的。大写单词出现在小写单词之前还是之后是与语言环境相关的,但是中间不会有其他单词。

该结果无法通过任何单次左右字符逐个比较(“字典顺序”)来实现。而且大多数语言环境还有其他排序规则的怪癖,它们也不会屈服于天真的字典顺序。

如果您具有适当的区域设置定义,则标准C++归类应该能够处理所有这些问题。但是,仅使用对whar_t对的比较功能就不能将其简化为词典上的比较,因此C++标准库不提供该接口(interface)。

以下仅是几个示例,说明为什么支持区域设置的排序规则很复杂;在Unicode Technical Standard 10中可以找到更详细的解释以及更多示例。

口音在哪里?

大多数浪漫语言(以及英语,在处理借来的单词时)都认为元音上的重音是次要特征。也就是说,首先对单词进行排序,就好像不存在重音符号一样,然后进行第二遍处理,在此过程中,未重音字母出现在重音字母之前。必须处理第三遍,以解决情况,在前两遍中将其忽略。

但这不适用于北欧语言。瑞典语,挪威语和丹麦语的字母有三个额外的元音,字母后面跟着z。这些元音用瑞典语写成å,ä和ö。在挪威语和丹麦语中,这些字母分别写在å,æ和ø上,而在丹麦语å中有时写成aa,这使奥胡斯成为丹麦城市字母顺序列表中的最后一个条目。

在德语中,字母ä,ö和ü通常按字母顺序表示为浪漫口音,但在德国电话簿(有时甚至是其他按字母顺序排列的列表)中,则按字母顺序进行,就好像它们是ae,oe和ue一样,这是较旧的样式写相同的音素(有许多对常见的姓氏,例如“Müller”和“Mueller”发音相同,而且经常混淆,因此相互搭配是有意义的。当我年轻时,加拿大电话簿中的苏格兰名字也使用了类似的约定;拼写M'McMac都聚在一起,因为它们在语音上都相同。)

一个符号,两个字母。或两个字母,一个符号

德语中也有符号ß,虽然在语音上并不完全相同,但它被整理为好像写为ss一样。稍后我们将再次遇到这个有趣的符号。

实际上,许多语言都将有向形图,甚至有向形图视为单个字母。包含44个字母的匈牙利字母包括Cs,Dz,Dzs,Gy,Ly,Ny,Sz,Ty和Zs,以及各种重音元音。但是,有关这种现象的文章中最常引用的语言(西类牙语)在1994年就不再将图ch和ll视为字母,这可能是因为强制西类牙裔作家遵循计算机系统比将计算机系统更改为更容易。处理西类牙有向图。 (维基百科声称这是来自“union 国教科文组织和其他国际组织”的压力;每个人花了相当长的时间才能接受新的字母顺序规则,并且您仍然偶尔会在南美国家的字母顺序列表中的“哥伦比亚”之后找到“智利”。)

摘要:比较字符串需要多次通过,有时还需要比较字符组

使其全部不区分大小写

由于相比之下,语言环境正确处理了大小写,因此实际上不必执行不区分大小写的排序。进行不区分大小写的等效类检查(“相等”测试)可能很有用,尽管这引发了一个问题,即其他不精确的等效类可能有用吗?在某些情况下,Unicode规范化,重音删除甚至抄写为拉丁语都是合理的,而在其他情况下则非常烦人。但是事实证明,大小写转换也不如您想象的那么简单。

由于存在二元图和三元图,其中有些具有Unicode代码点,因此Unicode标准实际上可以识别三种情况,而不是两种情况:小写,大写和标题大小写。最后一个是用来将单词的第一个字母大写的,例如,对于克罗地亚字母graph(U + 01C6;单个字符),大写字母是DŽ(U + 01C4)且其标题是必需的大小写为Dž(U + 01C5)。 “不区分大小写”比较的理论是,我们可以(至少在概念上)以某种方式转换任何字符串,以使“忽略大小写”定义的等效类的所有成员都转换为相同的字节序列。传统上,这是通过“大写”字符串来完成的,但是事实证明,这并非总是可能的,甚至是正确的。就像我一样,Unicode标准更喜欢使用术语“大小写折叠”。

C++语言环境不太适合

因此,回到C++,可悲的事实是C++语言环境没有足够的信息来进行准确的大小写折叠,因为C++语言环境的工作原理是字符串的大小写折叠仅包含顺序且单独的大写字母使用函数将字符串中的每个代码点映射到另一个代码点。正如我们将看到的那样,这是行不通的,因此其效率问题无关紧要。另一方面,ICU library具有一个接口(interface),可以按照Unicode数据库的允许正确进行大小写折叠,并且它的实现是由一些相当不错的编码人员精心设计的,因此在约束范围内它可能尽可能地高效。因此,我绝对建议您使用它。

如果要全面了解案例折叠的难度,则应阅读Unicode standard(PDF for chapter 5)的5.18和5.19节。以下仅是一些示例。

大小写转换不是从单个字符到单个字符的映射

最简单的例子是Germanß(U + 00DF),它没有大写形式,因为它从未出现在单词的开头,而且传统的德语拼写法也不使用大写字母。标准的大写变换是SS(或在某些情况下是SZ),但是该变换是不可逆的。并非ss的所有实例都写成ß。比较一下grüßen和küssen(分别是打招呼和亲吻)。在v5.1中,ẞ是“大写字母ß”,以U + 1E9E的形式添加到Unicode中,但是除了在法律规定必须使用全大写字母的路牌外,它并不常用。大小写ß是两个字母SS

并非所有表意文字(可见字符)都是单个字符代码

即使案例转换将单个字符映射到单个字符,也可能无法将其表示为wchar→wchar映射。例如,ǰ可以很容易地大写为J but,但是前者是单个组合字形(U + 01F0),而第二个是大写字母J,带有组合卡通(U + 030C)。

像ǰ这样的字形还有另一个问题:

按字符大小写折叠的朴素字符可以使归一化

假设我们将ǰ大写。我们如何将capital大写(如果它在您的系统上无法正确呈现,则是同一字符,并且在其下面带有一个杠,这是另一个IPA约定)?该组合为U + 01F0,U + 0320(j带caron,下面组合负号),因此我们继续用U + 004A,U + 030C替换U + 01F0,然后将U + 0320保留为:J̠̌。很好,但是它不等于下面带有卡通和负号的规范化大写字母J,因为在正常形式中,负号变音符号首先出现:U + 004A,U + 0320,U + 030C(J̠̌,看起来一样)。因此有时(说实话,很少,但有时)有必要重新规范化。

撇开Unicode的困惑,有时大小写转换是上下文相关的

希腊文提供了许多示例,说明如何根据商标是字首,字尾还是字内的方式来改组标记-您可以在Unicode标准的第7章中阅读更多有关此的内容-但简单而又常见case是Σ,它有两个小写形式:σ和ς。具有某些数学背景的非希腊人可能熟悉σ,但可能不知道无法在必须使用ς的单词结尾使用它。

简而言之

  • 最好的正确案例折叠方法是应用Unicode案例折叠算法,该算法要求为每个源字符串创建一个临时字符串。然后,您可以在两个转换后的字符串之间进行简单的按字节比较,以验证原始字符串是否在相同的等效类中。在可能的情况下,对转换后的字符串执行排序规则排序要比对原始字符串进行排序规则效率要低得多,并且出于排序目的,未转换的比较可能与转换后的比较一样好或更好。
  • 从理论上讲,如果您只对大小写折叠的相等感兴趣,则可以线性地进行转换,请记住,转换不一定是上下文无关的,也不是简单的字符到字符映射函数。不幸的是,C++语言环境没有为您提供执行此操作所需的数据。 Unicode CLDR更加接近,但这是一个复杂的数据结构。
  • 所有这些东西真的很复杂,并且充满了边缘情况。 (例如,请参见Unicode标准中有关重音立陶宛语i的注释。)您最好使用一个维护良好的现有解决方案,其中最好的例子是ICU。
  • 关于c++ - 如何使用任意语言环境比较 “basic_string”,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23280251/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com