ITPub博客

首页 > 应用开发 > IT综合 > Guru of the Week 条款29:不区分大小写的string (转)

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

原创 IT综合 作者:worldblog 时间:2007-12-13 12:07:55 0 删除 编辑
Guru of the Week 条款29:不区分大小写的string (转)[@more@]

GotW#29 不区分大小写的string (Case-Insensitive Strings)XML:namespace prefix = o ns = "urn:schemas-microsoft-com:Office:office" />

难度: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 string;

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

  template

  class traits = char_traits,

  class Allocator = allocator >

  class basic_string;

所以,“string”实际上是“basic_string, allocator >”。我们不必操心分配器(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

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

  {

  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 ci_string;

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

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

 

习题

1.这样从char_traits继承出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

  basic_ostream&

  operator<<(basic_ostream& os,

  const basic_string& str);

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

ANSWER: Notice first that cout is actually a basic_ostream >. 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, which isn't what cout is even though ci_char_traits inherits from char_traits 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();


来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/10752043/viewspace-992761/,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论
  • 博文量
    6241
  • 访问量
    2450072