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

2023-12-02

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

我只是想确定 C++ 是否无法执行(有效的)不区分大小写比较一个basic_string对象也包含任意任意的因素locale目的。例如,似乎不可能编写如下所示的高效函数:

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

根据我目前的理解(但有人可以证实这一点),这个函数has呼叫两者ctype::toupper() and collate::compare()对于给定的locale(一如既往地使用提取use_facet())。然而,因为collate::compare()特别需要 4 个指针参数,您需要为需要比较的每个字符传递这 4 个参数(在第一次调用ctype::toupper()),或者,首先将两个字符串转换为大写,然后进行一次调用collate::compare().

第一种方法显然效率低下(为每个测试的字符传递 4 个指针),第二种方法要求您将两个字符串全部转换为大写(需要分配内存以及不必要地将两个字符串复制/转换为大写)。我对此是否正确,即,不可能有效地做到这一点(因为没有办法解决)collate::compare()).


试图以一致的方式处理世界上所有的书写系统的小烦恼之一是,实际上你认为你所了解的关于字符的知识实际上都是正确的。这使得执行“不区分大小写的比较”之类的事情变得很棘手。事实上,进行任何形式的区域设置比较都是很棘手的,而且不区分大小写也很棘手。

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

字典顺序对人类不起作用

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

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

如果您有适当的区域设置定义,标准 C++ 排序规则应该能够处理所有这些问题。但它不能仅仅使用对成对的比较函数来简化为字典顺序比较whar_t,因此 C++ 标准库不提供该接口。

以下只是说明为什么区域设置感知排序规则如此复杂的几个示例;更长的解释,以及更多的例子,可以在Unicode 技术标准 10.

口音去哪儿了?

大多数浪漫语言(以及英语,在处理借用词时)都认为元音之上的重音是一种次要特征;也就是说,首先对单词进行排序,就像不存在重音符号一样,然后进行第二次排序,其中非重音字母位于重音字母之前。需要第三遍来处理大小写,这在前两遍中被忽略。

But that doesn't work for Northern European languages. The alphabets of Swedish, Norwegian and Danish have three extra vowels, which follow z in the alphabet. In Swedish, these vowels are written å, ä, and ö; in Norwegian and Danish, these letters are written å, æ, and ø, and in Danish å is sometimes written aa, making Aarhus the last entry in an alphabetical list of Danish cities.

In German, the letters ä, ö, and ü are generally alphabetised as with romance accents, but in German phonebooks (and sometimes other alphabetical lists), they are alphabetised as though they were written ae, oe and ue, which is the older style of writing the same phonemes. (There are many pairs of common surnames such as "Müller" and "Mueller" are pronounced the same and are often confused, so it makes sense to intercollate them. A similar convention was used for Scottish names in Canadian phonebooks when I was young; the spellings M', Mc and Mac were all clumped together since they are all phonetically identical.)

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

German also has the symbol ß which is collated as though it were written out as ss, although it is not quite identical phonetically. We'll meet this interesting symbol again a bit later.

In fact, many languages consider digraphs and even trigraphs to be single letters. The 44-letter Hungarian alphabet includes Cs, Dz, Dzs, Gy, Ly, Ny, Sz, Ty, and Zs, as well as a variety of accented vowels. However, the language most commonly referenced in articles about this phenomenon -- Spanish -- stopped treating the digraphs ch and ll as letters in 1994, presumably because it was easier to force Hispanic writers to conform to computer systems than to change the computer systems to deal with Spanish digraphs. (Wikipedia claims it was pressure from "UNESCO and other international organizations"; it took quite a while for everyone to accept the new alphabetization rules, and you still occasionally find "Chile" after "Colombia" in alphabetical lists of South American countries.)

总结:比较字符串需要多遍,有时需要比较字符组

使其全部不区分大小写

由于相比之下,区域设置可以正确处理大小写,因此实际上没有必要执行不区分大小写的排序。进行不区分大小写的等价类检查(“相等”测试)可能很有用,尽管这提出了其他哪些不精确的等价类可能有用的问题。 Unicode 规范化、重音删除、甚至转录为拉丁语在某些情况下都是合理的,但在其他情况下却非常烦人。但事实证明,大小写转换也不像您想象的那么简单。

Because of the existence of di- and trigraphs, some of which have Unicode codepoints, the Unicode standard actually recognizes three cases, not two: lower-case, upper-case and title-case. The last is what you use to upper case the first letter of a word, and it's needed, for example, for the Croatian digraph dž (U+01C6; a single character), whose uppercase is DŽ (U+01C4) and whose title case is Dž (U+01C5). The theory of "case-insensitive" comparison is that we could transform (at least conceptually) any string in such a way that all members of the equivalence class defined by "ignoring case" are transformed to the same byte sequence. Traditionally this is done by "upper-casing" the string, but it turns out that that is not always possible or even correct; the Unicode standard prefers the use of the term "case-folding", as do I.

C++ 语言环境不能完全胜任这项工作

因此,回到 C++,可悲的事实是 C++ 语言环境没有足够的信息来进行准确的大小写折叠,因为 C++ 语言环境的工作原理是假设字符串的大小写折叠仅包含顺序和单独的大写字母字符串中的每个代码点都使用将一个代码点映射到另一个代码点的函数。正如我们将看到的,这根本行不通,因此其效率问题是无关紧要的。另一方面,重症监护病房图书馆有一个接口,可以像 Unicode 数据库允许的那样正确地进行大小写折叠,并且它的实现是由一些非常优秀的编码人员精心设计的,因此它可能在限制范围内尽可能高效。所以我绝对推荐使用它。

如果您想很好地了解折叠案例的难度,您应该阅读 5.18 和 5.19 节统一码标准 (第 5 章的 PDF)。以下仅举几个例子。

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

The simplest example is the German ß (U+00DF), which has no upper-case form because it never appears at the beginning of a word, and traditional German orthography didn't use all-caps. The standard upper-case transform is SS (or in some cases SZ) but that transform is not reversible; not all instances of ss are written as ß. Compare, for example, grüßen and küssen (to greet and to kiss, respectively). In v5.1, ẞ, an "upper-case ß, was added to Unicode as U+1E9E, but it is not commonly used except in all-caps street signs, where its use is legally mandated. The normal expectation of upper-casing ß would be the two letters SS.

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

Even when a case transform maps a single character to a single character, it may not be able to express that as a wchar→wchar mapping. For example, ǰ can easily be capitalized to , but the former is a single combined glyph (U+01F0), while the second is a capital J with a combining caron (U+030C).

There is a further problem with glyphs like ǰ:

天真的逐字符大小写折叠可能会导致非规范化

Suppose we upper-case ǰ as above. How do we capitalize ǰ̠ (which, in case it doesn't render properly on your system, is the same character with an bar underneath, another IPA convention)? That combination is U+01F0,U+0320 (j with caron, combining minus sign below), so we proceed to replace U+01F0 with U+004A,U+030C and then leave the U+0320 as is: J̠̌. That's fine, but it won't compare equal to a normalized capital J with caron and minus sign below, because in the normal form the minus sign diacritic comes first: U+004A,U+0320,U+030C (J̠̌, which should look identical). So sometimes (rarely, to be honest, but sometimes) it is necessary to renormalize.

撇开 unicode 的怪异不谈,有时大小写转换是上下文相关的

Greek has a lot of examples of how marks get shuffled around depending on whether they are word-initial, word-final or word-interior -- you can read more about this in chapter 7 of the Unicode standard -- but a simple and common case is Σ, which has two lower-case versions: σ and ς. Non-greeks with some maths background are probably familiar with σ, but might not be aware that it cannot be used at the end of a word, where you must use ς.

In short

  1. 大小写折叠的最佳可用正确方法是应用 Unicode 大小写折叠算法,该算法需要为每个源字符串创建一个临时字符串。然后,您可以在两个转换后的字符串之间进行简单的字节比较,以验证原始字符串是否位于同一等价类中。对转换后的字符串进行排序规则虽然可能,但效率比对原始字符串进行排序规则要低得多,并且出于排序目的,未转换的比较可能与转换后的比较一样好或更好。

  2. 理论上,如果您只对大小写相等感兴趣,则可以线性进行转换,请记住转换不一定是上下文无关的,也不是简单的字符到字符映射函数。不幸的是,C++ 语言环境不向您提供执行此操作所需的数据。 Unicode CLDR 更接近,但它是一个复杂的数据结构。

  3. 所有这些东西都非常复杂,并且充满了边缘情况。 (请参阅 Unicode 标准中有关立陶宛语重音的注释i例如。)您最好只使用维护良好的现有解决方案,其中最好的例子是 ICU。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使用任意语言环境比较“basic_string” 的相关文章

  • X11 模式对话框

    如何使用 Xlib 在 X11 中创建模式对话框 模态对话框是一个位于应用程序其他窗口之上的窗口 就像瞬态窗口一样 并且拒绝将焦点给予应用程序的其他窗口 在 Windows 中 当试图从模态窗口夺取焦点时 模态也会通过闪 烁模态窗口的标题栏
  • Linq - 从表达式 创建表达式

    我有一个谓词Expression
  • 是否有可能劫持标准输出

    我正在尝试使用 C 重定向 Windows XP 上已运行进程的标准输出 我知道如果我自己生成进程 我可以做到这一点 但对于这个应用程序 我更喜欢一个 监听器 我可以附加到另一个进程 这在纯 Net 中可能吗 如果不可能 在 Win32 中
  • 生成多个随机数

    我想生成 25 个唯一的随机数并将它们列在控制台中 数字的长度应至少为 10 个字符 有什么简单的方法可以做到这一点吗 尝试将数字构建为字符串 并使用 HashSet 确保它们是唯一的 Random random new Random Ha
  • .crt 部分?这个警告是什么意思?

    我最近收到此警告 VC 2010 warning LNK4210 CRT section exists there may be unhandled static initializers or terminators 我假设这是关键部分
  • 深拷贝和动态转换 unique_ptr

    假设我有一个如下所示的类 class A virtual A class B public A class C public A 我还有一个 unique ptr 向量 它是这样声明的 std vector
  • NDK 应用 onDestroy 清理 - 如何 DetachCurrentThread

    因此 如果我们连接 我们必须在完成后分离线程 对吗 JNIEnv get jni env JNIEnv res JAVA VM gt GetEnv void res JNI VERSION 1 6 Using cached JavaVM J
  • 将公历日期转换为儒略日期,然后再转换回来(随着时间)

    我正在编写一个程序 必须将当前的公历日期和时间转换为儒略日期 然后再转换回公历门 最终我需要添加能够添加年 月 日 小时 分钟和秒的功能 但我需要先解决这部分问题 现在我已经从公历日期转换为儒略日期 所以从逻辑上讲 我觉得我应该能够以某种方
  • 平滑手绘曲线

    我有一个允许用户绘制曲线的程序 但这些曲线看起来不太好 它们看起来摇摇欲坠 而且是手绘的 所以我想要一种能够自动平滑它们的算法 我知道平滑过程中存在固有的模糊性 因此它不会每次都完美 但这种算法似乎确实存在于多个绘图包中 并且它们工作得很好
  • 嵌入资源文件的路径

    我的资源文件中有一个图标 我想引用它 这是需要图标文件路径的代码 IWshRuntimeLibrary IWshShortcut MyShortcut MyShortcut IWshRuntimeLibrary IWshShortcut W
  • 如何减少 MinGW g++ 编译器生成的可执行文件的大小?

    我有一个简单的 Hello world C 程序 在 Win XP 下由 MinGW g 编译器编译为 500kB 可执行文件 有人说这是由于iostream的库和静态链接libstdc dll Using s链接器选项有点帮助 减少了 5
  • 调用异步方法在视图模型的构造函数中加载数据有警告

    我的视图包含一个 ListView 它显示来自互联网的一些数据 我创建一个异步方法来加载数据并在我的视图模型的构造函数中调用该方法 它有一个警告提示我现在使用await关键字 还有其他解决方案可以在构造函数中异步加载数据吗 有几种可以应用的
  • 您对“大规模 C++ 软件设计”的看法 [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 正在阅读亚马逊评论 https rads stackoverflow com amzn click com 0201633620 and ACC
  • 当需要不同数量和类型的参数时如何创建操作委托列表

    我们有一组大约两打的类 它们继承自具有抽象 Validate 方法的基类 当然 每个类都有不同的验证需求 但它们之间的不同组合需要规则 因此 正如您可以想象的那样 这导致了大量代码重复 例如 A 类需要规则 1 3 6 和 9B 类需要规则
  • 传递数组时在 C 中的函数参数中强制指定数组大小

    Context 在 C 中 我有一个以数组作为参数的函数 该参数用作该函数的输出 输出的大小始终相同 我会 让阅读代码的人清楚所需的大小 不过它已经在函数注释中了 理想情况下 编译会输出警告或错误 这样我就可以在编译时而不是运行时防止出现问
  • Gremlin.net 文本包含等效项

    我正在使用 Gremlin net 库连接到 janus 图形服务器 我使用 cassandra 和弹性搜索进行数据存储和索引 在我使用的 gremlin 语言和 gremlin 控制台中文本包含在属性的文本中进行搜索 我正在使用混合索引
  • 如何同步nosql db(ravendb)中的更改

    我已经开始在 RavenDB 的示例上学习 NoSQL 我从一个最简单的模型开始 假设我们有由用户创建的主题 public class Topic public string Id get protected set public stri
  • 选择合适的IDE

    您会推荐使用以下哪种 IDE 语言来在 Windows 下开发涉及识别手势并与操作系统交互的项目 我将使用 OpenCV 库来执行图像处理任务 之后 我将使用 win32 API 或 NET 框架与操作系统交互 具体取决于您建议的工具 性能
  • “1个未解决的外部”C++

    我已经检查了所有文件之间的连接以及类和函数定义 但每次我尝试运行我的程序时 它都会阻止我并告诉我它有 1 个未解析的外部 该程序应该打开多个文件 一个 学生 文件和一个 成绩 文件 从中读取数据 然后使用 查询文件 来查找数据 找到查询中要
  • 从脚本启用/禁用 GameObject 组件 [Unity3D]

    我需要获取一个脚本中设置的布尔值 放入名为 bouclier 的变量 以启用或禁用游戏对象 该变量位于游戏对象 Player 中 此处右下角 我需要启用或禁用这个游戏对象 Bouclier01 为此 我将脚本附加到游戏对象 Bouclier

随机推荐