⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 appendix_a.html

📁 制作本书的目的是为了方便大家的阅读。转载时请保持本电子书的完整性。 前言、条款2、16、21、44根据从Addison-Wesley出版社下载的开放条款翻译。条款26、27、28、45根据从Sc
💻 HTML
📖 第 1 页 / 共 2 页
字号:
<?xml version="1.0" encoding="gb2312"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><title>附录A:区域设置和忽略大小写的字符串比较</title><link href="css/all.css" rel="stylesheet" type="text/css" /><meta http-equiv="Content-Type" content="text/html; charset=gb2312" /></head><body><h1>区域设置和忽略大小写的字符串比较</h1><p><a href="item_35.html">条款35</a>解释了怎样使用mismatch和lexicographical_comapre实现忽略大小写的字符串比较,但是它也指出一个真正通用的解决方案必须考虑区域设置。本书是关于STL的,而非国际化的,因此我几乎没有提到任何关于区域设置的东西。不过,Matt Austern,《Generic Programming and the STL》<a href="bibliography.html#bi04">[4]</a>的作者,在2000年5月《C++ Report》<a href="bibliography.html#bi11">[11]</a>的专栏里提到了涉及区域设置的忽略大小写字符串比较。为了完整地讲述这个重要的主题,我很高兴能在这里再版他的专栏,而且我感谢Matt和101communications准许我这么做。</p><h2 style="text-align:center"> 如何进行忽略大小写的字符串比较<br />Matt Austern</h2><p>如果你写的程序曾经用到过string(谁没有吗?),有时候可能你需要处理两个除了大小写不同其他都相同的字符串。即,你需要让比较——相等、小于、子串匹配、排序——都忽略大小写。而且,的确,关于标准C++库的最常见问题之一是怎样使string忽略大小写。这个问题已经被回答很多次了。很多答案是错误的。</p><p>首先,让我们放弃试图写忽略大小写string类的想法。是的,它在技术上多多少少是可能的。标准库类型std::string其实只是一个模板std::basic_string&lt;char, std::char_traits&lt;char&gt;, std::allocator&lt;char&gt; &gt;的别名。所有的比较都使用了特性参数,所以,通过提供一个正确地重定义了相等和小于的特性参数的方法,你可以实例化basic_string,使&lt;和==操作符是忽略大小写的。你可以这么做,但没必要那么麻烦。</p><ul>	<li><em>你将不能做I/O</em>,至少不能再没有很多痛苦的情况下进行。标准库里的I/O类,比如std::basic_istream和std::basic_ostream,与std::basic_string一样在字符类型和特性上模板化。(再次强调,std::ostream只是一个std::basic_ostream&lt;char, char_traits&lt;char&gt; &gt;的别名。)特性参数必须匹配。如果你对字符串使用std::basic_string&lt;char, my_traits_class&gt;,你就必须对流使用std::basic_ostream&lt;char, my_traits_class&gt;。你不能使用比如cin和cout那样普通的流对象。</li>	<li><em>忽略大小写不涉及一个对象,而涉及你怎样使用一个对象。</em>你可能在一些情况下非常需要把一个string当作关注大小写而在其他情况下忽略大小写。(或许取决于用户控制的选项。)为这两种应用定义不同的类型是在它们之间放置人造障碍。</li>	<li><em>它并不合适</em>。正如所有的特性类<sup><a id="Note1Back" href="#Note1">[1]</a></sup>,char_traits是小的、简单的和无状态的。我们可以在本专栏的后面看到,正确的忽略大小写比较不是这样的东西。</li>	<li><em>它不够</em>。即使所有basic_string本身的成员函数都忽略大小写,当你需要使用非成员泛型算法比如std::search和std::find_end时,将仍然没有帮助。如果你决定,出于效率的考虑,从basic_string对象的容器改为字符串表,它也将没有帮助。</li></ul><p>更自然地融合入标准库设计的更好的解决方案是在当你需要时才进行忽略大小写的比较。不要为string::find_first_of和string::rfind那样的成员函数而烦恼;它们的功能都存在于非成员泛型算法。同时,泛型算法灵活得足以适应忽略大小写的字符串。例如,如果你需要以忽略大小写的顺序排序一个字符串的集合,你需要做的就是提供适当的比较函数对象:</p><pre>std::sort(C.begin(), C.end(), compare_without_case); </pre><p>本专栏的剩余部分将致力于怎样写那个函数对象。</p><h3>第一次尝试</h3><p>有不止一种方法来按字母顺序排列单词。下次你在书店时,注意作者的名字是怎么安排的:Mary McCarthy在Bernard Malamud之前,还是之后?(这是习惯的问题,而且这两种方式我都看到过。)但是,字符串比较的最简单方式是我们都在小学学过的那个:词典或者“字典顺序”比较,我们从一个一个字符的比较建立了字符串比较。</p><p>词典比较可能不适合专业应用(不是唯一的方法;库可能以不同的方式排序人名和地名),但它适合大部分情况,而且这是字符串比较在C++里的默认意思。字符串是字符的序列,如果x和y的类型是std::string,表达式x &lt; y等价于这个表达式</p><pre>std::lexicographical_compare(x.begin(), x.end(), y.begin(), y.end()).</pre><p>在这个表达式中,lexicographical_compare使用operator&lt;比较单个字符,但还有一个版本的lexicographical_compare让你选择自己的比较字符的方法。那个版本带有五个实参,而不是四个;最后一个实参是一个函数对象,确定两个字符哪个应该在另一个之前的二元判定式。然后,为了用<code>lexicographical_compare</code>进行忽略大小写比较,我们需要把它和忽略大小写的字符比较函数对象结合起来。</p><p>在字符的忽略大小写的比较后面的一般的想法是把两个字符都转化成大写字母然后比较结果。这里把那个想法显而易见的转换成一个C++函数对象,使用了来自标准C库的一个广为人知的函数:</p><pre>struct lt_nocase	: public std::binary_function&lt;char, char, bool&gt; {	bool operator()(char x, char y) const { 		return std::toupper(static_cast&lt;unsigned char&gt;(x)) &lt;				std::toupper(static_cast&lt;unsigned char&gt;(y));	}};</pre><p>“任何复杂问题都有一个简单、整洁而且错误的解决方案。”写关于C++的书的人都喜欢这个类,因为它是一个好的、简单的例子。 我像其他人一样心虚;我在我的书中多次使用了它。它几乎是正确的,但是不够好。问题是微妙的。</p><p>这里有一个你可以开始发现问题的例子:</p><pre>int main(){	const char* s1 = &quot;GEW\334RZTRAMINER&quot;;	const char* s2 = &quot;gew\374rztraminer&quot;;	printf(&quot;s1 = %s, s2 = %s\n&quot;, s1, s2);	printf(&quot;sl &lt; s2: %s\n&quot;,		std::lexicographical_compare(s1, s1 + 14, s2, s2 + 14, lt_nocase())		? &quot;true&quot; :&quot;false&quot;);}</pre><p>你应该在你的系统上试试看。在我的系统上(一台运行IRIX 6.5的Silicon Graphics O2),这是输出:</p><pre>s1 = GEW&Uuml;RZTRAMINER, s2 = gewürztraminers1&lt; s2: true</pre><p>噢,多古怪。如果你做忽略大小写比较,难道“gewürztraminer”和“GEW&Uuml;RZTRAMINER”不同吗?现在做一个轻微的变化:如果你在printf语句之前插入这行</p><pre>setlocale(LC_ALL, &quot;de&quot;);</pre><p>,突然输出改变了:</p><pre>s1 = GEW&Uuml;RZTRAMINER, s2 = gewürztraminers1 &lt; s2: false</pre><p>忽略大小写的字符串比较比看起来更复杂。这表面上正确的程序非常依赖于大多数人经常忽略的东西:区域设置。</p><h3>区域设置</h3><p>一个char真的无异于一个小的整数。我们可以选择把一个小的整数解释成一个字符,但这种解释并不通用。一些特定的数字应该被解释为一个字母、一个标点符号还是一个不能打印的控制字符?</p><p>没有一个正确的答案,而且直到关系到C和C++语言核心之前它们没有任何不同。需要靠一些库函数产生那些区别:例如,isalpha确定了一个字符是否是字母,toupper把小写字母转换成大写而对大写字母或不是字母的字符则什么都不做。所有那些都取决于本地文化和语言习惯:字母和非字母之间的区别在英语中是一个意思,在瑞典语则是另一个意思。从小写到大写的转换在罗马和斯拉夫字母表中表示不同的东西,而在希伯来语中则没有任何意义。</p><p>默认情况下,字符操作函数适用于简单的英语文字字符集。字符'\374'不受toupper影响,因为它不是一个字母;在一些系统上打印时它可能看起来像ü,但那和操作英语文字的C库程序不相干。在ASCII字符集里没有ü字符。这行</p><pre>setlocale(LC_ALL, &quot;de&quot;);</pre><p>告诉C库开始根据德语习惯操作。(至少它在IRIX上是那样。区域的名字没有标准化。)德语中有字符ü,因此toupper把ü改为&Uuml;。</p><p>如果这还不使你紧张,那么马上就会。虽然toupper可能看起来像带有一个实参的简单函数,但它也依赖于一个全局变量——不好,一个隐藏的全局变量。这引发了所有常见的困难:使用toupper的函数潜在地依赖于整个程序中的任何一个其他函数。</p><p>如果你把toupper用于忽略大小写的字符串比较,这可能是灾难性的。如果你有一个依赖于有序list的算法(比如binary_search),然后一个新的区域设置引发了它后面的排序顺序的改变,那将发生什么?像这样的代码不可复用:只是勉强可用。你不能在库里使用它——库可以用于任何种类的程序,不只是从未调用setlocale的程序。你可能在一个大的程序里使用它却侥幸逃过一劫,但你将有一个维护问题:或许你能证明没有其他模块调用了setlocale,但你能证明在程序明年的版本里没有其他模块调用setlocale吗?</p><p>这个问题在C里没有好的解决方案。C库只有一个全局的区域设置,没别的了。在C++里有一个解决方案。</p><h3>C++中的区域设置</h3><p>C++标准库里的区域设置不是深深地埋藏在库实现里的全局数据。它是一个std::locale类型的对象,而且你可以建立它和把它传给函数,就像其他任何对象一样。例如,你要建立一个表示通常区域的区域设置对象可以这么写</p><pre>std::locale L = std::locale::classic();</pre><p>或者你通过这么写建立一个德语区域设置</p><pre>std::locale L(&quot;de&quot;);</pre><p>(和C库里的一样,区域的名字没有标准化。检查你的实现文档来查明提供了哪些有名字的区域设置。)</p><p>C++里的区域设置分成多个<dfn>方面</dfn>(facet),每个方面处理一个国际化的不同方向,而函数std::use_facet从区域设置对象<sup><a id="Note2Back" href="#Note2">[2]</a></sup>中提取一个特定的方面。ctype方面处理字符分类,包括大小写转换。最后,如果c1和c2是char类型,这段代码将以适合区域设置L并以忽略大小写的方式比较它们。</p><pre>const std::ctype&lt;char&gt;&amp; ct = std::use_facet&lt;std::ctype&lt;char&gt; &gt;(L);bool result = ct.toupper(c1) &lt; ct.toupper(c2);</pre><p>也有一个特别的缩写词:你可以写</p>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -