Effective STL 条款35

原创 2004年12月31日 01:25:00

条款35:通过mismatch或lexicographical比较实现简单的大小写无关字符串比较

一个STL菜鸟最常问的问题是“我怎么使用STL来进行大小写无关的字符串比较?”这是一个令人迷惑的简单问题。大小写无关字符串比较要么真的简单要么真的困难,依赖于你要多一般地解决这个问题。如果你忽略国际化问题而且只关注于设计成字符串strcmp那样的类型,这个任务很简单。如果你要有strcmp不具有的按语言处理字符串中的字符的能力(也就是,容纳文本的字符串是除了英语以外的语言)或程序使用一个locale而不是默认的,这个任务很困难。

在本条款,我会作出这个问题的一个简单版本,因为那足以演示STL怎么完成任务。(这个问题一个比较难的版本也能用STL解决。准确地说,它解决了你可以在附录A中看到的locale相关的问题。)为了让简单的问题变得更有挑战性,我会处理两次。想要使用大小写无关比较的程序员通常需要两种不同的调用接口,一种类似strcmp(返回一个负数、零或正数),另一种类似operator(返回true或false)。因此我会演示怎么使用STL算法实现两种调用接口。

但是首先,我们需要一种方法来确定两个字符除了大小写之外是否相等。当需要考虑国际化问题时,这是一复杂的问题。下面的字符比较显然是一个过分简单的解决方案,但它类似strcmp进行的字符串比较,因为本条款我只考虑类似strcmp的字符串比较,不考虑国际化问题,这个函数就够了:


这个函数遵循了strcmp,可以返回一个负数、零或正数,依赖于c1和c2之间的关系。与strcmp不同的是,ciCharCompare在进行比较前把两个参数转化为小写。这就产生了大小写无关的字符比较。

正如<cctype>(也是<ctype.h>)里的很多函数,tolower的参数和返回值类型是int,但除非这个int是EOF,它的值必须能表现为一个unsigned char。在C和C++中,char可能或可能不是有符号的(依赖于实现),当char有符号时,唯一确认它的值可以表现为unsigned char的方式是在调用tolower之前转换一下。这就解释了上面代码中的转换。(在char已经是无符号的实现中,这个转换没有作用。)这也解释了保存tolower返回值的是int而不是char。

给定了ciCharCompare,就很容易写出我们的第一个大小写无关的两个字符串比较函数,提供了一个类似strcmp的接口。ciStringCompare这个函数,返回一个负数、零或正数,依赖于要比较的字符串的关系。它基于mismatch算法,因为mismatch确定了两个区间中第一个对应的不相同的值的位置。

在我们可以调用mismatch前,我们必须先满足它的前提。特别是,我们必须确定一个字符串是否比另一个短,短的字符串作为第一个区间传递。因此我们可以把真正的工作放在一个叫做ciStringCompareImpl的函数,然后让ciStringCompare简单地确保传进去的参数顺序正确,如果参数交换了就调整返回值:


在ciStringCompareImpl中,大部分工作由mismatch来完成。它返回一对迭代器,表示了区间中第一个对应的字符不相同的位置:

幸运的是,注释把所有东西都弄清楚了。基本上,一旦你知道了字符串中第一个不同字符的位置,就可以很容易决定哪个字符串, 如果有的话,在另一个前面。唯一可能感到奇怪的是传给mismatch的判断式,也就是not2(ptr_fun(ciCharCompare))。当字符匹配时这个判断式返回true,因为当判断式返回false时mismatch会停止。我们不能为此使用ciCharCompare,因为它返回-1、1或0,而当字符匹配时它返回0,就像strcmp。如果我们把ciCharCompare作为判断式传给mismatch,C++会把ciCharCompare的返回类型转换为bool,而当然bool中零的等价物是false,正好和我们想要的相反!同样的,当ciCharCompare返回1或–1,那会被解释成true,因为,就像C,所有非零整数值都看作true。这再次和我们想要的相反。要修正这个语义倒置,我们在ciCharCompare前面放上not2和ptr_fun,而且我们都会一直很快乐地生活。

我们的第二个方法ciStringCompare是产生一个合适的STL判断式:可以在关联容器中用作比较函数的函数。这个实现很短也很好,因为我们需要做的是把ciCharCompare修改为一个有判断式接口的字符比较函数,然后把进行字符串比较的工作交给STL中名字第二长的算法——lexicographical_compare:

不,我不会让你再有悬念了。名字最长的算法是set_symmetric_difference。

如果你熟悉lexicographical_compare的行为,上面的代码在清楚不过了。如果你不,可能像隔着一块混凝土一样。幸运的是,要把混凝土换成玻璃并不难。

lexicographical_compare是strcmp的泛型版本。strcmp只对字符数组起作用,但lexicographical_compare对所有任何类型的值的区间都起作用。同时,strcmp总是比较两个字符来看看它们的关系是相等、小于或大于另一个。lexicographical_compare可以传入一个决定两个值是否满足一个用户定义标准的二元判断式。

在上面的调用中,lexicographical_compare用来寻找s1和s2第一个不同的位置,基于调用ciCharLess的结果。如果,使用那个位置的字符,ciCharLess返回true,lexicographical_compare也是;如果,在第一个字符不同的位置,从第一个字符串来的字符先于对应的来自第二个字符串的字符,第一个字符串就先于第二个。就像strcmp,lexicographical_compare认为两个相等值的区间是相等的,因此它对于这样的两个区间返回false:第一个区间不在第二个之前。也像strcmp,如果第一个区间在发现不同的对应值之前就结束了,lexicographical_compare返回true:一个先于任何区间的前缀是一个前缀。

谈了很多关于mismatch和lexicographical_compare。虽然我专注于本书的轻便性,但如果我没有提到大小写无关字符串比较函数作为对标准C库的非标准扩展而广泛存在,我可能是玩忽职守的。它们一般有stricmp或strcmpi这样的名字,而且它们一般和我们在本条款开发的函数一样没有提供更多对国际化的支持。如果你想牺牲一些移植性,你知道你的字符串没有包含嵌入的null,而且你不关心国际化,你可以找到实现一个大小写无关字符串比较最简单的方式完全不同STL。取而代之的是,它把两个字符串都转换为const char*指针(参见条款16),然后对指针使用stricmp或strcmpi:


有的人可能称此为hack,但stricmp/strcmpi被优化为只做一件事情,对长字符串运行起来一般比通用的算法mismatch和lexicographical_compare快得多。如果那对你很重要,你可能不在乎你用非标准C函数完成标准STL算法。有时候最高效地使用STL的方法是认识到其他方法更好。

《Effective C++》:条款35:考虑virtual函数以外的其他选择

virtual函数在派生中经常用到,在遇到一些问题时用virtual函数没问题,但是有时候我们应该思考一下是否有替代方案,以此来拓宽我们的视野。...
  • KangRoger
  • KangRoger
  • 2015年03月02日 21:12
  • 2061

effective stl 第19条:理解相等(equality)和等价(equivalence)的区别

#include #include #includeusing namespace std;bool ciStringCompare(const string l, const string r) {...
  • u014110320
  • u014110320
  • 2016年09月20日 23:36
  • 251

Effective STL条款17-条款18

条款17:使用交换技巧来修正过剩容量本节条款告诉我们,如果你有一个vector的容器,容器的容量是10000,但是,现在只用了1,那么为了节省内存,我们应该只保留使用的vector容量,多余的容量应该...
  • u011058765
  • u011058765
  • 2016年04月21日 09:10
  • 290

《Effective C++》学习笔记——条款35

条款 35:考虑virtual函数以外的其他选择
  • lx417147512
  • lx417147512
  • 2016年02月22日 00:29
  • 653

Effective STL学习笔记-条款19

条款19 了解相等和等价的区别了解相等和等价的区别例如find函数,或者一个set容器插入一个值得时候都会进行比较。但是它们的行为是不同的,find是通过 operator==,而set::inser...
  • gx864102252
  • gx864102252
  • 2017年08月27日 21:01
  • 105

Effective Modern C++ 条款35 比起基于线程编程,更偏爱基于任务编程

Effective Modern C++ 条款35
  • big_yellow_duck
  • big_yellow_duck
  • 2016年09月11日 16:06
  • 996

Effective STL 中文版(完整版)

 Winter总算找到《Effective STL》的完整中文版了,奉献给大家。书中作者解释了怎样结合STL组件来在库的设计得到最大的好处。这样的信息允许你对简单、直接的问题开发简单、直接的解决方案,...
  • WinterTree
  • WinterTree
  • 2005年01月16日 01:23
  • 16314

《Effective C++》:条款28-条款29

条款28避免返回handles指向对象内部成分:指的是不能返回对象内部数据/函数的引用、指针等。 条款29为异常安全而努力是值得的:指的是要有异常处理机制,避免发生异常时造成资源泄露等问题。...
  • KangRoger
  • KangRoger
  • 2015年02月19日 19:47
  • 1394

Effective C++ 条款2

尽量以const、enum、inline替换#define首先,大家要明白一个道理。#define是什么,有什么作用。很简单,大家都知道#define实现宏定义,如下代码:#define Flag 1...
  • u011058765
  • u011058765
  • 2015年06月19日 12:06
  • 499

Effective STL- 熟悉非标准的散列容器(hash 容器)

 条款25:熟悉非标准散列容器STL程序员一般用不了多久就开始惊讶,“vector、list、map,很好,但是散列(hash)表在哪里"?唉,在标准C++库里没有任何散列表。 每个人都同意这是个不幸...
  • bichenggui
  • bichenggui
  • 2009年10月21日 22:21
  • 1914
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Effective STL 条款35
举报原因:
原因补充:

(最多只允许输入30个字)