C++ Jounior

once setback,once inspiration,once self-awareness
重要的是这个磨练过程,而不是结果,要的是你粗壮的腿,而不是你身上背的那袋盐巴

 

2008年7月6日

银行家算法

     摘要: reference ; http://www.yuanma.org/data/2008/0116/article_2945.htm算法的实现 一、初始化 由用户输入数据,分别对可利用资源向量矩阵AVAILABLE、最大需求矩阵MAX、分配矩阵ALLOCATION、需求矩阵NEED赋值。   二、银行家算法 在避免死锁的方法中,所施加的限制条件较弱,有可能获得令人满意的系统性...  阅读全文

posted @ 2008-07-06 22:16 snowball 阅读(6719) | 评论 (8)编辑 收藏

2008年7月1日

面向对象的三个基本特征(讲解)

     摘要: 面向对象的三个基本特征(讲解) 面向对象的三个基本特征是:封装、继承、多态。 ...  阅读全文

posted @ 2008-07-01 16:10 snowball 阅读(10270) | 评论 (6)编辑 收藏

2008年4月11日

电子书下载

Inside Microsoft Dynamics AX 4.0 Link

posted @ 2008-04-11 16:04 snowball 阅读(333) | 评论 (0)编辑 收藏

2008年4月6日

初学者,你应当如何学习C++以及编程

JavaScript是世界上最受误解的语言,其实C++何尝不是。坊间流传的错误的C++学习方法一抓就是一大把。我自己在学习C++的过程中也走了许多弯路,浪费了不少时间。

  为什么会存在这么多错误认识?原因主要有三个,一是C++语言的细节太多。二是一些著名的C++书籍总在(不管有意还是无意)暗示语言细节的重要性和有趣。三是现代C++库的开发哲学必须用到一些犄角旮旯的语言细节(但注意,是库设计,不是日常编程)。这些共同塑造了C++社群的整体心态和哲学。

  单是第一条还未必能够成气候,其它语言的细节也不少(尽管比起C++起来还是小巫见大巫),就拿javascript来说,作用域规则,名字查找,closure,for/in,这些都是细节,而且其中还有违反直觉的。但许多动态语言的程序员的理念我猜大约是学到哪用到哪罢。但C++就不一样了,学C++之人有一种类似于被暗示的潜在心态,就是一定要先把语言核心基本上吃透了才能下手写出漂亮的程序。这首先就错了。这个意识形成的原因在第二点,C++书籍。市面上的C++书籍不计其数,但有一个共同的缺点,就是讲语言细节的书太多——《C++ gotchas》,《Effective C++》,《More Effective C++》,但无可厚非的是,C++是这样一门语言:要拿它满足现代编程理念的需求,尤其是C++库开发的需求,还必须得关注语言细节,乃至于在C++中利用语言细节已经成了一门学问。比如C++模板在设计之初根本没有想到模板元编程这回事,更没想到C++模板系统是图灵完备的,这也就导致了《Modern C++ Design》和《C++ Template Metaprogramming》的惊世骇俗。

  这些技术的出现为什么惊世骇俗,打个比方,就好比是一块大家都认为已经熟悉无比,再无秘密可言的土地上,突然某天有人挖到原来地下还蕴藏着最丰富的石油。在这之前的C++虽然也有一些细节,但也还算容易掌握,那可是C++程序员们的happy old times,因为C++的一切都一览无余,everything is figured out。然而《Modern C++ Design》的出世告诉人们,“瞧,还有多少细节你们没有掌握啊。”于是C++程序员们久违的激情被重燃起来,奋不顾身的踏入细节的沼泽中。尤其是,模板编程将C++的细节进一步挖掘到了极致——我们干嘛关心涉及类对象的隐式转换的优先级高低?看看boost::is_base_of就可以知道有多诡异了。

  但最大的问题还在于,对于这些细节的关注还真有它合适的理由:我们要开发现代模板库,要开发active library,就必须动用模板编程技术,要动用模板编程技术,就必须利用语言的犄角旮旯,enable_if,type_traits,甚至连早就古井无波的C宏也在乱世中重生,看看boost::preprocessor有多诡异就知道了,连C宏的图灵完备性(预编译期的)都被挖掘出来了。为什么要做这些?好玩?标榜?都不是,开发库的实际需求。但这也正是最大的悲哀了。在boost里面因实际需求而动用语言细节最终居然能神奇的完成任务的最好教材就是boost::foreach,这个小设施对语言细节的发掘达到了惊天地泣鬼神的地步,不信你先试着自己去看看它的源代码,再看看作者介绍它的文章吧。而boost::typeof也不甘其后——C++语言里面有太多被“发现”而不是被“发明”的技术。难道最初无意设置这些语言规则的家伙们都是Oracles?

  因为没有variadic templates,人们用宏加上缺省模板参数来实现类似效果。因为没有concepts,人们用模板加上析构函数的细节来完成类似工作。因为没有typeof,人们用模板元编程和宏加上无尽的细节来实现目标… C++开发者们的DIY精神不可谓不强。

  然而,如果仅仅是因为要开发优秀的库,那么涉及这些细节都还是情有可原的,至少在C++09出现并且编译器厂商跟上之前,这些都还能说是不得已而为之。但我们广大的C++程序员呢?大众是容易被误导的,我也曾经是。以为掌握了更多的语言细节就更牛,但实际却是那些语言细节十有八九是平时编程用都用不到的。C++中众多的细节虽然在库设计者手里面有其用武之地,但普通程序员则根本无需过多关注,尤其是没有实际动机的关注。一般性的编码实践准则,以及基本的编程能力和基本功,乃至基本的程序设计理论以及算法设计。才是真正需要花时间掌握的东西。

  学习最佳编码实践比学习C++更重要。看优秀的代码也比埋头用差劲的编码方式写垃圾代码要有效。直接、清晰、明了、KISS地表达意图比玩编码花招要重要…

  避免去过问任何语言细节,除非必要。这个必要是指在实际编程当中遇到问题,这样就算需要过问细节,也是最省事的,懒惰者原则嘛。一个掌握了基本的编程理念并有较强学习能力的程序员在用一门陌生的语言编程时就算拿着那本语言的圣经从索引翻起也可以编出合格的程序来。十年学会编程不是指对每门语言都得十年,那一辈子才能学几门语言哪,如果按字母顺序学的话一辈子都别指望学到Ruby了;十年学习编程更不是指先把语言特性从粗到细全都吃透才敢下手编程,在实践中提高才是最重要的。

  至于这种抠语言细节的哲学为何能在社群里面呈野火燎原之势,就是一个心理学的问题了。想像人们在论坛上讨论问题时,一个对语言把握很细致的人肯定能够得到更多的佩服,而由于论坛上的问题大多是小问题,所以解决实际问题的真正能力并不能得到显现,也就是说,知识型的人能够得到更多佩服,后者便成为动力和仿效的砝码。然而真正的编程能力是与语言细节没关系的,熟练运用一门语言能够帮你最佳表达你的意图,但熟练运用一门语言绝不意味着要把它的边边角角全都记住。懂得一些常识,有了编程的基本直觉,遇到一些细节错误的时候再去查书,是最节省时间的办法。

  C++的书,Bjarne的圣经《The C++ Programming Language》是高屋建瓴的。《大规模C++程序设计》是挺务实的。《Accelerated C++》是最佳入门的。《C++ Templates》是仅作参考的。《C++ Template Metaprogramming》是精力过剩者可以玩一玩的,普通程序员碰都别碰的。《ISO.IEC C++ Standard 14882》不是拿来读的。Bjarne最近在做C++的教育,新书是绝对可以期待的。

  P.S. 关于如何学习编程,g9的blog上有许多精彩的文章:这里,这里,这里,这里… 实际上,我建议你去把g9老大的blog翻个底朝天 :P

  再P.S. 书单?我是遑于给出一个类似《C++初学者必读》这种书单的。C++的书不计其数,被公认的好书也不胜枚举。只不过有些书容易给初学者造成一种错觉,就是“学习C++就应该是这个样子的”。比如有朋友提到的《高质量C/C++编程》,这本书有价值,但不适合初学者,初学者读这样的书容易一叶障目不见泰山。实际上,正确的态度是,细节是必要的。但细节是次要的。其实学习编程我觉得应该最先学习如何用伪码表达思想呢,君不见《Introduction to Algorithm》里面的代码?《TAOCP》中的代码?哦,对了它们是自己建立的语言,但这种仅教学目的的语言的目的就是为了避免让写程序的人一开始就忘了写程序是为了完成功能,以为写程序就是和语言细节作斗争了。Bjarne说程序的正确性最重要,boost的编码标准里面也将正确性列在性能前面。

  此外,一旦建立了正确的学习编程的理念,其实什么书(只要不是太垃圾的)都有些用处。都当成参考书,用的时候从目录或索引翻,基本就对了。

  再再P.S. myan老大和g9老大都给出了许多精彩的见解。我不得不再加上一个P.S。具体我就不摘录了,如果你读到这里,请务必往下看他们的评论。转载者别忘了转载他们的评论:-)

  许多朋友都问我同一个问题,到底要不要学习C++。其实这个问题问得很没有意义。“学C++”和“不学C++”这个二分法是没意义的,为什么?因为这个问题很表面,甚至很浮躁。重要的不是你掌握的语言,而是你掌握的能力,借用myan老大的话,“重要的是这个磨练过程,而不是结果,要的是你粗壮的腿,而不是你身上背的那袋盐巴。”。此外学习C++的意义其实真的是醉翁之意不在酒,像C/C++这种系统级语言,在学习的过程中必须要涉及到一些底层知识,如内存管理、编译连接系统、汇编语言、硬件体系结构等等等等知识(注意,这不包括过分犄角旮旯的语言枝节)。这些东西也就是所谓的内功了(其实最最重要的内功还是长期学习所磨练出来的自学能力)。对此大嘴Joel在《Joel On Software》里面提到的漏洞抽象定律阐述得就非常漂亮。

  所以,答案是,让你成为高手的并不是你掌握什么语言,精通C++未必就能让你成为高手,不精通C++也未必就能让你成为低手。我想大家都不会怀疑g9老大如果要抄起C++做一个项目的话会比大多数自认熟练C++的人要做得漂亮。所以关键的不是语言这个表层的东西,而是底下的本质矛盾。当然,不是说那就什么语言都不要学了,按照一种曹操的逻辑,“天下语言,唯imperative与declarative耳”。C++是前者里面最复杂的一种,支持最广泛的编程范式。借用当初数学系入学大会上一个老师的话,“你数学都学了,还有什么不能学的呢?”。学语言是一个途径,如果你把它用来磨练自己,可以。如果你把它用来作为学习系统底层知识的钥匙,可以。如果你把它用来作为学习如何编写优秀的代码,如何组织大型的程序,如何进行抽象设计,可以。如果掉书袋,光啃细节,我认为不可以(除非你必须要用到细节,像boost库的coder们)。

然后再借用一下g9老大的《银弹和我们的职业》中的话:

  银弹和我们的职业发展有什么相干?很简单:我们得把时间用于学习解决本质困难。新技术给高手带来方便。菜鸟们却不用指望被新技术拯救。沿用以前的比喻, 一流的摄影师不会因为相机的更新换代而丢掉饭碗,反而可能借助先进技术留下传世佳作。因为摄影的本质困难,还是摄影师的艺术感觉。热门技术也就等于相机。 不停追新,学习这个框架,那个软件,好比成天钻研不同相机的说明书。而热门技术后的来龙去脉,才好比摄影技术。为什么推出这个框架?它解决了什么其它框架 不能解决的问题?它在哪里适用?它在哪里不适用?它用了什么新的设计?它改进了哪些旧的设计?Why is forever. 和 朋友聊天时提到Steve McConnell的《Professional Software Development》里面引了一个调查,说软件开发技术的半衰期20年。也就是说20年后我们现在知识里一半的东西过时。相当不坏。朋友打趣道:“应 该说20年后IT界一半的技术过时,我们学的过时技术远远超过这个比例。具体到某人,很可能5年他就废了”。话虽悲观,但可见选择学习内容的重要性。学习 本质技艺(技术迟早过时,技艺却常用长新)还有一好处,就是不用看着自己心爱的技术受到挑战的时候干嚎。C/C++过时就过时了呗,只要有其它的系统编程 语言。Java倒了就倒了呗,未必我不能用.net?Ruby昙花一现又如何。如果用得不爽,换到其它动态语言就是了。J2EE被废了又怎样?未必我们就 做不出分布系统了?这里还举了更多的例子。

  一句话,只有人是真正的银弹。职业发展的目标,就是把自己变成银弹。那时候,你就不再是人,而是人弹。

  最后就以我在Bjarne的众多访谈当中摘录的一些关于如何学习C++(以及编程)的看法结束吧(没空逐段翻译了,只将其中我觉得最重要的几段译了一下,当然,其它也很重要,这些段落是在Bjarne的所有采访稿中摘抄出来的,所以强烈建议都过目一下):

  I suspect that people think too little about what they want to build, too little about what would make it correct, and too much about "efficiency" and following fashions of programming style. The key questions are always: "what do I want to do?" and "how do I know that I have done if?". Strategies for testing enters into my concerns from well before I write the firat line of code, and that despite my view that you have to write code very early - rather than wait until a design is complete.

  译:我感觉人们过多关注了所谓“效率”以及跟随编程风格的潮流,却严重忽视了本不该被忽视的问题,如“我究竟想要构建什么样的系统”、“怎样才能使它正确”。最关键的问题永远是:“我究竟想要做什么?”和“如何才能知道我的系统是否已经完成了呢?”就拿我来说吧,我会在编写第一行代码之前就考虑测试方案,而且这还是在我关于应当早于设计完成之前就进行编码的观点的前提之下。

  Obviously, C++ is very complex. Obviously, people get lost. However, most peple get lost when they get diverted into becoming language lawyers rather than getting lost when they have a clear idea of what they want to express and simply look at C++ language features to see how to express it. Once you know data absreaction, class hierarchies (object-oriented programming), and parameterization with types (generic programming) in a fairly general way, the C++ language features fall in place.

  译:诚然,C++非常复杂。诚然,人们迷失其中了。然而问题是,大多数人不是因为首先对自己想要表达什么有了清晰的认识只不过在去C++语言中搜寻合适的语言特性时迷失的,相反,大多数人是在不觉成为语言律师的路上迷失在细节的丛林中的。事实是,只需对数据抽象、类体系结构(OOP)以及参数化类型(GP)有一个相当一般层面的了解,C++纷繁的语言特性也就清晰起来了。

  Well, I don't think I made such a trade-off. I want elegant and efficient code. Sometimes I get it. These dichotomies (between efficiency versus correctness, efficiency versus programmer time, efficiency versus high-level, et cetera.) are bogus.

  I think the real problem is that "we" (that is, we software developers) are in a permanent state of emergency, grASPing at straws to get our work done. We perform many minor miracles through trial and error, excessive use of brute force, and lots and lots of testing, but--so often--it's not enough.

  Software developers have become adept at the difficult art of building reasonably reliable systems out of unreliable parts. The snag is that often we do not know exactly how we did it: a system just "sort of evolved" into something minimally acceptable. Personally, I prefer to know when a system will work, and why it will.

  There are more useful systems developed in languages deemed awful than in languages praised for being beautiful--many more. The purpose of a programming language is to help build good systems, where "good" can be defined in many ways. My brief definition is, correct, maintainable, and adequately fast. Aesthetics matter, but first and foremost a language must be useful; it must allow real-world programmers to express real-world ideas succinctly and affordably.

  I'm sure that for every programmer that dislikes C++, there is one who likes it. However, a friend of mine went to a conference where the keynote speaker asked the audience to indicate by show of hands, one, how many people disliked C++, and two, how many people had written a C++ program. There were twice as many people in the first group than the second. Expressing dislike of something you don't know is usually known as prejudice. Also, complainers are always louder and more certain than proponents--reasonable people acknowledge flaws. I think I know more about the problems with C++ than just about anyone, but I also know how to avoid them and how to use C++'s strengths.

  In any case, I don't think it is true that the programming languages are so difficult to learn. For example, every first-year university biology textbook contains more details and deeper theory than even an expert-level programming-language book. Most applications involve standards, operating systems, libraries, and tools that far exceed modern programming languages in complexity. What is difficult is the appreciation of the underlying techniques and their application to real-world problems. Obviously, most current languages have many parts that are unnecessarily complex, but the degree of those complexities compared to some ideal minimum is often exaggerated.

  We need relatively complex language to deal with absolutely complex problems. I note that English is arguably the largest and most complex language in the world (measured in number of words and idioms), but also one of the most successful.

  C++ provides a nice, extended case study in the evolutionary approach. C compatibility has been far harder to maintain than I or anyone else expected. Part of the reason is that C has kept evolving, partially guided by people who insist that C++ compatibility is neither necessary nor good for C. Another reason-- probably even more important--is that organizations prefer interfaces that are in the C/C++ subset so that they can support both languages with a single effort. This leads to a constant pressure on users not to use the most powerful C++ features and to myths about why they should be used "carefully," "infrequently," or "by experts only." That, combined with backwards-looking teaching of C++, has led to many failures to reap the potential benefits of C++ as a high-level language with powerful abstraction mechanisms.

  The question is how deeply integrated into the application those system dependencies are. I prefer the application to be designed conceptually in isolation from the underlying system, with an explicitly defined interface to "the outer world," and then integrated through a thin layer of interface code.

  Had I had a chance to name the style of programming I like best, it would have been "class-oriented programming", but then I'm not particularly good at finding snappy names. The school of thought that I belong to - rooted in Simula and related design philosophies - emphasizes the role of compile-time checking and flexible (static) type systems. Reasoning about the behavior of a program has to be rooted in the (static) structure of the source code. The focus should be on guarantees, invariant, etc. which are closely tied to that static structure. This is the only way I know to effectively deal with correctness. Testing is essential but cannot be systematic and complete without a good internal program structure - simple-minded blackbox testing of any significant system is infeasible because of the exponential explosion of states.

  So, I recommend people to think in terms of class invariants, exception handling guarantees, highly structured resource management, etc. I should add that I intensely dislike debugging (as ah hoc and unsystematic) and strongly prefer reasoning about source code and systematic testing.

  Pros: flexibility, generality, performance, portability, good tool support, available on more platforms than any competitor except C, Access to hardware and system resources, good availability of programmers and designers. Cons: complexity, sub-optimal use caused by poor teaching and myths.

 

posted @ 2008-04-06 12:07 snowball 阅读(1886) | 评论 (4)编辑 收藏

C++中的内存划分

在C++中,内存分成5个区,他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区。

  栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清楚的变量的存储区。里面的变量通常是局部变量、函数参数等。

  堆,就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。

  自由存储区,就是那些由malloc等分配的内存块,他和堆是十分相似的,不过它是用free来结束自己的生命的。

  全局/静态存储区,全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。

  常量存储区,这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改(当然,你要通过非正当手段也可以修改,而且方法很多)

 

posted @ 2008-04-06 12:02 snowball 阅读(346) | 评论 (1)编辑 收藏

Memset ,memcpy,strcpy 区别

Memset 用来对一段内存空间全部设置为某个字符,一般用在对定义的字符串进行初始化为 ‘ ’ ‘\0’

:char a[100];memset(a, '\0', sizeof(a));

    memset 可以方便的清空一个结构类型的变量或数组。

如:

struct sample_struct
{
char csName[16];
int iSeq;
int iType;
};

对于变量
struct sample_strcut stTest;

一般情况下,清空 stTest 的方法:

stTest.csName[0]='\0';
stTest.iSeq=0;
stTest.iType=0;

memset 就非常方便:
memset(&stTest,0,sizeof(struct sample_struct));

如果是数组:

struct sample_struct TEST[10];

memset(TEST,0,sizeof(struct sample_struct)*10);

memcpy 用来做内存拷贝,你可以拿它拷贝任何数据类型的对象,可以指定拷贝的数据长度。

例: char a[100],b[50]; memcpy(b, a, sizeof(b)); 注意如用 sizeof(a) ,会造成 b 的内存地址溢出。

Strcpy   就只能拷贝字符串了,它遇到 '\0' 就结束拷贝。

例: char a[100],b[50];strcpy(a,b); 如用 strcpy(b,a) ,要注意 a 中的字符串长度(第一个 ‘\0’ 之前)是否超过 50 位,如超过,则会造成 b 的内存地址溢出。

str 也可以用用个参数的 strncpy(a,b,n)

========================================================

memset 主要应用是初始化某个内存空间。
memcpy
是用于 copy 源空间的数据到目的空间中。
strcpy
用于字符串 copy, 遇到 ‘\0’ ,将结束。

如果你理解了这些,你应该知道他们的区别:例如你初始化某块空间的时候,用到 memcpy ,那么应该怎么写,是不是显得很笨。
int m[100]
memset((void*)m,0x00,sizeof(int)*100);//Ok

memcpy((void*)m,"\0\0\0\0....",sizeof(int)*100);//it’s wrong.
reference : http://hi.baidu.com/%B5%CE%C9%B3/blog/item/12025c2af5ffc33c5343c19f.html

posted @ 2008-04-06 10:31 snowball 阅读(398) | 评论 (0)编辑 收藏

2008年4月3日

高效人士的做事方法

1.主动获得工作计划,达到在全局思维上对自已所做的事情有一个主动的感觉。(比如相关的文档,web 连接,联系人,相关术语)
2.锻练身体(跑步,太极拳)
3.学习英语(晨读,听力)

posted @ 2008-04-03 08:55 snowball 阅读(274) | 评论 (0)编辑 收藏

2008年4月2日

.net中的正则表达式使用高级技巧

     摘要: reference :http://www.cnblogs.com/thinhunan/archive/2007/08/21/339316.html.net中的正则表达式使用高级技巧 前言 ...  阅读全文

posted @ 2008-04-02 13:42 snowball 阅读(497) | 评论 (0)编辑 收藏

C#正则表达式整理备忘

     摘要: reference :http://www.cnblogs.com/KissKnife/archive/2008/03/23/1118423.html有一段时间,正则表达式学习很火热很潮流,当时在CSDN一天就能看到好几个正则表达式的帖子,那段时间借助论坛以及Wrox Press出版的《C#字符串和正则表达式参考手册》学习了一些基础的知识,同时也为我在CSDN大概赚了1000分,今天想起来,去找《...  阅读全文

posted @ 2008-04-02 13:40 snowball 阅读(310) | 评论 (0)编辑 收藏

解读C#中的规则表达式[转]

reference : http://www.chinaaspx.com/Comm/Dotnetbbs/Showtopic.aspx?Forum_ID=5&Id=5250&Page=1
多少年来,许多的编程语言和工具都包含对规则表达式的支持,.NET基础类库中包含有一个名字空间和一系列可以充分发挥规则表达式威力的类,而且它们也都与未来的Perl 5中的规则表达式兼容。 

  此外,regexp类还能够完成一些其他的功能,例如从右至左的结合模式和表达式的编辑等。 

  在这篇文章中,我将简要地介绍System.Text.RegularExpression中的类和方法、一些字符串匹配和替换的例子以及组结构的详细情况,最后,还会介绍一些你可能会用到的常见的表达式。 

应该掌握的基础知识 
  规则表达式的知识可能是不少编程人员“常学常忘”的知识之一。在这篇文章中,我们将假定你已经掌握了规则表达式的用法,尤其是Perl 5中表达式的用法。.NET的regexp类是Perl 5中表达式的一个超集,因此,从理论上说它将作为一个很好的起点。我们还假设你具有了C#的语法和.NET架构的基本知识。 

  如果你没有规则表达式方面的知识,我建议你从Perl 5的语法着手开始学习。在规则表达式方面的权威书籍是由杰弗里·弗雷德尔编写的《掌握表达式》一书,对于希望深刻理解表达式的读者,我们强烈建议阅读这本书。 

RegularExpression组合体 
  regexp规则类包含在System.Text.RegularExpressions.dll文件中,在对应用软件进行编译时你必须引用这个文件,例如,csc r:System.Text.RegularExpressions.dll foo.cs命令将创建foo.exe文件,它就引用了System.Text.RegularExpressions文件。 

名字空间简介 
  在名字空间中仅仅包含着6个类和一个定义,它们是: 

  Capture: 包含一次匹配的结果; 

  CaptureCollection: Capture的序列; 

  Group: 一次组记录的结果,由Capture继承而来; 

  Match: 一次表达式的匹配结果,由Group继承而来; 

  MatchCollection: Match的一个序列; 

  MatchEvaluator: 执行替换操作时使用的代理; 

  Regex:编译后的表达式的实例。 

  Regex类中还包含一些静态的方法: 

  Escape: 对字符串中的regex中的转义符进行转义; 

  IsMatch: 如果表达式在字符串中匹配,该方法返回一个布尔值; 

  Match: 返回Match的实例; 

  Matches: 返回一系列的Match的方法; 

  Replace: 用替换字符串替换匹配的表达式; 

  Split: 返回一系列由表达式决定的字符串; 

  Unescape:不对字符串中的转义字符转义。 

简单匹配 
  我们首先从使用Regex、Match类的简单表达式开始学习。 

  Match m = Regex.Match("abracadabra", "(a|b|r)+"); 

  我们现在有了一个可以用于测试的Match类的实例,例如:if (m.Success)... 

  如果想使用匹配的字符串,可以把它转换成一个字符串: 

  Console.WriteLine("Match="+m.ToString()); 

  这个例子可以得到如下的输出: Match=abra。这就是匹配的字符串了。 

字符串的替换 
  简单字符串的替换非常直观。例如下面的语句: 

  string s = Regex.Replace("abracadabra", "abra", "zzzz"); 

  它返回字符串zzzzcadzzzz,所有匹配的字符串都被替换成了zzzzz。 

  现在我们来看一个比较复杂的字符串替换的例子: 

  string s = Regex.Replace(" abra ", @"^\s*(.*?)\s*$", "$1"); 

  这个语句返回字符串abra,其前导和后缀的空格都去掉了。 

  上面的模式对于删除任意字符串中的前导和后续空格都非常有用。在C#中,我们还经常使用字母字符串,在一个字母字符串中,编译程序不把字符“ \” 作为转义字符处理。在使用字符“\”指定转义字符时,@"..."是非常有用的。另外值得一提的是$1在字符串替换方面的使用,它表明替换字符串只能包含被替换的字符串。 

匹配引擎的细节 
  现在,我们通过一个组结构来理解一个稍微复杂的例子。看下面的例子: 

  string text = "abracadabra1abracadabra2abracadabra3"; 

  string pat = @" 

    ( # 第一个组的开始 

     abra # 匹配字符串abra 

     ( # 第二个组的开始 

     cad # 匹配字符串cad 

     )? # 第二个组结束(可选) 

    ) # 第一个组结束 

    + # 匹配一次或多次 

    "; 

  //利用x修饰符忽略注释 

  Regex r = new Regex(pat, "x"); 

  //获得组号码的清单 

  int[] gnums = r.GetGroupNumbers(); 

  //首次匹配 

  Match m = r.Match(text); 

  while (m.Success) 

   { 

  //从组1开始 

   for (int i = 1; i < gnums.Length; i++) 

    { 

    Group g = m.Group(gnums[i]); 

  //获得这次匹配的组 

    Console.WriteLine("Group"+gnums[i]+"=["+g.ToString()+"]"); 

  //计算这个组的起始位置和长度 

    CaptureCollection cc = g.Captures; 

    for (int j = 0; j < cc.Count; j++) 

     { 

     Capture c = cc[j]; 

     Console.WriteLine(" Capture" + j + "=["+c.ToString() 

       + "] Index=" + c.Index + " Length=" + c.Length); 

     } 

    } 

  //下一个匹配 

   m = m.NextMatch(); 

   } 

  这个例子的输出如下所示: 

  Group1=[abra] 

      Capture0=[abracad] Index=0 Length=7 

      Capture1=[abra] Index=7 Length=4 

  Group2=[cad] 

      Capture0=[cad] Index=4 Length=3 

  Group1=[abra] 

      Capture0=[abracad] Index=12 Length=7 

      Capture1=[abra] Index=19 Length=4 

  Group2=[cad] 

      Capture0=[cad] Index=16 Length=3 

  Group1=[abra] 

      Capture0=[abracad] Index=24 Length=7 

      Capture1=[abra] Index=31 Length=4 

  Group2=[cad] 

      Capture0=[cad] Index=28 Length=3 

  我们首先从考查字符串pat开始,pat中包含有表达式。第一个capture是从第一个圆括号开始的,然后表达式将匹配到一个abra。第二个capture组从第二个圆括号开始,但第一个capture组还没有结束,这意味着第一个组匹配的结果是abracad ,而第二个组的匹配结果仅仅是cad。因此如果通过使用?符号而使cad成为一项可选的匹配,匹配的结果就可能是abra或abracad。然后,第一个组就会结束,通过指定+符号要求表达式进行多次匹配。 

  现在我们来看看匹配过程中发生的情况。首先,通过调用Regex的constructor方法建立表达式的一个实例,并在其中指定各种选项。在这个例子中,由于在表达式中有注释,因此选用了x选项,另外还使用了一些空格。打开x选项,表达式将会忽略注释和其中没有转义的空格。 

  然后,取得表达式中定义的组的编号的清单。你当然可以显性地使用这些编号,在这里使用的是编程的方法。如果使用了命名的组,作为一种建立快速索引的途径这种方法也十分有效。 

  接下来是完成第一次匹配。通过一个循环测试当前的匹配是否成功,接下来是从group 1开始重复对组清单执行这一操作。在这个例子中没有使用group 0的原因是group 0是一个完全匹配的字符串,如果要通过收集全部匹配的字符串作为一个单一的字符串,就会用到group 0了。 

  我们跟踪每个group中的CaptureCollection。通常情况下每次匹配、每个group中只能有一个capture,但本例中的Group1则有两个capture:Capture0和Capture1。如果你仅需要Group1的ToString,就会只得到abra,当然它也会与abracad匹配。组中ToString的值就是其CaptureCollection中最后一个Capture的值,这正是我们所需要的。如果你希望整个过程在匹配abra后结束,就应该从表达式中删除+符号,让regex引擎知道我们只需要对表达式进行匹配。 

基于过程和基于表达式方法的比较 
  一般情况下,使用规则表达式的用户可以分为以下二大类:第一类用户尽量不使用规则表达式,而是使用过程来执行一些需要重复的操作;第二类用户则充分利用规则表达式处理引擎的功能和威力,而尽可能少地使用过程。 

  对于我们大多数用户而言,最好的方案莫过于二者兼而用之了。我希望这篇文章能够说明.NET语言中regexp类的作用以及它在性能和复杂性之间的优、劣点。 

基于过程的模式 
  我们在编程中经常需要用到的一个功能是对字符串中的一部分进行匹配或其他一些对字符串处理,下面是一个对字符串中的单词进行匹配的例子: 

  string text = "the quick red fox jumped over the lazy brown dog."; 

  System.Console.WriteLine("text=[" + text + "]"); 

  string result = ""; 

  string pattern = @"\w+|\W+"; 

  foreach (Match m in Regex.Matches(text, pattern)) 

   { 

  // 取得匹配的字符串 

   string x = m.ToString(); 

  // 如果第一个字符是小写 

   if (char.IsLower(x[0])) 

  // 变成大写 

    x = char.ToUpper(x[0]) + x.Substring(1, x.Length-1); 

  // 收集所有的字符 

   result += x; 

   } 

  System.Console.WriteLine("result=[" + result + "]"); 

  正象上面的例子所示,我们使用了C#语言中的foreach语句处理每个匹配的字符,并完成相应的处理,在这个例子中,新创建了一个result字符串。这个例子的输出所下所示: 

  text=[the quick red fox jumped over the lazy brown dog.] 

  result=[The Quick Red Fox Jumped Over The Lazy Brown Dog.] 

基于表达式的模式 
  完成上例中的功能的另一条途径是通过一个MatchEvaluator,新的代码如下所示: 

  static string CapText(Match m) 

    { 

  //取得匹配的字符串 

    string x = m.ToString(); 

  // 如果第一个字符是小写 

    if (char.IsLower(x[0])) 

  // 转换为大写 

     return char.ToUpper(x[0]) + x.Substring(1, x.Length-1); 

    return x; 

    } 

     

   static void Main() 

    { 

    string text = "the quick red fox jumped over the 

     lazy brown dog."; 

    System.Console.WriteLine("text=[" + text + "]"); 

    string pattern = @"\w+"; 

    string result = Regex.Replace(text, pattern, 

   new MatchEvaluator(Test.CapText)); 

    System.Console.WriteLine("result=[" + result + "]"); 

    } 

  同时需要注意的是,由于仅仅需要对单词进行修改而无需对非单词进行修改,这个模式显得非常简单。 

常用表达式 
  为了能够更好地理解如何在C#环境中使用规则表达式,我写出一些对你来说可能有用的规则表达式,这些表达式在其他的环境中都被使用过,希望能够对你有所帮助。 

罗马数字 
  string p1 = "^m*(d?c{0,3}|c[dm])" + "(l?x{0,3}|x[lc])(v?i{0,3}|i[vx])$"; 

  string t1 = "vii"; 

  Match m1 = Regex.Match(t1, p1); 

交换前二个单词 
  string t2 = "the quick brown fox"; 

  string p2 = @"(\S+)(\s+)(\S+)"; 

  Regex x2 = new Regex(p2); 

  string r2 = x2.Replace(t2, "$3$2$1", 1); 

关健字=值 
  string t3 = "myval = 3"; 

  string p3 = @"(\w+)\s*=\s*(.*)\s*$"; 

  Match m3 = Regex.Match(t3, p3); 

实现每行80个字符 
  string t4 = "********************" 

   + "******************************" 

   + "******************************"; 

  string p4 = ".{80,}"; 

  Match m4 = Regex.Match(t4, p4); 

月/日/年 小时:分:秒的时间格式 
  string t5 = "01/01/01 16:10:01"; 

  string p5 = @"(\d+)/(\d+)/(\d+) (\d+):(\d+):(\d+)"; 

  Match m5 = Regex.Match(t5, p5); 

改变目录(仅适用于Windows平台) 
string t6 = @"C:\Documents and Settings\user1\Desktop\"; 

string r6 = Regex.Replace(t6,@" \\user1\\ ", @" \\user2\\ "); 

扩展16位转义符 
  string t7 = "%41"; // capital A 

  string p7 = "%([0-9A-Fa-f][0-9A-Fa-f])"; 

  string r7 = Regex.Replace(t7, p7, HexConvert); 

删除C语言中的注释(有待完善) 
  string t8 = @" 

  /* 

   * 传统风格的注释 

   */ 

  "; 

  string p8 = @" 

   /\* # 匹配注释开始的定界符 

   .*? # 匹配注释 

   \*/ # 匹配注释结束定界符 

  "; 

  string r8 = Regex.Replace(t8, p8, "", "xs"); 

删除字符串中开始和结束处的空格 
  string t9a = " leading"; 

  string p9a = @"^\s+"; 

  string r9a = Regex.Replace(t9a, p9a, ""); 

  string t9b = "trailing "; 

  string p9b = @"\s+$"; 

  string r9b = Regex.Replace(t9b, p9b, ""); 

  在字符\后添加字符n,使之成为真正的新行 

  string t10 = @"\ntest\n"; 

  string r10 = Regex.Replace(t10, @" \\n ", "\n"); 

转换IP地址 
  string t11 = "55.54.53.52"; 

  string p11 = "^" + 

   @"([01]?\d\d|2[0-4]\d|25[0-5])\." + 

   @"([01]?\d\d|2[0-4]\d|25[0-5])\." + 

   @"([01]?\d\d|2[0-4]\d|25[0-5])\." + 

   @"([01]?\d\d|2[0-4]\d|25[0-5])" + 

   "$"; 

  Match m11 = Regex.Match(t11, p11); 

删除文件名包含的路径 
  string t12 = @"c:\file.txt"; 

  string p12 = @"^.*\\"; 

  string r12 = Regex.Replace(t12, p12, ""); 

联接多行字符串中的行 
  string t13 = @"this is 

  a split line"; 

  string p13 = @"\s*\r?\n\s*"; 

  string r13 = Regex.Replace(t13, p13, " "); 

提取字符串中的所有数字 
  string t14 = @" 

  test 1 

  test 2.3 

  test 47 

  "; 

  string p14 = @"(\d+\.?\d*|\.\d+)"; 

  MatchCollection mc14 = Regex.Matches(t14, p14); 

找出所有的大写字母 
  string t15 = "This IS a Test OF ALL Caps"; 

  string p15 = @"(\b[^\Wa-z0-9_]+\b)"; 

  MatchCollection mc15 = Regex.Matches(t15, p15); 

找出小写的单词 
  string t16 = "This is A Test of lowercase"; 

  string p16 = @"(\b[^\WA-Z0-9_]+\b)"; 

  MatchCollection mc16 = Regex.Matches(t16, p16); 

找出第一个字母为大写的单词 
  string t17 = "This is A Test of Initial Caps"; 

  string p17 = @"(\b[^\Wa-z0-9_][^\WA-Z0-9_]*\b)"; 

  MatchCollection mc17 = Regex.Matches(t17, p17); 

找出简单的HTML语言中的链接 
  string t18 = @" 

  <html> 

  <a href=""first.htm"">first tag text</a> 

  <a href=""next.htm"">next tag text</a> 

  </html> 

  "; 

  string p18 = @"<A[^>]*?HREF\s*=\s*[""']?" + @"([^'"" >]+?)[ '""]?>"; 

  MatchCollection mc18 = Regex.Matches(t18, p18, "si");

posted @ 2008-04-02 13:37 snowball 阅读(228) | 评论 (0)编辑 收藏

仅列出标题  下一页

导航

留言簿(1)

随笔分类

友情链接

搜索

最新随笔

最新评论

阅读排行榜