newbie一个,对STL没什么了解。下面这个是看着string类的代码顺藤摸瓜写出来的,可能很幼稚,请大家指点。(贴完这个可能有端时间不能来了,忙)
The Solution of Case_Insensitive String By bankrock
1. What does “case-insensitive” mean?
Case-insensitive字面上的意思是大小写无关。在以英语编写的程序中这应该很好理解,但如汉语等其他一些语言是不分大小写的,可能在一些程序设计语言中,大小写也不一定是不同的(比如basic)。这里只讨论英语环境下case-insensitive类的设计。
2. Write a case_insensitive String class.
若是不要求与STL兼容,完全可以自己设计一个类来实现ci_string这个case_insensitive类。但考虑STL的通用性和大量理论实践验证后的有效性,改写string类来实现似乎才是有实用价值的做法。
最主要的是在operator==的操作符重载函数里,加入实现case_insensitive的关键代码。
对STL中string函数的改写实现方法。
这是书上所提供的方法,也是兼容性较好的一种方法(不过从后面的item3里看来还是在模板推演方面有点问题)
现看看string类在STL中的定义:
typedef basic_string<char> string;
可以看出string实际上是个模板类basic_string<char>的别名
而STL中模板类basic_string中的operator==比较函数如下:
代码:
// operator ==
template<typename _CharT, typename _Traits, typename _Alloc>
inline bool
operator==(const basic_string<_CharT, _Traits, _Alloc>& __lhs,
const basic_string<_CharT, _Traits, _Alloc>& __rhs)
{ return __lhs.compare(__rhs) == 0; }
template<typename _CharT, typename _Traits, typename _Alloc>
inline bool
operator==(const _CharT* __lhs,
const basic_string<_CharT, _Traits, _Alloc>& __rhs)
{ return __rhs.compare(__lhs) == 0; }
template<typename _CharT, typename _Traits, typename _Alloc>
inline bool
operator==(const basic_string<_CharT, _Traits, _Alloc>& __lhs,
const _CharT* __rhs)
{ return __lhs.compare(__rhs) == 0; }
其中都用到一个compare()函数,而参数是一个basic_string类对象或是个_CharT指针,
(下面以实参为一个basic_stirng类对象的compare函数为例)在定义basic_string的头文件里可以找到符合这样的basic_string的compare()成员函数:
代码:
int compare(const basic_string& __str) const
{
size_type __size = this->size();
size_type __osize = __str.size();
size_type __len = min(__size, __osize);
int __r = traits_type::compare(_M_data(), __str.data(), __len);
if (!__r)
__r = __size - __osize;
return __r;
}
这里调用了traits_type的compare函数,而trait_type是由类定义中
typedef _Traits traits_type;
设置的别名。而compare的三个实参数中的第一个和第二个由一连串的关系可以得出其均指向该对象中储存在一个名为_Alloc_hider的struct中的_M_p,即真正的数据。该struct定义如下:
代码:
struct _Alloc_hider : _Alloc
{
_Alloc_hider(_CharT* __dat, const _Alloc& __a)
: _Alloc(__a), _M_p(__dat) { }
_CharT* _M_p; // The actual data.
};
由此我们终于知道实现string类中的数据比较的函数就是这个traits_type::compare(),而要实现string类中的case-insensitive的相等的比较应该改写traits_type中的compare函数,使其符合case-insensitive的要求。
再回过来看string类中的traits_type到底是什么类:
由于有如下定义:
代码:
template<class charT,
class traits = char_traits<charT>,
class Allocator = allocator<charT> >
class basic_string;
(这个缺省类型是抄书的,实在是找不到)
可知string类用的taits_type是char_traits<char>因此要改写这个类里的compare函数,而char_traits<char>是类char_traits<_CharT>的一个特化类。其中的compare函数定义如下:
代码:
static int
compare(const char_type* __s1, const char_type* __s2, size_t __n)
{ return memcmp(__s1, __s2, __n); }
而memcmp有如下定义:
代码:
int memcmp(
const void *buf1,
const void *buf2,
size_t count
);
函数返回值<0时buf1<buf2,=0时buf1=buf2,>0时buf1>buf2。
这就是直接实现数据比较的函数了。
至此可知只要用能实现case-insensitive的函数替换掉memcmp即可达到目的。
正好有个函数可以满足要求:
代码:
Compares characters in two buffers (case-insensitive).
int _memicmp(
const void *buf1,
const void *buf2,
size_t count
);
(这个和书上说的memicmp有点不同,函数名前多了了个“_”,好像都行)
至此这个operator==的case-insensitive形式宣布改造完成。
其他的几个函数也要改写,就是找到原来类中函数,在继承类中改写和大小有关的处理部分。
一个完整的实际程序如下:
代码:
#include<iostream>
#include<string>
using namespace std;
struct ci_char_traits : public char_traits<char>
{
static bool eq( 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 n >= 0 ? s : 0;
}
};
typedef basic_string<char,ci_char_traits> ci_string;
int main()
{
ci_string s("tttR");
if(s=="TTtr")
cout<<"right"<<endl;
system("pause");
return 0;
}
3. Is making case sensitivity a property of the object a good idea?
我觉得这完全要看实际情况来作出决定,不同的应用程序里肯定有不同的实现方法。比如说一个搜索程序,默认状况下应该时case-insensitive的,否则可能错过用户想要的内容;而一个打字训练的程序,就应该有case sensitive的特性,这里要求精确性明显应该比搜索程序高。
但程序也应该有一定的柔韧性。比如搜索程序可能有必要为用户提供一个“区分搜索文件的大小写”的选择,这种情况下,就需要调入case sensitive特性,这样上面的实现方法就有了用武之地。