我前段时间听到过关于如何创建模板化字符串类的讨论,不应该使用strcmp,strcpy和strlen作为可以使用UTF8和UTF16的模板化字符串类。我记得,你应该使用来自algorithm.h的函数,但是,我不记得实现是怎么回事,或者为什么会这样。有人可以解释一下使用哪些功能,如何使用它们以及为什么?
模板化字符串类的示例将是
之类的内容String<UTF8> utf8String;
String<UTF16> utf16String;
这是UTF8将是无符号字符的位置,而UTF16是无符号短字符。
答案 0 :(得分:6)
首先,C ++不需要额外的字符串类。可能已经有数百或数千个字符串类已经开发出来,而你的字符串类不会改善这种情况。除非你纯粹为了你的教化而这样做,否则你应该长时间地思考,然后决定不写新的。
您可以使用std::basic_string<char>
来保存UTF-8代码单元序列,std::basic_string<char16_t>
来保存UTF-16代码单元序列,std::basic_string<char32_t>
来保存UTF-32代码单元序列等。 C ++甚至为这些类型提供了简短易用的名称:string
,u16string
和u32string
。 basic_string
已经通过提供成员函数来解决您在这里提出的问题,这些函数用于复制,比较和获取适用于您使用的任何代码单元的字符串长度。
我无法想到任何没有与遗留代码接口的新代码使用其他任何东西作为字符串的规范存储类型的任何理由。即使您与使用其他内容的遗留代码进行交互,如果该接口的表面区域不大,您应该仍然使用其中一种标准类型,而不是其他任何东西,当然,如果您与遗留代码接口无论如何,你将使用那种传统类型,而不是编写自己的新类型。
话虽如此,您不能将strcmp
,strcpy
和strlen
用于模板化字符串类型的原因是它们都在空终止字节序列上运行。如果你的代码单元大于一个字节,那么在实际终止空代码单元之前可能有 bytes 为零(假设你根本就使用了null终止,你可能不应该)。考虑字符串“Hello”的UTF-16表示的字节(在小端机器上)。
48 00 65 00 6c 00 6c 00 6f 00
由于UTF-16使用16位代码单元,因此字符“H”最终存储为两个字节48 00
。通过假设第一个空字节为结尾对上述字节序列进行操作的函数将假设第一个字符的后半部分标记整个字符串的结尾。这显然不起作用。
因此,strcmp
,strcpy
和strlen
都是可以更一般地实施的算法的专用版本。由于它们只使用字节序列,并且您需要处理代码单元序列可能大于一个字节的代码单元序列,因此您需要使用可以与任何代码单元一起使用的通用算法。标准库提供了许多通用算法。以下是我对替换这些str*
函数的建议。
strcmp
比较两个代码单元序列,如果两个序列相等则返回0,如果第一个按字典顺序小于第二个,则返回正数,否则返回负数。标准库包含通用算法lexicographical_compare
,它几乎完全相同,只是如果第一个序列按字典顺序小于第二个序列,则返回true,否则返回false。
strcpy
复制一系列代码单元。您可以使用标准库的copy
算法。
strlen
获取指向代码单元的指针,并在找到空值之前计算代码单元的数量。如果您需要此函数而不是只告诉您字符串中代码单元数的函数,则可以通过将null值作为要查找的值传递,使用算法find
来实现它。如果你想要找到序列的实际长度,你的类应该只提供一个size
方法,它直接访问你的类在内部用来存储大小的任何方法。
与str*
函数不同,我建议的算法采用两个迭代器来划分代码单元序列;一个指向序列中的第一个元素,一个指向序列的最后一个元素之后的位置。 str*
函数只接受指向第一个元素的指针,然后假设序列继续,直到找到它的第一个零值代码单元。当您实现自己的模板化字符串类时,最好也不要使用显式的null终止约定,只需提供一个end()
方法,为您的字符串提供正确的结束点。
答案 1 :(得分:2)
您无法使用strcmp
,strcpy
或strlen
的原因是它们对字符串进行操作,其长度由终止零字节指示。由于您的字符串中可能包含零个字节,因此您无法使用这些函数。
我只想准确编码你想要的东西。你想要什么取决于你想要做什么。
答案 2 :(得分:1)
在UTF16中,您可能会在字符串中间看到等于'\0'
的字节; strcmp
,strcpy
和strlen
会为这样的字符串返回错误的结果,因为它们假设字符串是零终止的。
您可以使用STL中的copy,equal和distance来复制,比较和计算基于模板的迭代器的长度。