??xml version="1.0" encoding="utf-8" standalone="yes"?>久久综合给合久久狠狠狠97色,久久综合伊人77777麻豆,99久久无码一区人妻a黑http://www.shnenglu.com/eXile/archive/2009/11/02/99995.htmleXileeXileMon, 02 Nov 2009 09:38:00 GMThttp://www.shnenglu.com/eXile/archive/2009/11/02/99995.htmlhttp://www.shnenglu.com/eXile/comments/99995.htmlhttp://www.shnenglu.com/eXile/archive/2009/11/02/99995.html#Feedback2http://www.shnenglu.com/eXile/comments/commentRss/99995.htmlhttp://www.shnenglu.com/eXile/services/trackbacks/99995.html   shared_ptr是新的标准库的一个主要成员,作ؓ一个非嵌入式的指针Q其设计可谓已经是绞脑汁。当Ӟq有很多人对它提Z不满。没有完的设计Q只有合适的设计?br>
1. shared_ptr最大的特点是接口的单性与实现的灵zL?br>    对于shared_ptr<Object>,object的内存管理是可定制的Q甚臛_以定制引用计数结点的内存分配Q以满对内存有Ҏ要求的情c而这一切,都被Object的实现者隐藏v来,使用Object的客L是不用关心的。这和以前标准库的组件实现策略有些不同。比如说, vector<int, A1>和vector<int, A2>Q由于内存分配策略的不同Q而变成类型的不同Q造成接口的改变?/span>q一点在shared_ptr的设计时被避免了Q当然以一定的性能代h。shared_ptr作ؓC++面向对象设计的一个重要组Ӟ接口的简单性是很重要的Q必要?/span>接口和实现的分离。与此相似的q有tr1::function的设计?br>
2.在同一体系中,各种cd的智能指针可以互相{换?br>
   如下例:

struct Object : InterfaceA, InterfaceB {
    MemberA memberA;
};


shared_ptr
<Object> obj(new Object);
shared_ptr
<InterfaceA> a = obj;
shared_ptr
<InterfaceB> b = obj;
shared_ptr<Object> p = static_pointer_cast<Object>(b);
shared_ptr<void> p2 = obj;


  甚至q可以取得数据成员的指针Q?br>
shared_ptr<Object> obj(new Object);
shared_ptr
<MemberA> memberA(obj, &obj->memberA);


  再来说说shared_ptr的缺炏V?br>

1.对于使用引用计数的智能指针来_必须要小心出现@环引用?br>     在重度?/span>shared_ptr的系l中Q你必须一开始就明确cMcȝ关系Q以军_哪里使用shared_ptrQ哪里?/span>weak_ptrQ否则就会出现内存泄霌Ӏ?span style="WIDOWS: 2; TEXT-TRANSFORM: none; TEXT-INDENT: 0px; BORDER-COLLAPSE: separate; FONT: medium Simsun; WHITE-SPACE: normal; ORPHANS: 2; LETTER-SPACING: normal; COLOR: #000000; WORD-SPACING: 0px" class=Apple-style-span>shared_ptr的接?/span>转换的灵zL,也很ҎD指针被滥用?/span>内存自动理的问题ƈ没有得到解决Q它只是被{UM?br>
2.shared_ptr使用非嵌入式设计Q这样可以用于基本cdQ比?shared_ptr<int>。但是根据个人经验,q种情况在很用。大部分情况q是使用自己设计的类。这有一个问题,是没有很方便的办法实现this指针?/span>指针的{换。标准库中提供了enable_shared_from_thiscL解决q个问题。但q?/span>已经使所谓的非嵌入式设计徒有虚名。而假如一开始采?span style="WIDOWS: 2; TEXT-TRANSFORM: none; TEXT-INDENT: 0px; BORDER-COLLAPSE: separate; FONT: medium Simsun; WHITE-SPACE: normal; ORPHANS: 2; LETTER-SPACING: normal; COLOR: #000000; WORD-SPACING: 0px" class=Apple-style-span>嵌入式设计的话,则在性能代h和多U程设计斚wh更大的灵zL?/span>



eXile 2009-11-02 17:38 发表评论
]]>
TDD for GUIhttp://www.shnenglu.com/eXile/archive/2009/06/22/88271.htmleXileeXileMon, 22 Jun 2009 05:15:00 GMThttp://www.shnenglu.com/eXile/archive/2009/06/22/88271.htmlhttp://www.shnenglu.com/eXile/comments/88271.htmlhttp://www.shnenglu.com/eXile/archive/2009/06/22/88271.html#Feedback0http://www.shnenglu.com/eXile/comments/commentRss/88271.htmlhttp://www.shnenglu.com/eXile/services/trackbacks/88271.html阅读全文

eXile 2009-06-22 13:15 发表评论
]]>
(Z)正则表达?0分钟入门教程http://www.shnenglu.com/eXile/archive/2009/01/15/72092.htmleXileeXileThu, 15 Jan 2009 06:58:00 GMThttp://www.shnenglu.com/eXile/archive/2009/01/15/72092.htmlhttp://www.shnenglu.com/eXile/comments/72092.htmlhttp://www.shnenglu.com/eXile/archive/2009/01/15/72092.html#Feedback0http://www.shnenglu.com/eXile/comments/commentRss/72092.htmlhttp://www.shnenglu.com/eXile/services/trackbacks/72092.html

正则表达?0分钟入门教程

版本Qv2.3 (2008-4-13) 作者:deerchao 转蝲h?a >来源

目录

跌目录

  1. 本文目标
  2. 如何使用本教E?/font>
  3. 正则表达式到底是什么东西?
  4. 入门
  5. 试正则表达?/font>
  6. 元字W?/font>
  7. 字符转义
  8. 重复
  9. 字符c?/font>
  10. 分枝条g
  11. 反义
  12. 分组
  13. 后向引用
  14. 零宽断言
  15. 负向零宽断言
  16. 注释
  17. 贪婪与懒?/font>
  18. 处理选项
  19. ql?递归匚w
  20. q有些什么东西没提到
  21. 联系作?/font>
  22. 最?来点q告...
  23. |上的资源及本文参考文?/font>
  24. 更新说明

本文目标

30分钟内让你明白正则表辑ּ是什么,q对它有一些基本的了解Q让你可以在自己的程序或|页里用它?/p>

如何使用本教E?/h2>

最重要的是——请l我30分钟Q如果你没有使用正则表达式的l验Q请不要试图?0U?/em>内入门——除非你是超?:)

别被下面那些复杂的表辑ּ吓倒,只要跟着我一步一步来Q你会发现正则表辑ּ其实q?span lang="zh-cn">没有你想? 中的那么困难。当Ӟ如果你看完了q篇教程之后Q发现自己明白了很多Q却又几乎什么都C得,那也是很正常的——我认ؓQ没接触q正则表辑ּ的h在看完这 教E后Q能把提到过的语法记?0%以上的可能性ؓ零。这里只是让你明白基本的原理Q以后你q需要多l习Q多使用Q才能熟l掌握正则表辑ּ?/p>

除了作ؓ入门教程之外Q本文还试图成ؓ可以在日常工作中使用的正则表辑ּ语法参考手册。就作者本人的l历来说Q这个目标还是完成得不错的——你看,我自׃没能把所有的东西C来,不是吗?

清除格式 文本格式U定Q?span class="name">专业术语 元字W?语法格式 正则表达?/span> 正则表达式中的一部分(用于分析) 对其q行匚w的源字符?/span> Ҏ则表辑ּ或其中一部分的说?/span>

隐藏Ҏ 本文双有一些注释,主要是用来提供一些相关信息,或者给没有E序员背景的读者解释一些基本概念,通常可以忽略?/p>

正则表达式到底是什么东西?

字符是计机软g处理文字时最基本的单位,可能是字母,数字Q标点符PI格Q换行符Q汉字等{?span class="name">字符?/span>?个或更多个字W的序列?span class="name">文本也就是文字,字符丌Ӏ说某个字符?span class="name">匚w某个正则表达式,通常是指q个字符串里有一部分Q或几部分分别)能满辑ּl出的条件?/p>

在编写处理字W串的程序或|页Ӟl常会有查找W合某些复杂规则的字W串的需要?span class="name">正则表达?/span>是用于描述q些规则的工兗换句话_正则表达式就是记录文本规则的代码?/p>

很可能你使用qWindows/Dos下用于文件查扄通配W?wildcard)Q也是*?span class="code">?。如果你x找某个目录下的所有的Word文档的话Q你会搜?span style="color: red;">*.doc。在q里Q?span class="code">*会被解释成Q意的字符丌Ӏ和通配W类|正则表达式也是用来进行文本匹配的工具Q只不过比v通配W,它能更精地描述你的需求——当Ӟ代h是更复杂——比如你可以~写一个正则表辑ּQ用来查?span class="desc">所有以0开_后面跟着2-3个数字,然后是一个连字号“-”Q最后是7?位数字的字符?/span>(?span class="string">010-12345678?span class="string">0376-7654321)?/p>

入门

学习正则表达式的最好方法是从例子开始,理解例子之后再自己对例子q行修改Q实验。下面给Z不少单的例子Qƈ对它们作了详l的说明?/p>

假设你在一英文小说里查找hiQ你可以使用正则表达?span class="regex">hi?/p>

q几乎是最单的正则表达式了Q它可以_匚wq样的字W串Q?span class="desc">׃个字W组成,前一个字W是h,后一个是i。通常Q处理正则表辑ּ的工具会提供一个忽略大写的选项Q如果选中了这个选项Q它可以匚whi,HI,Hi,hIq四U情况中的Q意一U?/p>

不幸的是Q很多单词里包含hiq两个连l的字符Q比?span class="string">him,history,high{等。用hi来查扄话,q里边的hi也会被找出来。如果要_地查找hiq个单词的话Q我们应该?span class="regex">\bhi\b?/p>

\b是正则表辑ּ规定的一个特D代码(好吧Q某些h叫它元字W,metacharacterQ,代表着单词的开头或l尾Q也是单词的分界处。虽焉常英文的单词是q|标点W号或者换行来分隔的,但是\bq不匚wq些单词分隔字符中的M一个,?strong>只匹配一个位|?/strong>?/p>

如果需要更_的说法,\b匚wq样的位|:它的前一个字W和后一个字W不全是(一个是,一个不是或不存?\w?/p>

假如你要扄?span class="desc">hi后面不远处跟着一个LucyQ你应该?span class="regex">\bhi\b.*\bLucy\b?/p>

q里Q?span class="part">.是另一个元字符Q匹?span class="desc">除了换行W以外的L字符?span class="part">*同样是元字符Q不q它代表的不是字W,也不是位|,而是数量——它指定*前边的内容可以连l重复出CQ意次以整个表达式得到匹?/span>。因此,.*q在一起就意味着L数量的不包含换行的字W?/span>。现?span class="regex">\bhi\b.*\bLucy\b的意思就很明显了Q?span class="desc">先是一个单词hi,然后是Q意个L字符(但不能是换行)Q最后是Lucyq个单词?/p>

换行W就?\n',ASCII~码?0(十六q制0x0A)的字W?/p>

如果同时使用其它元字W,我们p构造出功能更强大的正则表达式。比如下面这个例子:

0\d\d-\d\d\d\d\d\d\d\d匚wq样的字W串Q?span class="desc">?开_然后是两个数字,然后是一个连字号“-”Q最后是8个数?/span>(也就是中国的电话L。当Ӟq个例子只能匚w区号?位的情Ş)?/p>

q里?span class="part">\d是个新的元字W,匚w一位数?0Q或1Q或2Q或……)?span class="part">-不是元字W,只匹配它本n——连字符或者减受?/p>

Z避免那么多烦人的重复Q我们也可以q样写这个表辑ּQ?span class="regex">0\d{2}-\d{8}?q里\d后面?span class="part">{2}({8})的意思是前面\d必须q箋重复匚w2?8??/p>

试正则表达?/h2>

如果你不觉得正则表达式很难读写的话,要么你是一个天才,要么Q你不是地球人。正则表辑ּ的语法很令h头疼Q即使对l常使用它的人来说也是如此。由于难于读写,Ҏ出错Q所以找一U工具对正则表达式进行测试是很有必要的?/p>

׃在不同的环境下正则表辑ּ的一些细节是不相同的Q本教程介绍的是微Y .Net Framework 2.0下正则表辑ּ的行为,所以,我向你介l一?Net下的工具Regex Tester。首先你保已经安装?a title="转到下蝲.Net Framework 2.0的页?>.Net Framework 2.0Q然?a title="从www.unibetter.com下蝲Regex Tester, 75KB">下蝲Regex Tester。这是个l色软gQ下载完后打开压羃?直接q行RegexTester.exe可以了?/p>

下面是Regex Testerq行时的截图Q?/p>

Regex Testerq行时的截图

元字W?/h2>

现在你已l知道几个很有用的元字符了,?span class="code">\b,.,*Q还?span class="code">\d.正则表达式里q有更多的元字符Q比?span class="code">\s匚wL的空白符Q包括空|制表W?Tab)Q换行符Q中文全角空格等?span class="code">\w匚w字母或数字或下划U或汉字{?/span>?/p>

对中?汉字的特D处理是?Net提供的正则表辑ּ引擎支持的,其它环境下的具体情况h看相x?/p>

下面来看看更多的例子Q?/p>

\ba\w*\b匚w以字?span class="part">a开头的单词——先是某个单词开始处(\b)Q然后是字母a,然后是Q意数量的字母或数?\w*)Q最后是单词l束?\b)?/p>

好吧Q现在我们说说正则表辑ּ里的单词是什么意思吧Q就是多于一个的q箋?span class="code">\w。不错,q与学习英文时要背的成千上万个同名的东西的确关系不大 :)

\d+匚w1个或更多q箋的数?/span>。这里的+是和*cM的元字符Q不同的?span class="code">*匚w重复L?可能??Q?span class="code">+则匹?span class="desc">重复1ơ或更多?/span>?/p>

\b\w{6}\b 匚w刚好6个字?数字的单?/span>?/p>
?.常用的元字符
代码 说明
. 匚w除换行符以外的Q意字W?/span>
\w 匚w字母或数字或下划U或汉字
\s 匚wL的空白符
\d 匚w数字
\b 匚w单词的开始或l束
^ 匚w字符串的开?/span>
$ 匚w字符串的l束

元字W?span class="code">^Q和数字6在同一个键位上的符P?span class="code">$都匹配一个位|,q和\b有点cM?span class="code">^匚w你要用来查找的字W串的开_$匚wl尾。这两个代码在验证输入的内容旉常有用,比如一个网站如果要求你填写的QQ号必Mؓ5位到12位数字时Q可以用:^\d{5,12}$?/p>

q里?span class="part">{5,12}和前面介l过?span class="part">{2}是类似的Q只不过{2}匚w只能不多不少重复2?/span>Q?span class="part">{5,12}则是重复的次C能少?ơ,不能多于12?/span>Q否则都不匹配?/p>

因ؓ使用?span class="part">^?span class="part">$Q所以输入的整个字符串都要用来和\d{5,12}来匹配,也就是说整个输入必须??2个数?/span>Q因此如果输入的QQ可匚wq个正则表达式的话,那就W合要求了?/p>

和忽略大写的选项cMQ有些正则表辑ּ处理工具q有一个处理多行的选项。如果选中了这个选项Q?span class="code">^?span class="code">$的意义就变成?span class="desc">匚w行的开始处和结束处?/p>

字符转义

如果你想查找元字W本w的话,比如你查?span class="desc">.,或?span class="desc">*,出C问题Q你没办法指定它们,因ؓ它们会被解释成别的意思。这时你得使用\来取消这些字W的Ҏ意义。因此,你应该?span class="regex">\.?span class="regex">\*。当Ӟ要查?span class="desc">\本nQ你也得?span class="regex">\\.

例如Q?span class="regex">unibetter\.com匚wunibetter.comQ?span class="regex">C:\\Windows匚wC:\Windows?/p>

重复

你已l看q了前面?span class="code">*,+,{2},{5,12}q几个匹配重复的方式了。下面是正则表达式中所有的限定W?指定数量的代码,例如*,{5,12}{?Q?/p>
?.常用的限定符
代码/语法 说明
* 重复零次或更多次
+ 重复一ơ或更多?/span>
? 重复零次或一?/span>
{n} 重复n?/span>
{n,} 重复nơ或更多?/span>
{n,m} 重复n到m?/span>

下面是一些用重复的例子Q?/p>

Windows\d+匚wWindows后面?个或更多数字

^\w+匚w一行的W一个单?或整个字W串的第一个单词,具体匚w哪个意思得看选项讄)

字符c?/h2>

要想查找数字Q字母或数字Q空白是很简单的Q因为已l有了对应这些字W集合的元字W,但是如果你想匚w没有预定义元字符的字W集?比如元音字母a,e,i,o,u),应该怎么办?

很简单,你只需要在Ҏ号里列出它们p了,?span class="regex">[aeiou]匹?span class="desc">M一个英文元韛_?/span>Q?span class="regex">[.?!]匚w标点W号(.??)?/p>

我们也可以轻村֜指定一个字W?span class="name">范围Q像[0-9]代表的含意与\d是完全一致的Q?span class="desc">一位数?/span>Q同?span class="regex">[a-z0-9A-Z_]也完全等同于\wQ如果只考虑英文的话Q?/p>

下面是一个更复杂的表辑ּQ?span class="regex">\(?0\d{2}[) -]?\d{8}?/p>

“(”?#8220;)”也是元字W,后面?a >分组?/font>里会提到Q所以在q里需要?a >转义?/p>

q个表达式可以匹?span class="desc">几种格式的电话号?/span>Q像(010)88886666Q或022-22334455Q或02912345678{。我们对它进行一些分析吧Q首先是一个{义字W?span class="part">\(,它能出现0ơ或1??),然后是一?span class="part">0Q后面跟着2个数?\d{2})Q然后是)?span class="part">-?span class="part">I格中的一个,它出?ơ或不出??)Q最后是8个数?\d{8})?/p>

分枝条g

不幸的是Q刚才那个表辑ּ也能匚w010)12345678?span class="string">(022-87654321q样?#8220;不正?#8221;的格式。要解决q个问题Q我们需要用?span class="name">分枝条g。正则表辑ּ里的分枝条g指的是有几种规则Q如果满_中Q意一U规则都应该当成匚wQ具体方法是?span class="code">|把不同的规则分隔开。听不明白?没关p,看例子:

0\d{2}-\d{8}|0\d{3}-\d{7}q个表达式能匚w两种以连字号分隔的电话号码:一U是三位区号Q?位本地号(?10-12345678)Q一U是4位区P7位本地号(0376-2233445)?/p>

\(0\d{2}\)[- ]?\d{8}|0\d{2}[- ]?\d{8}q个表达?span class="desc">匚w3位区L电话LQ其中区号可以用括hhQ也可以不用Q区号与本地号间可以用连字号或空格间隔,也可以没有间?/span>。你可以试试用分枝条件把q个表达式扩展成也支?位区L?/p>

\d{5}-\d{4}|\d{5}q个表达式用于匹配美国的邮政~码。美国邮~的规则?位数字,或者用q字号间隔的9位数字。之所以要l出q个例子是因为它能说明一个问题:使用分枝条gӞ要注意各个条件的序。如果你把它Ҏ\d{5}|\d{5}-\d{4}的话Q那么就只会匚w5位的邮编(以及9位邮~的??。原因是匚w分枝条gӞ会从左到右地测试每个条Ӟ如果满了某个分枝的话,׃会去再管其它的条件了?/p>

分组

我们已经提到了怎么重复单个字符Q直接在字符后面加上限定W就行了Q;但如果想要重复多个字W又该怎么办?你可以用括h指定子表辑ּ(也叫?span class="name">分组)Q然后你可以指定这个子表达式的重复ơ数了,你也可以对子表达式进行其它一些操?后面会有介绍)?/p>

(\d{1,3}\.){3}\d{1,3}是一?span class="desc">单的IP地址匚w表达式。要理解q个表达式,h下列序分析它:\d{1,3}匚w1?位的数字Q?span class="part">(\d{1,3}\.){3}匚w三位数字加上一个英文句?q个整体也就是这?span class="name">分组)重复3?/span>Q最后再加上一个一C位的数字(\d{1,3})?/p>

IP地址中每个数字都不能大于255Q大家千万不要被?4》第三季的编剧给忽悠?..

不幸的是Q它也将匚w256.300.888.999q种不可能存在的IP地址。如果能使用术比较的话Q或许能单地解决q个问题Q但是正则表辑ּ中ƈ不提供关于数学的M功能Q所以只能用冗长的分组Q选择Q字W类来描qC个正的IP地址Q?span class="regex">((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)?/p>

理解q个表达式的关键是理?span class="part">2[0-4]\d|25[0-5]|[01]?\d\d?Q这里我׃l说了,你自己应该能分析得出来它的意义?/p>

反义

有时需要查找不属于某个能简单定义的字符cȝ字符。比如想查找除了数字以外Q其它Q意字W都行的情况Q这旉要用?span class="name">反义Q?/p>
?.常用的反义代?/caption>
代码/语法 说明
\W 匚wL不是字母Q数字,下划U,汉字的字W?/span>
\S 匚wL不是I白W的字符
\D 匚wL非数字的字符
\B 匚w不是单词开头或l束的位|?/span>
[^x] 匚w除了x以外的Q意字W?/span>
[^aeiou] 匚w除了aeiouq几个字母以外的L字符

例子Q?span class="regex">\S+匚w不包含空白符的字W串?/p>

<a[^>]+>匚w用尖括号括v来的以a开头的字符?/span>?/p>

后向引用

使用括h定一个子表达式后Q?strong>匚wq个子表辑ּ的文?/strong>(也就是此分组捕获的内?可以在表辑ּ或其它程序中作进一步的处理。默认情况下Q每个分l会自动拥有一?span class="name">l号Q规则是Q从左向叻I以分l的左括号ؓ标志Q第一个出现的分组的组号ؓ1Q第二个?Q以此类推?/p>

后向引用用于重复搜烦前面某个分组匚w的文本。例如,\1代表分组1匚w的文?/span>。难以理解?LCZQ?/p>

\b(\w+)\b\s+\1\b可以用来匚w重复的单?/span>Q像go go, 或?span class="string">kitty kitty。这个表辑ּ首先?span class="desc">一个单?/span>Q也是单词开始处和结束处之间的多于一个的字母或数?/span>(\b(\w+)\b)Q这个单词会被捕获到~号?的分l中Q然后是1个或几个I白W?/span>(\s+)Q最后是分组1中捕L内容Q也是前面匚w的那个单词)(\1)?/p>

你也可以自己指定子表辑ּ?span class="name">l名。要指定一个子表达式的l名Q请使用q样的语法:(?<Word>\w+)(或者把括h?span class="code">'也行Q?span class="code">(?'Word'\w+)),q样把\w+的组名指定ؓWord了。要反向引用q个分组捕获的内容,你可以?span class="code">\k<Word>,所以上一个例子也可以写成q样Q?span class="regex">\b(?<Word>\w+)\b\s+\k<Word>\b?/p>

使用括L时候,q有很多特定用途的语法。下面列Z最常用的一些:

?.常用分组语法
分类 代码/语法 说明
捕获 (exp) 匚wexp,q捕h本到自动命名的组?/span>
(?<name>exp) 匚wexp,q捕h本到名称为name的组里,也可以写??'name'exp)
(?:exp) 匚wexp,不捕获匹配的文本Q也不给此分l分配组?/span>
零宽断言 (?=exp) 匚wexp前面的位|?/span>
(?<=exp) 匚wexp后面的位|?/span>
(?!exp) 匚w后面跟的不是exp的位|?/span>
(?<!exp) 匚w前面不是exp的位|?/span>
注释 (?#comment) q种cd的分l不Ҏ则表辑ּ的处理生Q何媄响,用于提供注释让h阅读

我们已经讨论了前两种语法。第三个(?:exp)不会改变正则表达式的处理方式Q只是这Ll匹配的内容不会像前两种那样被捕获到某个l里面,也不会拥有组?/span>?/p>

零宽断言

地球人,是不是觉得这些术语名U太复杂Q太难记了?我也和你一栗知道有q么一U东西就行了Q它叫什么,随它dQ?#8220;无名Q万物之?..”

接下来的四个用于查找在某些内?但ƈ不包括这些内?之前或之后的东西Q也是说它们像\b,^,$那样用于指定一个位|,q个位置应该满一定的条g(xa)Q因此它们也被称?span class="name">零宽断言。最好还是拿例子来说明吧Q?/p>

断言用来声明一个应该ؓ真的事实。正则表辑ּ中只有当断言为真时才会l进行匹配?/p>

(?=exp)也叫零宽度正预测先行断言Q它断言自n出现的位|的后面能匹配表辑ּexp。比?span class="regex">\b\w+(?=ing\b)Q匹?span class="desc">以ingl尾的单词的前面部分(除了ing以外的部?Q如查找I'm singing while you're dancing.Ӟ它会匚wsing?span class="desc">danc?/p>

(?<=exp)也叫零宽度正回顾后发断言Q它断言自n出现的位|的前面能匹配表辑ּexp。比?span class="regex">(?<=\bre)\w+\b会匹?span class="desc">以re开头的单词的后半部?除了re以外的部?Q例如在查找reading a bookӞ它匹?span class="desc">ading?/p>

假如你想要给一个很长的数字中每三位间加一个逗号(当然是从双加v?Q你可以q样查找需要在前面和里面添加逗号的部分:((?<=\d)\d{3})*\bQ用它对1234567890q行查找时结果是234567890?/p>

下面q个例子同时使用了这两种断言Q?span class="regex">(?<=\s)\d+(?=\s)匚w以空白符间隔的数?再次Q不包括q些I白W??/p>

负向零宽断言

前面我们提到q怎么查找不是某个字符或不在某个字W类?/strong>的字W的Ҏ(反义)。但是如果我们只是想?strong>保某个字符没有出现Q但q不惛_匚w?/strong>时怎么办?例如Q如果我们想查找q样的单?-它里面出C字母q,但是q后面跟的不是字母u,我们可以试q样Q?/p>

\b\w*q[^u]\w*\b匚w包含后面不是字母u的字母q的单?/span>。但是如果多做测?或者你思维_敏锐Q直接就观察出来?Q你会发玎ͼ如果q出现在单词的l尾的话Q像Iraq,BenqQ这个表辑ּ׃出错。这是因?span class="part">[^u]总要匚w一个字W,所以如果q是单词的最后一个字W的话,后面?span class="part">[^u]会匚wq后面的单词分隔符(可能是空|或者是句号或其它的什?Q后面的\w*\b会匚w下一个单词,于是\b\w*q[^u]\w*\bp匚w整个Iraq fighting?span class="name">负向零宽断言能解册L问题Q因为它只匹配一个位|,q不消费M字符。现在,我们可以q样来解册个问题:\b\w*q(?!u)\w*\b?/p>

零宽度负预测先行断言(?!exp)Q?span class="desc">断言此位|的后面不能匚w表达式exp。例如:\d{3}(?!\d)匚w三位数字Q而且q三位数字的后面不能是数?/span>Q?span class="regex">\b((?!abc)\w)+\b匚w不包含连l字W串abc的单?/span>?/p>

同理Q我们可以用(?<!exp),零宽度正回顾后发断言?span class="desc">断言此位|的前面不能匚w表达式expQ?span class="regex">(?<![a-z])\d{7}匚w前面不是写字母的七位数?/span>?/p>

误l分析表辑ּ(?<=<(\w+)>).*(?=<\/\1>)Q这个表辑ּ最能表现零宽断a的真正用途?/p>

一个更复杂的例子:(?<=<(\w+)>).*(?=<\/\1>)匚w不包含属性的单HTML标签内里的内?/span>?span class="code">(<?(\w+)>)指定了这L前缀Q?span class="desc">被尖括号括v来的单词(比如可能?lt;b>)Q然后是.*(L的字W串),最后是一?span class="name">后缀(?=<\/\1>)。注意后~里的\/Q它用到了前面提q的字符转义Q?span class="part">\1则是一个反向引用,引用的正?span class="desc">捕获的第一l?/span>Q前面的(\w+)匚w的内容,q样如果前缀实际上是<b>的话Q后~是</b>了。整个表辑ּ匚w的是<b>?lt;/b>之间的内?再次提醒Q不包括前缀和后~本n)?/p>

注释

括L另一U用途是通过语法(?#comment)来包含注释。例如:2[0-4]\d(?#200-249)|25[0-5](?#250-255)|[01]?\d\d?(?#0-199)?/p>

要包含注释的话,最好是启用“忽略模式里的I白W?#8221;选项Q这样在~写表达式时能Q意的dI格QTabQ换行,而实际用时q些都将被忽略。启用这个选项后,?后面到这一行结束的所有文本都被当成注释忽略掉。例如,我们可以前面的一个表辑ּ写成q样Q?/p>

      (?<=    # 断言要匹配的文本的前~
<(\w+)> # 查找括hh的字母或数字(即HTML/XML标签)
) # 前缀l束
.* # 匚wL文本
(?= # 断言要匹配的文本的后~
<\/\1> # 查找括hh的内容:前面是一?/"Q后面是先前捕获的标{?br> ) # 后缀l束

贪婪与懒?/h2>

当正则表辑ּ中包含能接受重复的限定符Ӟ通常的行为是Q在使整个表辑ּ能得到匹配的前提下)匚w可能多的字W。考虑q个表达式:a.*bQ它会匚w最长的以a开始,以bl束的字W串。如果用它来搜烦aabab的话Q它会匹配整个字W串aabab。这被称?span class="name">贪婪匚w?/p>

有时Q我们更需?span class="name">懒惰匚wQ也是匚w可能少的字W。前面给出的限定W都可以被{化ؓ懒惰匚w模式Q只要在它后面加上一个问?span class="code">?。这?span class="regex">.*?意味着匚wL数量的重复,但是在能使整个匹配成功的前提下用最的重复。现在看看懒惰版的例子吧Q?/p>

a.*?b匚w最短的Q以a开始,以bl束的字W串。如果把它应用于aabab的话Q它会匹?span class="desc">aabQ第一到第三个字符Q?/span>?span class="desc">abQ第四到W五个字W)?/p>

Z么第一个匹配是aabQ第一到第三个字符Q而不是abQ第二到W三个字W)Q简单地_因ؓ正则表达式有另一条规则,比懒惎ͼ贪婪规则的优先更高Q最先开始的匚w拥有最高的优先权——The match that begins earliest wins?/p>
?.懒惰限定W?/caption>
代码/语法 说明
*? 重复Lơ,但尽可能重?/span>
+? 重复1ơ或更多ơ,但尽可能重?/span>
?? 重复0ơ或1ơ,但尽可能重?/span>
{n,m}? 重复n到mơ,但尽可能重?/span>
{n,}? 重复nơ以上,但尽可能重?/span>

处理选项

在C#中,你可以?a title="MSDN 相关文">Regex(String, RegexOptions)构造函?/font>来设|正则表辑ּ的处理选项。如QRegex regex = new Regex("\ba\w{6}\b", RegexOptions.IgnoreCase);

上面介绍了几个选项如忽略大写Q处理多行等Q这些选项能用来改变处理正则表辑ּ的方式。下面是.Net中常用的正则表达式选项Q?/p>
?.常用的处理选项
名称 说明
IgnoreCase(忽略大小? 匚w时不区分大小写?/td>
Multiline(多行模式) 更改^?span class="code">$的含义,使它们分别在L一行的行首和行֌配,而不仅仅在整个字W串的开头和l尾匚w?在此模式?$的精含意是:匚w\n之前的位|以及字W串l束前的位置.)
Singleline(单行模式) 更改.的含义,使它与每一个字W匹配(包括换行W\nQ?
IgnorePatternWhitespace(忽略I白) 忽略表达式中的非转义I白q启用由#标记的注释?/td>
RightToLeft(从右向左查找) 匚w从右向左而不是从左向双行?/td>
ExplicitCapture(昑ּ捕获) 仅捕获已被显式命名的l?/td>
ECMAScript(JavaScript兼容模式) 使表辑ּ的行Z它在JavaScript里的行ؓ一致?/td>

一个经常被问到的问题是Q是不是只能同时使用多行模式和单行模式中的一U?{案是:不是。这两个选项之间没有M关系Q除了它们的名字比较怼Q以至于让h感到疑惑Q以外?/p>

ql?递归匚w

q里介绍的^衡组语法是由.Net Framework支持的;其它语言Q库不一定支持这U功能,或者支持此功能但需要用不同的语法?/p>

有时我们需要匹配像( 100 * ( 50 + 15 ) )q样的可嵌套的层ơ性结?/span>Q这时简单地使用\(.+\)则只会匹配到最左边的左括号和最双的右括号之间的内?q里我们讨论的是贪婪模式Q懒惰模式也有下面的问题)。假如原来的字符串里的左括号和右括号出现的次C相等Q比?span class="string">( 5 / ( 3 + 2 ) ) )Q那我们的匹配结果里两者的个数也不会相{。有没有办法在这L字符串里匚w到最长的Q配对的括号之间的内容呢Q?/p>

Z避免(?span class="code">\(把你的大脑彻底搞p涂Q我们还是用括号代替圆括号吧。现在我们的问题变成了如何把xx <aa <bbb> <bbb> aa> yyq样的字W串里,最长的配对的尖括号内的内容捕获出来Q?/p>

q里需要用C下的语法构造:

  • (?'group') 把捕L内容命名为group,q压?span class="name">堆栈(Stack)
  • (?'-group') 从堆栈上弹出最后压入堆栈的名ؓgroup的捕获内容,如果堆栈本来为空Q则本分l的匚wp|
  • (?(group)yes|no) 如果堆栈上存在以名ؓgroup的捕获内容的话,l箋匚wyes部分的表辑ּQ否则l匹配no部分
  • (?!) 零宽负向先行断言Q由于没有后~表达式,试图匚wLp|

如果你不是一个程序员Q或者你自称E序员但是不知道堆栈是什么东西)Q你pL解上面的三种语法吧:W一个就是在黑板 上写一?group"Q第二个是从黑板上擦掉一?group"Q第三个是看黑板上写的q有没有"group"Q如果有ql匹配yes部分Q否? 匹配no部分?/p>

我们需要做的是每碰C左括P在压入一?Open",每碰C个右括号Q就弹出一个,C最后就看看堆栈是否为空Q-如果不ؓI那p明左括号比右括号多,那匹配就应该p|。正则表辑ּ引擎会进行回?攑ּ最前面或最后面的一些字W?Q尽量整个表达式得到匹配?/p>

<                         #最外层的左括号
[^<>]* #最外层的左括号后面的不是括L内容
(
(
(?'Open'<) #到了左括号Q在黑板上写一?Open"
[^<>]* #匚w左括号后面的不是括号的内?br> )+
(
(?'-Open'>) #到了右括号Q擦掉一?Open"
[^<>]* #匚wx号后面不是括L内容
)+
)*
(?(Open)(?!)) #在遇到最外层的右括号前面Q判断黑板上q有没有没擦掉的"Open"Q如果还有,则匹配失?br>> #最外层的右括号

ql的一个最常见的应用就是匹配HTML,下面q个例子可以匚w嵌套?lt;div>标签Q?span class="regex"><div[^>]*>[^<>]*(((?'Open'<div[^>]*>)[^<>]*)+((?'-Open'</div>)[^<>]*)+)*(?(Open)(?!))</div>.

q有些什么东西没提到

我已l描qC构造正则表辑ּ的大量元素,q有一些我没有提到的东ѝ下面是未提到的元素的列表,包含语法和简单的说明。你可以在网上找到更详细的参 考资料来学习它们--当你需要用到它们的时候。如果你安装了MSDN Library,你也可以在里面找到关?net下正则表辑ּ详细的文?/p>
?.未详细讨论的语?/caption>
代码/语法 说明
\a 报警字符(打印它的效果是电脑嘀一?
\b 通常是单词分界位|,但如果在字符c里使用代表退?/span>
\t 制表W,Tab
\r 回R
\v 竖向制表W?/span>
\f 换页W?/span>
\n 换行W?/span>
\e Escape
\0nn ASCII代码中八q制代码为nn的字W?/span>
\xnn ASCII代码中十六进制代码ؓnn的字W?/span>
\unnnn Unicode代码中十六进制代码ؓnnnn的字W?/span>
\cN ASCII控制字符。比如\cC代表Ctrl+C
\A 字符串开?cM^Q但不受处理多行选项的媄?
\Z 字符串结或行尾(不受处理多行选项的媄?
\z 字符串结?cM$Q但不受处理多行选项的媄?
\G 当前搜烦的开?/span>
\p{name} Unicode中命名ؓname的字W类Q例如\p{IsGreek}
(?>exp) 贪婪子表辑ּ
(?<x>-<y>exp) ql?/span>
(?im-nsx:exp) 在子表达式exp中改变处理选项
(?im-nsx) 辑ּ后面的部分改变处理选项
(?(exp)yes|no) 把exp当作零宽正向先行断言Q如果在q个位置能匹配,使用yes作ؓ此组的表辑ּQ否则用no
(?(exp)yes) 同上Q只是用空表达式作为no
(?(name)yes|no) 如果命名为name的组捕获C内容Q用yes作ؓ表达式;否则使用no
(?(name)yes) 同上Q只是用空表达式作为no

联系作?/h2>

好吧,我承?我骗了你,dq里你肯定花了不?0分钟.怿?q是我的?而不是因Z太笨.我之所以说"30分钟",是ؓ了让你有信心,有耐心l箋下去.既然你看Cq里,那证明我的阴谋成功了.被忽悠的感觉很爽吧?

要投诉我,或者觉得我其实可以做得更好,或者有M其它问题,Ƣ迎?a >我的博客让我知道.



eXile 2009-01-15 14:58 发表评论
]]>U真IP数据库格式详?zt)http://www.shnenglu.com/eXile/archive/2008/07/20/56679.htmleXileeXileSun, 20 Jul 2008 05:46:00 GMThttp://www.shnenglu.com/eXile/archive/2008/07/20/56679.htmlhttp://www.shnenglu.com/eXile/comments/56679.htmlhttp://www.shnenglu.com/eXile/archive/2008/07/20/56679.html#Feedback0http://www.shnenglu.com/eXile/comments/commentRss/56679.htmlhttp://www.shnenglu.com/eXile/services/trackbacks/56679.htmlhttp://lumaqq.linuxsir.org/article/qqwry_format_detail.html

U真IP数据库格式详?/h1>

摘要
|络上的IP数据库以U真版的最为流行,LumaQQ也采用了U真版IP数据库做为IP查询功能? 基础。不q关于其格式的文档却非常之少Q后来终于在|上扑ֈ了一份文,得以了解其内q,不过那䆾文档寥寥数语Q也是颇心才读明白。在q里我重写一 份,以此做ؓLumaQQ开发者文的一部分Q我惌是必要的。本文详l介l了U真IP数据库的格式Qƈ且给Z一些Demo以供参考?

Luma, 清华大学
修改日期Q?2005/01/14

Note: 在此感谢U真IP数据库作者金狐和那唯一一份文的作者?/p> 修改历史:
2005-01-14 修改了原来一些表达不清和错误的地?


自从有了IP数据库这U东西,QQ外挂的显CIP功能也随之而生Q本识颇H,是否q有其他应用不得而知Q不q,IP数据库确实是个不错的东西? 如今|络上最行的IP数据库我惛_该是U真版的Q说错了也不要扁我)Q迄今ؓ止其IP记录条数已经接近30000Q对于有些IP甚至能精到楼层Q不? 快哉?004q??月间Q正逢LumaQQ破土动工Qؓ了加上这个h人都喜欢Q但是好像h人都不知道ؓ什么喜Ƣ的显IP功能Q我也采用了U真版IP? 据库Q它的优Ҏ记录多,查询速度快,它只用一个文件QQWry.dat包含了所有记录,方便嵌入到其他程序中Q也方便升?/p>

基本l构

QQWry.dat文g在结构上分ؓ3块:文g_记录区,索引区。一般我们要查找IPӞ先在索引区查找记录偏U,然后再到记录Z息。由? 记录区的记录是不定长的,所以直接在记录Z搜烦是不可能的。由于记录数比较多,如果我们遍历索引Z会是有点慢的Q一般来_我们可以用二分查找法搜烦 索引区,光度比遍历烦引区快若q数量。图1是QQWry.dat的文件结构图?/p>



?. QQWry.dat文gl构

要注意的是,QQWry.dat里面全部采用了little-endian字节?/p>

一. 了解文g?/h3>

QQWry.dat的文件头只有8个字节,其结构非常简单,首四个字节是W一条烦引的l对偏移Q后四个字节是最后一条烦引的l对偏移?/p>

? 了解记录?/h3>

每条IP记录都由国家和地区名l成Q国家地区在q里q不是太切Q因为可能会查出?#8220;清华大学计算机系”之类的,q里清华大学成了国家名了,所? q个国家地区名和IP数据库制作的时候有关系。所以记录的格式有点像QNameQ有一个全局部分和局部部分组成,我们q里q是沿用国家名和地区名的说法?/p>

于是我们惌着一条记录的格式应该? [IP地址][国家名][地区名]Q当Ӟq个没有什么问题,但是q只是最单的情况。很昄Q国家名和地区名可能会有很多的重复,如果每条记录都保存一 个完整的名称拯是非怸理想的,所以我们就需要重定向以节省空间。所以ؓ了得C个国家名或者地区名Q我们就有了两个可能Q第一是直接的字W串表示? 国家名,W二是一?字节的结构,W一个字节表明了重定向的模式Q后?个字节是国家名或者地区名的实际偏UM|。对于国家名来说Q情况还可能更复? 些,因ؓq样的重定向最多可能有两次?/p>

那么什么是重定向模式?Ҏ上面所_一条记录的格式是[IP地址][国家记录][地区记录]Q如果国家记录是重定向的话,那么地区记录是有可能没有的,于是有了两U情况,我管他叫做模?和模?。我们对q些格式的情况D图说明:



?. IP记录的最单Ş?/center>

?表示了最单的IP记录格式Q我x有什么可以解释的



?. 重定向模?

?演示了重定向模式1的情c我们看到在模式1的情况下Q地录也跟着国家记录CQ在IP地址之后只剩下了国家记录?字节Q后?个字节构成了一个指针,指向了实际的国家名,然后又跟着地址名。模?的标识字节是0x01?/p>



?. 重定向模?

?演示了重定向模式2的情c我们看C在模?的情况下Q其标识字节?x02Q,地区记录没有跟着国家记录赎ͼ因此在国家记录之?个字节之 后还是有地区记录。我想你已经明白了模?和模?的区别,卻I模式1的国家记录后面不会再有地录,模式2的国家记录后会有地区记录。下面我们来看一 下更复杂的情c?/p>



?. 混和情况1

?演示了当国家记录为模?的时候可能出现的更复杂情况,在这U情况下Q重定向指向的位|仍然是个重定向Q不q第二次重定向ؓ模式2。大家不用担 心,没有模式3了,q个重定向也最多只有两ơ,q且如果发生了第二次重定向,则其一定ؓ模式2Q而且q种情况只会发生在国家记录上Q对于地录,模式1 和模?是一LQ地录也不会发生2ơ重定向。不q,q个图还可以更复杂,如图7Q?/p>



?. 混和情况2

?是模?下最复杂的؜和情况,不过我想应该也很好理解,只不q地录也来重定向而已Q有一Ҏ要提醒你Q如果重定向的地址?Q则表示未知的地区名?/p>

所以我们ȝ如下Q一条IP记录由[IP地址][国家记录][地区记录]l成Q对于国家记录,可以有三U表C方式:字符串Ş式,重定向模?和重? 向模?。对于地录,可以有两U表C方式:字符串Ş式和重定向,另外有一条规则:重定向模?的国家记录后不能跟地录。按照这个ȝQ在q些方式 中合理组合,构成了IP记录的所有可能情c?/p>

设计的理?/h3>

在我们l去了解索引区的l构之前Q我们先来了解一下ؓ何记录区的结构要如此设计。我想你可能惛_了答案:字符串重用。没错,在这U结构下Q对于一 个国家名和地区名Q我只需要保存其一ơ就可以了。我们D例说明,Z表示方便Q我们用写字母代表IP记录QC表示国家名,A表示地区名:

  1. 有两条记录a(C1, A1), b(C2, A2)Q如果C1 = C2, A1 = A2Q那么我们就可以使用?昄的结构来实现重用
  2. 有三条记录a(C1, A1), b(C2, A2), c(C3, A3)Q如果C1 = C2, A2 = A3Q现在我们想存储记录bQ那么我们可以用?的结构来实现重用
  3. 有两条记录a(C1, A1), b(C2, A2)Q如果C1 = C2Q现在我们想存储记录bQ那么我们可以采用模?表示C2Q用字符串表CA2

你可以D出更多的情况Q你也会发现在这U结构下Q不同的字符串只需要存储一ơ?/p>

了解索引?/h3>

?了解文g?部分Q我们说明了文g头实际上是两个指针,分别指向了第一条烦引和最后一条烦引的l对偏移。如?所C:



?. 文g头指向烦引区囄

实在是很单,不是吗?从文件头你就可以定位到烦引区Q然后你可以开始搜索IP了!每条索引长度?个字节,?个字节是起始IP地址Q后三个? 节就指向了IP记录。这里有些概念需要说明一下,什么是起始IPQ那么有没有l束IPQ?假设有这么一条记录:166.111.0.0 - 166.111.255.255Q那?66.111.0.0是起始IPQ?66.111.255.255是l束IPQ结束IP是IP记录中的那头 4个字节,q下你应该就清楚了吧。于是乎Q每条烦引配合一条记录,构成了一个IP范围Q如果你要查?66.111.138.138所在的位置Q你׃? ?66.111.138.138落在?66.111.0.0 - 166.111.255.255 q个范围内,那么你就可以着q条索引去读取国家和地区名了。那么我们给Z个最详细的图解吧Q?/p>



?. 文g详细l构

现在一切都清楚了是不是Q也许还有一点你不清楚,QQWry.dat的版本信息存在哪里呢Q? {案是:最后一条IP记录实际上就是版本信息,最后一条记录显C出来就是这P255.255.255.0 255.255.255.255 U真|络 2004q??5日IP数据。OKQ到现在你应该全部清楚了?/p>

Demo

下一步:我给Z个读取IP记录的程序片断,此片断摘录自LumaQQ源文件edu.tsinghua.lumaqq.IPSeeker.javaQ如果你有兴,可以下蝲源代码详l看看?/p>

 /**
* l定一个ip国家地区记录的偏U,q回一个IPLocationl构
* @param offset 国家记录的v始偏U?br> * @return IPLocation对象
*/
private IPLocation getIPLocation(long offset) {
try {
// 跌4字节ip
ipFile.seek(offset + 4);
// dW一个字节判断是否标志字?br> byte b = ipFile.readByte();
if(b == REDIRECT_MODE_1) {
// d国家偏移
long countryOffset = readLong3();
// 跌{臛_Ud
ipFile.seek(countryOffset);
// 再检查一ơ标志字节,因ؓq个时候这个地方仍然可能是个重定向
b = ipFile.readByte();
if(b == REDIRECT_MODE_2) {
loc.country = readString(readLong3());
ipFile.seek(countryOffset + 4);
} else
loc.country = readString(countryOffset);
// d地区标志
loc.area = readArea(ipFile.getFilePointer());
} else if(b == REDIRECT_MODE_2) {
loc.country = readString(readLong3());
loc.area = readArea(offset + 8);
} else {
loc.country = readString(ipFile.getFilePointer() - 1);
loc.area = readArea(ipFile.getFilePointer());
}
return loc;
} catch (IOException e) {
return null;
}
}

/**
* 从offset偏移开始解析后面的字节Q读Z个地区名
* @param offset 地区记录的v始偏U?br> * @return 地区名字W串
* @throws IOException 地区名字W串
*/
private String readArea(long offset) throws IOException {
ipFile.seek(offset);
byte b = ipFile.readByte();
if(b == REDIRECT_MODE_1 || b == REDIRECT_MODE_2) {
long areaOffset = readLong3(offset + 1);
if(areaOffset == 0)
return LumaQQ.getString("unknown.area");
else
return readString(areaOffset);
} else
return readString(offset);
}

/**
* 从offset位置d3个字节ؓ一个longQ因为java为big-endian格式Q所以没办法
* 用了q么一个函数来做{?br> * @param offset 整数的v始偏U?br> * @return d的long|q回-1表示d文gp|
*/
private long readLong3(long offset) {
long ret = 0;
try {
ipFile.seek(offset);
ipFile.readFully(b3);
ret |= (b3[0] & 0xFF);
ret |= ((b3[1] << 8) & 0xFF00);
ret |= ((b3[2] << 16) & 0xFF0000);
return ret;
} catch (IOException e) {
return -1;
}
}

/**
* 从当前位|读?个字节{换成long
* @return d的long|q回-1表示d文gp|
*/
private long readLong3() {
long ret = 0;
try {
ipFile.readFully(b3);
ret |= (b3[0] & 0xFF);
ret |= ((b3[1] << 8) & 0xFF00);
ret |= ((b3[2] << 16) & 0xFF0000);
return ret;
} catch (IOException e) {
return -1;
}
}

/**
* 从offset偏移处读取一个以0l束的字W串
* @param offset 字符串v始偏U?br> * @return d的字W串Q出错返回空字符?br> */
private String readString(long offset) {
try {
ipFile.seek(offset);
int i;
for(i = 0, buf[i] = ipFile.readByte(); buf[i] != 0; buf[++i] = ipFile.readByte());
if(i != 0)
return Utils.getString(buf, 0, i, "GBK");
} catch (IOException e) {
log.error(e.getMessage());
}
return "";
}

代码q不复杂QgetIPLocation是主要方法,它检查国家记录格式,q对字W串形式Q模?Q模?采用不同的代码,readArea则相对简单,因ؓ只有字符串和重定向两U情况需要处理?/p>

ȝ

U真IP数据库的l构使得查找IP单迅速,不过你想要编辑它却是比较ȝ的,我想应该需要专门的工具来生成QQWry.dat文gQ由于其文g格式的限Ӟ你要直接dIP记录׃Ҏ了。不q,能查到IP已经很开心了Q希望纯真记录越来越多~?/p>

eXile 2008-07-20 13:46 发表评论
]]>单元试[zt]http://www.shnenglu.com/eXile/archive/2008/04/29/48422.htmleXileeXileTue, 29 Apr 2008 05:39:00 GMThttp://www.shnenglu.com/eXile/archive/2008/04/29/48422.htmlhttp://www.shnenglu.com/eXile/comments/48422.htmlhttp://www.shnenglu.com/eXile/archive/2008/04/29/48422.html#Feedback0http://www.shnenglu.com/eXile/comments/commentRss/48422.htmlhttp://www.shnenglu.com/eXile/services/trackbacks/48422.html来源: WingFire On Toplanguange

1.单元试库要量地增加开发h员的负担。额外负担必d可能直白Q傻瓜化?br>市面上的许多讲到单元试的书都是以XUnit本的Q这DCppUnit的接受程度颇高。CppUnit中规中矩Q四q_EI但不够犀利。个为boost.test最单,只要一个BOOST_AUTO_TEST_CASE可以开始了。CppUnit则要复杂一点,而这U复杂性是多余的,甚至是有害的。用CppUnit的时候,我看到有Zؓ了共享测试代码,随便在test case里面加函敎ͼ然后复用Q结果导致case不独立。boost.test們֐于不要徏?h文gQ所以要复用不方便(或者,不习惯在Cpp中复用)Q反而不Ҏ犯错误?br>2.实施单元试Q必能够让E序员看得到好处q尽快受益。新目必须早引入单元试Q要早在正式~码之前?br>想立刻让UT变得完美是不可能的,行政命o也不会有好结果。在推行单元试的时候,教育很重要。必让同事能理解单元测试ؓ什么有效,如何工作QUT~写准则之类的问题。另外,在工作多q的E序员(对UT~Z认识的)中推行单元测试,d更大。更要注意教育和反馈。最好的反馈是帮助他们从单元测试中L。例如,修改更轻松,思维更面向接口,bug更少Q代码更Ҏ理解{等。作为推动者,有义务去d发现q些改善之处q积极地反馈l程序员。从而增强应用UT的信心和意愿?br>3.必须充分自动化?br>UT的Q务之一是给代码~织一层细密的保护|。程序员应该认识刎ͼ单元试是ؓ自己服务的,所以,我们要的是完成Q务而不是展C。能够自动地完成d则是最好的。如果单元测试过多地q扰E序员的正常思考,׃招致更多的抵触(抵触L存在的)或敷衍。敷衍是可怕的。我向来是把单元试的运行作为build的一个步骤的。成功的单元试不需要输ZQ何信息,最多在全部passs的时候给个OKp够了。图形界面的试工具在我看来也是鸡肋Q新手的玩具而已。图形界面既不利于参数化q行Q也不方便自动化Q实在是降低开发效率的杀手?br>4.不要q求完美的UT?br>不是所有东襉K很容易测试。UT要求被测试的东西可重玎ͼ可观?基本上,大部分的物理操作因ؓ~Z可重复性或可观察性,很难试Q例如databaseQGUI Q注意,q不意味着在实C个GUI库或db driver时就不能做UT了)。勉强UT全覆盖,既不现实Q也不实惠。ƈ且,q很可能让UT变得复杂Q高成本Q这是非常危险的和不值得的。我的主张是Q很难测Q那׃,但要正确应对。我的做法是难的部分隔离C些抽象层当中厅R然后ؓq些抽象层写MockObject卛_试了。我曄应用在数据库应用中,q很自然的得C个良好的数据讉K的抽象层Q单元测试就只测了这个抽象层。而实际的数据库访问中的物理操作部分,则从单元试中剥d厅R如果坚持分ȝ理操作和逻辑操作的话Q这个剥dȝ部分一般很很有限Q也很容易测试。相反,如果不剥,导致单元测试的l果要依赖数据库的状态。这U额外的依赖性没什么好处。这里的关键是,必须让不可测的部分尽可能隔离Q尽可能,可能地逻辑操作从物理操作中分离出来。被隔离部分所包含的逻辑操作仍然需要写UT?

eXile 2008-04-29 13:39 发表评论
]]>
设计Qt风格的C++API (zt)http://www.shnenglu.com/eXile/archive/2008/02/18/42857.htmleXileeXileSun, 17 Feb 2008 16:50:00 GMThttp://www.shnenglu.com/eXile/archive/2008/02/18/42857.htmlhttp://www.shnenglu.com/eXile/comments/42857.htmlhttp://www.shnenglu.com/eXile/archive/2008/02/18/42857.html#Feedback0http://www.shnenglu.com/eXile/comments/commentRss/42857.htmlhttp://www.shnenglu.com/eXile/services/trackbacks/42857.html设计Qt风格的C++API

作者Matthias EttrichQ译者Googol LeeQ原文地址?a >q里?

在奇(TrolltechQ,Z改进Qt的开发体验,我们做了大量的研I。这文章里Q我打算分n一些我们的发现Q以及一些我们在设计Qt4时用到的原则Qƈ且展C如何把q些原则应用C的代码里?

设计应用E序接口QAPIQ是很难的。这是一门和设计语言同样隄艺术。这里可以选择太多的原则,甚至有很多原则和其他原则有矛盾?

现在Q计机U学教育把很大的力气攑֜法和数据结构上Q而很关注设计语a和框架背后的原则。这让应用程序员完全没有准备去面对越来越重要的Q务:创造可重用的组件?

在面向对象语a普及之前Q可重用的通用代码大部分是由库提供者写的,而不是应用程序员。在Qt的世界里Q这U状冉|了明昄改善。在M时候,用Qt~程是写新的组件。一个典型的Qt应用E序臛_都会有几个在E序中反复用的自定义组件。一般来_同样的组件会成ؓ其他应用E序的一部分。KDEQK桌面环境Q走得更q,用许多追加的库来扩展QtQ实C数百个附加类。(一般来_一个类是一个可重用lgQ原文这里没有写清楚。)

但是Q一个好的,高效的C++ API是由什么组成的呢?是好q是坏,取决于很多因素——比如,手头的工作和特定的目标群体。好的API有很多特性,一些特性是大家都想要的Q而另一些则是针对特定问题域的?

好的API的六个特?/h2>

API是面向程序员的,用来描述提供l最l用LGUI是什么样子。API中的P带表E序员(ProgrammerQ,而不是程序(ProgramQ,用来API是给E序员用的,lhcȝE序员用的?

我们坚信API应该是最化且完整的Q拥有清C单的语义Q直觉化Q容易记忆,q且引导人写出易ȝ代码?

  • 最化Q?/strong>最化的API是指一个类可能只拥有最的公开成员且尽可能只拥有最的cR这个原则可以让API更简单易懂,更好讎ͼ更容易除错,且更Ҏ改变?
  • 完整的:完整的API是指要提供所有期望的功能。这个可能与最化原则相冲H。另外,如果一个成员函数属于一个不应该属于的类Q很多潜在的使用者都会找不到q个函数?
  • 拥有清晰且简单的语义Q?/strong>像其他设计工作一P你必遵守最惊奇原则(the principle of least surpriseQ。让常见的Q务简单易行。不常见的工作可行,但不会让用户q分x。解决特D问题时Q不要让解决Ҏ没有必要的过度通用。(比如QQt3中的QMimeSourceFactory可以通过调用QImageLoader来实C同的API。)
  • 直觉化:像电脑上的其他东西一PAPI必须是直觉化的。不同的l验和背景会D在判断什么是直觉而什么不是时不同的感觉。如果一个中U用户不L档就可以使用Qa semi-experienced user gets away without reading the documentationQ没懂这里的get away该怎么译Q,q且一个程序员不懂API可以理解羃写的代码Q这UAPI是直觉化的?
  • 易于记忆Q?/strong>让API易于记忆Q用统一且精的命名Ҏ。用可识别的模式和概念Qƈ且避免羃写?
  • 引导易读的代码(Lead to readable codeQ:代码一l写,会读Qƈ且除错和修改Q多ơ。易ȝ代码可能会花Ҏ间来写,但是可以节省产品周期中的其他旉?

最后,CQ不同类型的用户会用到API的不同部分。虽然简单的实例化一个QtcL非常直觉化的Q让资深专家在试囑֭cd之前M遍文档,是很合理的?

便利陷阱

q是个常见的误解Q更好的APIQ用更少的代码完成一件事。永q记住代码一ơ写,之后需要不断的阅读q理解。比如:

    QSlider *slider = new QSlider(12, 18, 3, 13, Qt::Vertical,
0, "volume");

q比下面那样难读Q甚至难写)Q?

    QSlider *slider = new QSlider(Qt::Vertical);
slider->setRange(12, 18);
slider->setPageStep(3);
slider->setValue(13);
slider->setObjectName("volume");

布尔参数陷阱

布尔参数通常会导致不易读的代码。更q一步,l一个已l存在的函数加入一个布参敎ͼq常常是个错误。在Qt里,一个传l的例子是repaint()Q这个函数带有一个布参敎ͼ来标识是否擦除背景(默认擦除Q。这让代码通常写成Q?

    widget->repaint(false);

初学者很Ҏ把这句话理解?#8220;别重?#8221;Q?

q样做是考虑到布参数可以减一个函敎ͼ避免代码膨胀。事实上Q这反而增加了代码量。有多少Qt用户真的C了下面三行程序都是做什么的Q?

    widget->repaint();
widget->repaint(true);
widget->repaint(false);

一个好一些的API可能看v来是q样Q?

    widget->repaint();
widget->repaintWithoutErasing();

在Qt4里,我们重新设计了widgetQ得用户不再需要不重画背景的重画widgetQ来解决q个问题。Qt4原生支持双缓存,废掉了这个特性?

q里q有一些例子:

    widget->setSizePolicy(QSizePolicy::Fixed,
QSizePolicy::Expanding, true);
textEdit->insert("Where's Waldo?", true, true, false);
QRegExp rx("moc_*.c??", false, true);

一个显而易见的解决Ҏ是,使用枚Dcd代替布尔参数。这正是我们在Qt4?a >QString大小写敏感时的处理方法。比较:

    str.replace("%USER%", user, false);               // Qt 3
str.replace("%USER%", user, Qt::CaseInsensitive); // Qt 4

静态多?/h2>

怼的类应该含有怼的API。在必要的时候——就是说Q需要用运行时多态的时候——这可以通过l承实现。但是多态依旧会发生在设计时期。比如,如果你用QListBox代替QComboBoxQ或者用QSlider代替QSpinBoxQ你会发现相似的API使这U替换非常容易。这是我们所说的“静态多?#8221;?

静态多态也使API和程序模式更Ҏ记忆。作为结论,一l相关类使用怼的APIQ有时要比给每个cL供完的单独APIQ要好?

Q译注:C++ 0x要引入的conceptQ就是静态多态的语法层实现。这个要比单独的函数名相似更强大且易用。)

命名的艺?/h2>

命名Q大概是设计API时唯一最重要的问题了。该怎么U呼q个c?成员函数该叫什么?

通用的命名规?/h3>

一些规则通常Ҏ有名字都是有用的。首先,像我之前提到的Q别用羃写。甚臛_明显的羃写,比如“prev”表示“previous”从长q看也是不划的Q因为用户必记住哪些词是羃写?

如果API本n不一_事情自然会变得很p糕Q比如, Qt3有activatePreviousWindow()和fetchPrev()。坚?#8220;没有~写”的规则更Ҏ创徏一致的API?

另一个重要但更加微妙的规则是Q在设计cȝ时候,必须力保证子类命名I间的干净。在Qt3里,没有很好的遵守这个规则。比如,?a >QToolButton来D例。如果你在Qt3里,对一?a >QToolButton调用name()、caption()、text()或者textLabel()Q你希望做什么呢Q你可以在Qt Designer里拿QToolButton试试Q?

  • name属性承自QObjectQ表CZ个对象用于除错和试的内部名字?
  • caption属性承自QWidgetQ表C窗口的标题Q这个标题在视觉上对QToolButton没有M意义Q因Z们L跟随父窗口而创建?
  • text属性承自QButtonQ一般情况下是按钮上现实的文字,除非useTextLabel为真?
  • textLabel?a >QToolButton里声明,q且在useTextLabel为真时显C在按钮上?

׃对可L的xQname在Qt4里被UCobjectNameQcaption变成了windowsTitleQ而在QToolButton里不再有单独的textLabel属性?

l类命名

标识一l类而不是单独给每个cL个恰当的名字。比如,Qt4里所有模式感知项目的视图c(model-aware item view classesQ都拥有-View的后~Q?a >QListView?a >QTableView?a >QTreeViewQ,q且对应Z目的类都用后缀-Widget代替Q?a >QListWidget?a >QTableWidget?a >QTreeWidgetQ?

l枚丄型及其值命?/h3>

当声明枚举时Q时刻记住,在C++Q不像Java和C#Q中Q用枚丑րg需要类型信息。下面的例子演示了给枚DDv个太q常用的名字所引v的危宻I

    namespace Qt
{
enum Corner { TopLeft, BottomRight, ... };
enum CaseSensitivity { Insensitive, Sensitive };
...
};
tabWidget->setCornerWidget(widget, Qt::TopLeft);
str.indexOf("$(QTDIR)", Qt::Insensitive);

在最后一行,Insensitive是什么意思?一个用于命名枚丑ր的指导思想是,在每个枚丑ր里Q至重复一个枚丄型名中的元素Q?

    namespace Qt
{
enum Corner { TopLeftCorner, BottomRightCorner, ... };
enum CaseSensitivity { CaseInsensitive,
CaseSensitive };
...
};
tabWidget->setCornerWidget(widget, Qt::TopLeftCorner);
str.indexOf("$(QTDIR)", Qt::CaseInsensitive);

当枚丑ր可以用“?#8221;q接h当作一个标志时Q传l的做法是将“?#8221;的结果作Z个int保存Q这不是cd安全的。Qt4提供了一个模板类 QFlags<T>来实现类型安全,其中T是个枚Dcd。ؓ了方便用,Qt为很多标志类名提供了typedefQ所以你可以使用cd Qt::Alignment代替QFlags<Qt::AlignmentFlag>?

Z方便Q我们给枚Dcd单数的名字(q样表示枚Dgơ只能有一个标志)Q?#8220;标志”则用复数名字。比如:

    enum RectangleEdge { LeftEdge, RightEdge, ... };
typedef QFlags<RectangleEdge> RectangleEdges;

有些情况下,“标志“cM用了单数的名字。这Ӟ枚DcM?Flag做后~Q?

    enum AlignmentFlag { AlignLeft, AlignTop, ... };
typedef QFlags<AlignmentFlag> Alignment;

Q这里ؓ啥不是把”标志“cȝ-Flag做后~Q而是把枚丑ր做后缀呢?感觉有点h……Q?

l函数和参数命名

l函数命名的一个规则是Q名字要明确体现个函数是否有副作用。在Qt3Q常数函?a >QString::simplifyWhiteSpace()q反了这个原则,因ؓ它返回类一?a >QString实例Q而不是像名字所提示的那P更改了调用这个函数的实例本n。在Qt4Q这个函数被重命名ؓQString::simplified()?

参数名是E序员的重要信息来源Q虽然在使用APIӞq不直接展示在代码里。由于现代IDE在程序员写代码时可以自动昄参数名(是自动感知或者自动补全之cȝ功能Q,值得花时间给头文仉声明的参C个合适的名字Qƈ且在文中也使用相同的名字?

l布D|函敎ͼSetterQ、提取函敎ͼGetterQ和属性命?/h3>

l布属性的讄函数和提取函C个合适的名字QL非常痛苦的。提取函数应该叫做checked()q是isChecked()QscrollBarsEnabled()q是areScrollBarEnabled()?

在Qt4里,我们使用下列规则命名提取函数Q?

  • 形容cȝ属性用is-前缀。比如:
    • isChecked()
    • isDown()
    • isEmpty()
    • isMovingEnable()
    另外Q应用到复数名词的Ş容类属性没有前~Q?
    • scrollBarsEnabled()Q而不?tt>areScrollBarsEnabled()
  • 动词cȝ属性不使用前缀Q且不用第三hUͼ-sQ:
    • acceptDrops()Q而不?tt>acceptsDrops()
    • allColumnsShowFocus()
  • 名词cȝ属性,通常没有前缀Q?
    • autoCompletion()Q而不?tt>isAutoCompletion()
    • boundaryChecking()
    有时Q没有前~׃引v误解Q这U情况用前~is-Q?
    • isOpenGLAvailable()Q而不?tt>openGL()
    • isDialog()Q而不?tt>dialog()
    Q通过调用dialogue()ҎQ正常情况下会期望返回一?a >QDialog*的实例。)

讄函数名字l承自提取函数名Q只是移掉了所有前~Qƈ使用set-做前~Q比如:setDown()q有setScrollBarsEnabled()。属性的名字与提取函数相同,只是L了前~?

指针q是引用Q?/h2>

传出参数的最佳选择是什么,指针q是引用Q?

    void getHsv(int *h, int *s, int *v) const
void getHsv(int &h, int &s, int &v) const

大部分C++书推荐在能用引用的地方就用引用,q是因ؓ一般认为引用比指针?#8220;安全且好?#8221;。然而,在奇(TrolltechQ,我们們֐使用指针Q因让代码更易读。比较:

    color.getHsv(&h, &s, &v);
color.getHsv(h, s, v);

只有W一行能清楚的说明,在函数调用后Qh、s和v有很大几率被改动?

例子QQProgressBar

Z展示如何实际应用q些概念Q我们将学习Qt3中的API QProgressBarq和Qt4里相通的API做比较。在Qt3里:

    class QProgressBar : public QWidget
{
...
public:
int totalSteps() const;
int progress() const;
const QString &progressString() const;
bool percentageVisible() const;
void setPercentageVisible(bool);
void setCenterIndicator(bool on);
bool centerIndicator() const;
void setIndicatorFollowsStyle(bool);
bool indicatorFollowsStyle() const;
public slots:
void reset();
virtual void setTotalSteps(int totalSteps);
virtual void setProgress(int progress);
void setProgress(int progress, int totalSteps);
protected:
virtual bool setIndicator(QString &progressStr,
int progress,
int totalSteps);
...
};

API相当复杂Q且不统一。比如,仅从名字reset()q不能理解其作用QsetTotalSteps()和setProgress()是紧耦合的?

改进API的关键,是注意到QProgressBar和Qt4?a >QAbstractSpinBoxcd其子c?a >QSpinBoxQ?a >QSlider?a >QDial很相伹{解x法?用minimum、maximum和value代替progress和totalSteps。加入alueChanged()信号。加入setRange()函数?

之后观察progressString、percentage和indicator实际都指一个东西:在进度条上显C的文字。一般来说文字是癑ֈ比信息,但是也可以用setIndicator()设ؓL字符。下面是新的APIQ?

    virtual QString text() const;
void setTextVisible(bool visible);
bool isTextVisible() const;

默认的文字信息是癑ֈ比信息。文字信息可以藉由重新实现text()而改变?

在Qt3 API中,setCenterIndicator()和setIndicatorFollowStyle()是两个媄响对齐的函数。他们可以方便的׃个函数实玎ͼsetAlignment()Q?

    void setAlignment(Qt::Alignment alignment);

如果E序员不调用setAlignment()Q对齐方式基于当前的风格。对于基于Motif的风|文字居中显C;对其他风|文字靠在右辏V?

q是改进后的QProgressBar APIQ?

    class QProgressBar : public QWidget
{
...
public:
void setMinimum(int minimum);
int minimum() const;
void setMaximum(int maximum);
int maximum() const;
void setRange(int minimum, int maximum);
int value() const;
virtual QString text() const;
void setTextVisible(bool visible);
bool isTextVisible() const;
Qt::Alignment alignment() const;
void setAlignment(Qt::Alignment alignment);
public slots:
void reset();
void setValue(int value);
signals:
void valueChanged(int value);
...
};

如何把API设计好(原文是How to Get APIs RightQ我L成We do APIs right……Q?/h2>

API需要质量保证。第一个修订版不可能是正确的;你必d试。写些用例:看看那些使用了这些API的代码,q证代码是否易诅R?

其他的技巧包括让别的人分别在有文和没有文的情况下Q用这些APIQ或者ؓAPIcd文档Q包括类的概q和独立的函敎ͼ?

当你卡住Ӟ写文也是一U获得好名字的方法:仅仅是尝试把条目Q类Q函敎ͼ枚D|{等呢个Q写下来q且使用你写的第一句话作ؓ灉|。如果你不能扑ֈ一个精的名字Q这常常说明q个条目不应该存在。如果所有前面的事情都失败了q且你确认这个概늚存在Q发明一个新名字。毕竟,“widget”?“event”?#8220;focus”?#8220;buddy”q些名字是q么来的?



eXile 2008-02-18 00:50 发表评论
]]>试驱动开发(Q_Q)的顿?/title><link>http://www.shnenglu.com/eXile/archive/2008/01/23/41728.html</link><dc:creator>eXile</dc:creator><author>eXile</author><pubDate>Wed, 23 Jan 2008 09:23:00 GMT</pubDate><guid>http://www.shnenglu.com/eXile/archive/2008/01/23/41728.html</guid><wfw:comment>http://www.shnenglu.com/eXile/comments/41728.html</wfw:comment><comments>http://www.shnenglu.com/eXile/archive/2008/01/23/41728.html#Feedback</comments><slash:comments>11</slash:comments><wfw:commentRss>http://www.shnenglu.com/eXile/comments/commentRss/41728.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/eXile/services/trackbacks/41728.html</trackback:ping><description><![CDATA[  对于试驱动开发(Q_Q)Q始l有一些迷惑,比如_它的试需要考虑完备性吗Q需要考虑覆盖率吗Q等{此cR今天从Javaeye中看C句话Q终于明白了?br>  “<span style="COLOR: red">什么是TDDQTDD是把你的需求用试l描q出?/span>?#8221;<br>  也就是说QTQD中的试和一般意义上的单元测试ƈ不一P管Q_Q中的测试有时也作ؓ单元试来用,但它们是两回事。(q里?font style="COLOR: #000000" color=#ff0000>需求,指的不是客户需求,而是E序员的开发需求)?/font><br>  使用Q_Q时Q首先写的是试Q这时相应代码还没有实现Q那么测试什么东西呢Q所以说Q写试的过E,同时也是<strong><span style="COLOR: red">?/span><span style="COLOR: red">计接?/span></strong>的过E。这和写单元试的目的完全是不一L?br>  Q_Q还有一个额外的好处。大多数人都是懒的,不要指望所有的E序员在写完功能代码后,再去~写相应的单元测试。我觉得q个接口的实现没有问题,所以就不用试。这U想法也很常见。所以一开始就写下试Q可以杜l后患? <img src ="http://www.shnenglu.com/eXile/aggbug/41728.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/eXile/" target="_blank">eXile</a> 2008-01-23 17:23 <a href="http://www.shnenglu.com/eXile/archive/2008/01/23/41728.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss> <footer> <div class="friendship-link"> <p>лǵվܻԴȤ</p> <a href="http://www.shnenglu.com/" title="精品视频久久久久">精品视频久久久久</a> <div class="friend-links"> </div> </div> </footer> <a href="http://www.4neq.cn" target="_blank">պŷþ</a>| <a href="http://www.jjygw.cn" target="_blank">99Ʒþþþþþ</a>| <a href="http://www.25jt.cn" target="_blank">ƷŮþøվ</a>| <a href="http://www.spbbs.cn" target="_blank">ƷžžþƵ </a>| <a href="http://www.bestsilm.cn" target="_blank">þù㽶һƷ</a>| <a href="http://www.jiletu.cn" target="_blank">99þѹػ</a>| <a href="http://www.yes365cc.cn" target="_blank">Ʒٸavþ</a>| <a href="http://www.yangrendong.com.cn" target="_blank">97þۺɫdžѿ </a>| <a href="http://www.fygjkd.cn" target="_blank">˾þþƷavһ</a>| <a href="http://www.baiyaiyungroup.com.cn" target="_blank">޾ƷþëƬ</a>| <a href="http://www.tianyacity.cn" target="_blank">һþöۺ</a>| <a href="http://www.a-house.com.cn" target="_blank">þѹۿƵ</a>| <a href="http://www.jm1818.cn" target="_blank">þĻ˿</a>| <a href="http://www.fti1st.com.cn" target="_blank">97Ʒ˾þô߽</a>| <a href="http://www.114jw.cn" target="_blank">77777ҹþö</a>| <a href="http://www.s88w.cn" target="_blank">þþƷAAƬһ</a>| <a href="http://www.itwinds.cn" target="_blank">Ʒþþþþ֣ݹ˾ </a>| <a href="http://www.pnpxnc.cn" target="_blank">Ʒһþ㽶߿ </a>| <a href="http://www.netbirds.cn" target="_blank">Ļþһ</a>| <a href="http://www.zhxjk.cn" target="_blank">þþƷ99þþùŴ</a>| <a href="http://www.xnhtml.com.cn" target="_blank">avþþƷ</a>| <a href="http://www.976z.cn" target="_blank">91Ʒ91þþþþ</a>| <a href="http://www.ofjf.cn" target="_blank">18պҹþó</a>| <a href="http://www.tmqywedding.cn" target="_blank">þþƷ99þ˿</a>| <a href="http://www.4o2ptp.cn" target="_blank">˳þõӰվ</a>| <a href="http://www.iconglass.com.cn" target="_blank">99ƷۺϾþþþ</a>| <a href="http://www.ovnz.cn" target="_blank">jizzjizzƷþ</a>| <a href="http://www.fayongshicai.cn" target="_blank">ۿ ۺϾþþþùɫ ŷ һ </a>| <a href="http://www.j8hq8.cn" target="_blank">޾Ʒþþþȥq</a>| <a href="http://www.i35idc.cn" target="_blank">ƷþþþþӰԺ</a>| <a href="http://www.mk606.cn" target="_blank">þɫۺһ</a>| <a href="http://www.fqgjz.cn" target="_blank">þþþAVվ</a>| <a href="http://www.jblw.net.cn" target="_blank">9999Ʒŷþþþþ</a>| <a href="http://www.cnsei.cn" target="_blank">˾þô߽Ʒ</a>| <a href="http://www.ganzhoupei.cn" target="_blank">þҹ³Ƭ</a>| <a href="http://www.vauban.cn" target="_blank">99þþƷһ </a>| <a href="http://www.zazhui.cn" target="_blank">þù׾Ʒҹ</a>| <a href="http://www.ylog.cn" target="_blank">þ99Ʒ鶹լլ</a>| <a href="http://www.xingtaiidc.cn" target="_blank">˺ݺۺϾþ88</a>| <a href="http://www.mdg163.cn" target="_blank">AVþþƷݺݰ˳</a>| <a href="http://www.02324.cn" target="_blank">ũ帾ŮëƬƷþ</a>| <script> (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })(); </script> </body>