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

原创 2002年08月05日 10:17:00

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<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();

不区分大小写查找字符串

引用     上次发表了自己编写不区分大小查找的方法,这次使用了String对象中自带的方法regionMatches(boolean ignoreCase, int toffset, Stri...
  • cyp331203
  • cyp331203
  • 2014年12月09日 21:52
  • 1291

windows c++ 比较字符串 不区分大小写

char *strstr( const char *str, const char *strSearch ); // C only char *strstr( char *str,...
  • is2120
  • is2120
  • 2014年04月18日 17:40
  • 4228

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

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

Java 中replaceAll忽略大小写

原文:http://hw1287789687.iteye.com/blog/2150897 Java 中replaceAll如何忽略大小写呢? 方式一:在正则表达式前面添加(?i)...
  • u012109105
  • u012109105
  • 2015年07月22日 15:19
  • 1511

C++_字符串匹配_忽略大小写_方法

在我们平时的学习和工作中,我们经常需要对字符串进行各种比较,例如,忽略大小写比较,精确比较等。但目前 C++标准库并没有为string提供这样的方法,从而使我们不能方便的比较。所以碰到这种问题一般是自...
  • u012400327
  • u012400327
  • 2014年12月14日 00:50
  • 2486

C#不区分大小写的字符串替换(Replace)函数

http://www.cnblogs.com/zwl12549/archive/2009/06/11/1501442.html 在.NET中,不调用C++/CLI,进行字符串替换有好...
  • stableboy
  • stableboy
  • 2016年04月17日 10:14
  • 2787

SQL SERVER 筛选时区分大小写的语法

Demo: --SELECT * FROM 表名 WHERE 列名 COLLATE Chinese_PRC_CS_AS ='筛选字符' --SELECT * FROM 表名 WHERE 列名 C...
  • qq285679784
  • qq285679784
  • 2017年04月06日 17:27
  • 1342

linux下解决mysql区分大小写问题

本文介绍了linux下mysql区分大小写的解决方法,添加lower_case_table_names=1,表示mysql不区分大小写,这段代码必须在[mysqld_safe]之前。 ...
  • u011391839
  • u011391839
  • 2015年05月13日 17:14
  • 2334

mysql数据库设置不区分大小写

用惯了windows下面的不区分大小写的mysql语句,到了linux下面还真的很多不习惯。 在MySQL 中,数据库和表对就于那些目录下的目录和文件。因而,操作系统的敏感性决定数据库和表命名的大小...
  • u014263805
  • u014263805
  • 2015年02月03日 19:50
  • 884

MYSQL 大小写区分说明

1、linux下mysql安装完后是默认:区分表名的大小写,不区分列名的大小写; 2、用root帐号登录后,在/etc/my.cnf 或my.ini中的[mysqld]后添加添加lower_c...
  • Stubborn_Cow
  • Stubborn_Cow
  • 2015年08月13日 01:15
  • 755
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Guru of the Week 条款29:不区分大小写的string
举报原因:
原因补充:

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