分享
 
 
 

Guru of the Week 条款29:不区分大小写的string

王朝vc·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

GotW#29 不区分大小写的string (Case-Insensitive Strings)

难度:7/10

你期望一个不分大小写的字符串类型吗?你的使命是,应该选个现成的并接受它,还是自己写一个。

问题

写一个不分大小写的字符串类型,它其它方面都与标准库中的“string”类相同,只是在大小写区分上和(非标的,但被广泛使用的)C函数stricmp():

ci_string s( "AbCdE" );

// case insensitive

assert( s == "abcde" );

assert( s == "ABCDE" );

// still case-preserving, of course

assert( strcmp( s.c_str(), "AbCdE" ) == 0 );

assert( strcmp( s.c_str(), "abcde" ) != 0 );

解决方案

写一个不分大小写的字符串类型,它其它方面都与标准库中的“string”类相同,只是在大小写区分上和(非标的,但被广泛使用的)C函数stricmp():

“怎么实现一个不分大小写的字符串类型”这个问题是如此常见,以致于它需要一份专有的FAQ--所以才在GotW中讨论它。

注意1:stricmp()这个不区分大小写的字符串比较函数不是C标准的一部分,但它为很多C编译器扩展提供。

注意2:“不区分大小写”的实际含义完全取决于你的程序和国家语言。例如,很多语言根本就没有大小写;但即使如此,你仍然需要决策重读和非重读字符是否等价,诸如此类。

下面是我们期望达到的目标:本GotW指导了如何为标准string类实现“不区分大小写”,无论你处在什么语境下。

ci_string s( "AbCdE" );

// case insensitive

assert( s == "abcde" );

assert( s == "ABCDE" );

// still case-preserving, of course

assert( strcmp( s.c_str(), "AbCdE" ) == 0 );

assert( strcmp( s.c_str(), "abcde" ) != 0 );

关键点是领会“string类”在标准C++中到底是什么。如果你看一下string的头文件,你将看到如下的东西:

typedef basic_string<char> string;

所以,string并不是一个真正的类,它是一个模板的(特化的)typedef。再向下,basic_string<>模板申明如下,这是其全貌:

template<class charT,

class traits = char_traits<charT>,

class Allocator = allocator<charT> >

class basic_string;

所以,“string”实际上是“basic_string<char, char_traits<char>, allocator<char> >”。我们不必操心分配器(allocator)部分,关键点是char_traits部分,它决定了字符的相互作用和比较运算(!运算)。

basic_string提供了常用的比较函数以比较两个string对象是否相等,或一个小于另一个,等等。这些string类的比较函数是建立在char_traits模板提供的字符比较函数基础上的。具体一点,char_traits模板提供了如下的字符比较函数:eq()(相等)、ne()(不等)、lt()(小于)、compare()(比较字符序列)、find()(搜索字符序列)。

如果你希望在(string的)这些操作上有不同的行为,我们所要做的只是提供一个不同的char_traits模板。这是最容易的方法:

struct ci_char_traits : public char_traits<char>

// 继承为了得到我们不必过载的函数

{

static bool eq( char c1, char c2 )

{ return toupper(c1) == toupper(c2); }

static bool ne( char c1, char c2 )

{ return toupper(c1) != toupper(c2); }

static bool lt( char c1, char c2 )

{ return toupper(c1) < toupper(c2); }

static int compare( const char* s1,

const char* s2,

size_t n ) {

return memicmp( s1, s2, n );

// 如果你的编译器提供了它,

// 不然你就得自己实现一个。

}

static const char*

find( const char* s, int n, char a ) {

while( n-- > 0 && toupper(*s) != toupper(a) ) {

++s;

}

return s;

}

};

最后将它们合在一起:

typedef basic_string<char, ci_char_traits> ci_string;

我们重定义了一个“ci_string”,它的操作非常象标准的“string”,只是它用ci_char_traits代替了char_traits<char>以使用特别的比较规则。我们只不过将ci_char_traits的规则实现为“不区分大小写”,就使得ci_string大动手脚就表现为“不区分大小写”了--也就是说,我们根本没有碰basic_string就有了一个“不区分大小写”的string!

这次的GotW揭示了basic_string模板的工作原理以及实现使用上的灵活性。如果你期望不用上面的memicmp()和toupper()实现的这些比较函数,只需要用你自己的代码替换这5个函数,怎么满足你自己的程序的需求,就怎么实现它们。

习题

1.这样从char_traits<char>继承出ci_char_traits安全吗?为什么安全或为什么不安全?

2. 为什么下面的代码编译不通过?

(WQ注,由于C++的改进,此代码已经可以编译通过,并正常运行!)

ci_string s = "abc";

cout << s << endl;

提示1:参见GotW #19。

提示2:摘自21.3.7.9 [lib.string.io],basic_string的operator<<操作申明如下(是个偏特化):

template<class charT, class traits, class Allocator>

basic_ostream<charT, traits>&

operator<<(basic_ostream<charT, traits>& os,

const basic_string<charT,traits,Allocator>& str);

(WQ注,C++标准库中,现在已将“basic_ostream<charT, traits>& os”改为“ostream&”,所以没问题了。)

ANSWER: Notice first that cout is actually a basic_ostream<char, char_traits<char> >. Then we see the problem: operator<< for basic_string is templated and all, but it's only specified for insertion into a basic_ostream with the same 'char type' and 'traits type' as the string. That is, the standard operator<< will let you output a ci_string to a basic_ostream<char, ci_char_traits>, which isn't what cout is even though ci_char_traits inherits from char_traits<char> in the above solution.

(由于已错误,不译。)

有两个解决办法:定义ci_strings自己的流入/流出函数,或使用“.c_str()”:

cout << s.c_str() << endl;

3. 当在标准string对象和ci_string对象间使用其它操作(如+、+=、=)时,发生什么?例如:

string a = "aaa";

ci_string b = "bbb";

string c = a + b;

答案:还是,定义ci_string自己的这些操作,或使用“.c_str()”:

string c = a + b.c_str();

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有