Wednesday, January 30, 2008

How to customize my VIM

To have highlight in programming, follow this

1. Create a .vimrc in your $HOME directory:

vim ~/.vimrc

2. Put all the statements in it:

syntax on
set number
set wrapmargin=1
...

Now syntax highlighter and numbering should be on.


"
syntax on
set number
set wrapmargin=1
set autoindent
set smartindent
set nocompatible
set showmatch
set ruler
set nohls
set showcmd
set showmode
set cindent
"

Above is how my .vimrc looks like.

set nocompatible

This setting prevents vim from emulating the original vi's bugs and limitations.

set autoindent
set smartindent

The first setting tells vim to use "autoindent" (that is, use the current line's indent level to set the indent level of new lines). The second makes vim attempt to intelligently guess the indent level of any new line based on the previous line, assuming the source file is in a C-like language. Combined, they are very useful in writing well-formatted source code.

set showmatch

This setting will cause the cursor to very briefly jump to a brace/parenthese/bracket's "match" whenever you type a closing or opening brace/parenthese/bracket. I've had almost no mismatched-punctuation errors since I started using this setting.

set ruler

This setting ensures that each window contains a statusline that displays the current cursor position.

set nohls

By default, search matches are highlighted. I find this annoying most of the time. This option turns off search highlighting. You can always turn it back on with :set hls.





More info can be obtained from Google : )

and

http://vimdoc.sourceforge.net/htmldoc/usr_toc.html

http://www.vi-improved.org/vimrc.php

http://vim.sourceforge.net/index.php

Some comments

Since I am doing network programming, I gradually realize that it is not as "bad" as I thought before -- programming can be interesting :)

but at first, practising, training, and updating !!!

Unix network programming stuff

Official site:
http://www.opengroup.org/onlinepubs/009695399/

try google and TKTL man page : )

IPtables Good links

Official:
http://www.netfilter.org/index.html


RFC:
http://tools.ietf.org/html/rfc1853

Tutorial:
http://iptables-tutorial.frozentux.net/

Steve:
http://www.sns.ias.edu/~jns/wp/2006/01/24/iptables-how-does-it-work/
Michael Rash:
http://www.cipherdyne.org/LinuxFirewalls/

Friday, January 25, 2008

Linux associate name with IP

Edit:
/etc/hosts

Add a line like this:
0.0.0.0 myname

More details:
http://www.usefulutilities.com/support/technote/2l.html

Monday, January 7, 2008

Linux tuxfile

a good link for linux newbies:

http://www.tuxfiles.org/

have fun ~

Friday, January 4, 2008

link to file orgnization in c

A very good article I found when conducting my c project.

http://www.gamedev.net/reference/articles/article1798.asp

Wednesday, January 2, 2008

Stack and heaps

Very good article on stack & heaps from CSDN blog.


堆和栈的区别 (可能做为笔试题目) CSDN Blog推出文章指数概念,文章指数是对Blog文章综合评分后推算出的,综合评分项分别是该文章的点击量,回复次数,被网摘收录数量,文章长度和文章类型;满分100,每月更新一次。
一、预备知识—程序的内存分配
一个由c/C++编译的程序占用的内存分为以下几个部分
1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放
4、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放
5、程序代码区—存放函数体的二进制代码。
二、例子程序
这是一个前辈写的,非常详细
//main.cpp
int a = 0; 全局初始化区
char *p1; 全局未初始化区
main()
{
int b; 栈
char s[] = "abc"; 栈
char *p2; 栈
char *p3 = "123456"; 123456\0在常量区,p3在栈上。
static int c =0; 全局(静态)初始化区
p1 = (char *)malloc(10);
p2 = (char *)malloc(20);
分配得来得10和20字节的区域就在堆区。
strcpy(p1, "123456"); 123456\0放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。
}


二、堆和栈的理论知识
2.1申请方式
stack:
由系统自动分配。 例如,声明在函数中一个局部变量 int b; 系统自动在栈中为b开辟空间
heap:
需要程序员自己申请,并指明大小,在c中malloc函数
如p1 = (char *)malloc(10);
在C++中用new运算符
如p2 = (char *)malloc(10);
但是注意p1、p2本身是在栈中的。


2.2
申请后系统的响应
栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。
堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,
会 遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内 存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大 小,系统会自动的将多余的那部分重新放入空闲链表中。

2.3申请大小的限制
栈:在Windows下,栈是向低地址扩展的数 据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(也有的说是1M,总 之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。
堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。


2.4申请效率的比较:
栈由系统自动分配,速度较快。但程序员是无法控制的。
堆是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.
另外,在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆,也不是在栈是直接在进程的地址空间中保留一快内存,虽然用起来最不方便。但是速度快,也最灵活。

2.5堆和栈中的存储内容
栈: 在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。
当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。
堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。

2.6存取效率的比较

char s1[] = "aaaaaaaaaaaaaaa";
char *s2 = "bbbbbbbbbbbbbbbbb";
aaaaaaaaaaa是在运行时刻赋值的;
而bbbbbbbbbbb是在编译时就确定的;
但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。
比如:
#include
void main()
{
char a = 1;
char c[] = "1234567890";
char *p ="1234567890";
a = c[1];
a = p[1];
return;
}
对应的汇编代码
10: a = c[1];
00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh]
0040106A 88 4D FC mov byte ptr [ebp-4],cl
11: a = p[1];
0040106D 8B 55 EC mov edx,dword ptr [ebp-14h]
00401070 8A 42 01 mov al,byte ptr [edx+1]
00401073 88 45 FC mov byte ptr [ebp-4],al
第一种在读取时直接就把字符串中的元素读到寄存器cl中,而第二种则要先把指针值读到edx中,在根据edx读取字符,显然慢了。


2.7小结:
堆和栈的区别可以用如下的比喻来看出:
使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。
使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。 (经典!)

CS truth and illussion

From CSDN :



一、计算机的本质

计算机的本质是一个复杂的数字电路,数字电路只能处理和输出高低两种电位组成的信号,所以计算机只认识高低两种电位状态!

任 何人类的观念(比如数字,字符,图像等),在计算机里都被转换成这两种状态,再通过硬件电路或者程序的方式,使对这些状态的输出,符合这些观念本身应有的 规律和形式,因为人看不到内部的转换和处理,只看到外部输出的样子,于是,在人看来,计算机可以完成多种多样的任务,相当神奇!

计算机能够完成多种任务的两大基石:

1.编码:用计算机的高低电位对应外部事物的各种状态。

2.输出:以外部事物的样子把编码处理后的结果展示出来。(可以通过硬件电路也可以通过软件程序)

编码的概念:

用少量简单基本的符号,选用一定的组合规则,以表示出大量复杂多样的信号。基本符号的种类和这些符号的组合则是一切信息编码的两大要素,例如用10个阿拉伯数码表示数字,用26个英文字母表示英文词汇等,这就是编码的典型案例。计算机中,广泛采用01两个基本符号组成的基2,或称二进制码。(一般用高低两个电位表示10

--《计算机组成与结构》

二、具体事例

1.字符

编码:以ASCII字符集编码为例,将数字、大小写字母、标点符号、控制字符等128个符号,对应到 0x0 0x7e 上。也就是说用 0x0 - 0x7e 128个状态编码了128个符号。

输出:早期的计算机通过符号显示器这一硬件来将字符显示出来。比如当接收到计算机发过来的一个字节 0x58 时,crt将根据0x58指向的字符rom中的高低电位控制电子束的开关,在屏幕上组成图形“X”。

2.数值

编码:数值是表示数量多少的数据。对数值数据的编码,计算机中有:原码,补码,反码。

处理,计算机的运算电路,根据数值编码方案设计,使输入状态经过电路处理后得到的输出状态符合加减乘除等运算规则。比如,输入 ▂ 和 ▂ 输出 █ █ ,符合 10 + 01 = 11

输出:将结果数字转换成ascii编码,送字符显示设备。比如将 5 )转换成ascii字符 5 ▂ ▂ █ █ ▂ █ ▂ █),然后送显卡画图。

3.图像

编码:将点的位置和颜色等信息,对应到一定的高低电位序列,实现编码。

输出:显示设备根据点的信息,在显示器上输出一个点,大量的点按一定规则排列,形成人们观念中的图像。

三、一个重要理念:计算机是一部状态机,不是数字机。

计算机不认识数字,只认识高低两个状态。

数字只表示高低电位的排列状态。比如,0xb8 ,它并不代表数量,只代表电位排列状态,化成二进制为:10111000 ,就是代表: █ █ █ ▂ 这一状态。

自然界中数字的概念:

表示事物个数的符号。

进制的概念:

数学上的进位制本来是人们为了计数和运算的方便而约定的,约定逢二进一,就是二进制,约定逢三进一,就是三进制,依次类推。不同的进位制,除了繁简的差异外,没有任何本质上的区别。也就是说,不同的进制,只是自身的形式不同,其表示数目的本质是一样的,比如二进制100与十六进制4 都表示一样的数目,他们本质是一样的,仅仅形式不同。以计算机举例,内存中有 █ █ ,你可以用二进制的概念叫他为 1011 ,亦可以以十六进制叫他为 b ,也可以以十进制叫他为 11 。无论怎么叫,他内存中的状态始终不变。

因为计算机里只有高低两种状态,所以数字无论是二进制还是十六进制,只是这种状态排列的一种表示方式。在这里数字已经失去了它本来的表示数量功能的意义了,取而代之,这里表示电位排列状态。

我比较习惯把高低电位用符号表示,而不用01.因为用数字0 1 来表示高低电位,潜在的意思是:这个东西是数字,而数字是符合运算规则的。其实,计算机内部很多东西都不是数字,比如mov指令: █ █ █ ▂ ,它并不代表一个数值,只是一个cpu能识别的电位序列信号。如果用0 1 来表示就是,10111000 ,看上去就是一个数字。还有,是否符合运算规则是内部电路来决定的,如果电路设计成 输入 ▂ 和 ▂ 输出 █ █ ,那么就符合加法运算,但如果设计成输出 ▂ ▂ 呢,再表示成输入:10 01 ,输出:00 就比较别扭了。

如果克服这些障碍,把 0 1 仅仅看成是计算机的两种状态,那么表示起来还是很方便的,因为这时不仅可以用 0 1 ,还可以转化成更方便的十六进制,象上面那条 mov 指令就可以表示成 10111000 或者 十六进制 b8 。但要记住,10111000 也好 b8 也好,只是内存中高低电位状态分布的一种表示,并不表示数量。

知道这些,就容易理解:汇编程序在汇编过程中,直接查表把ASCII 码组成的 mov 汇编成 0xb8,这里如果把 0xb8 看成一个数字,就容易迷惑,把它想象成一个高低电位分布的状态,就容易理解了。

四、ASCII 里的数字字符,与真正的数字,有时让人迷惑。

以无符号整数5为例,在计算机屏幕上看到的5,与计算机内部以原码表示的数值5是不同的。在计算机屏幕上显示的5是一个字符,它以ASCII编码,在计算机内部的电位序列为: ▂ ▂ █ █ (二进制 0011 0101 十六进制0x 35)。

而计算机内部原码表示的数字 5 电位序列为:▂ ▂ ▂ ▂ ▂ (二进制 0000 0101 十六进制0x 5)。

就通常的输入手段来说,通过键盘,在编辑器里输入,不能手工输入机器数 5 ,因为编辑器程序把键盘输入的扫描码转换成了ascii编码,但老式计算机可以通过开关,打孔等输入方式输入机器数。

要想得到机器数5 ,必须借助编译器等第三方程序。比如汇编语句“ db 5 ,这里的 5 仍然是一个ASCII 码,通过汇编程序处理后,才在内存中得到真正的机器数 5

c语句“int x=5这里的5也是ASCII ,编译后才是真正的5

计算机语言的源程序,都是ASCII码组成的,尽管如此,他们还是会提供一些语法,以便确定编译后,你到底需要ASCII还是真实的数字,比如c char x = 5;与 char x = 5’;

总之,人眼可以看到的数字,就是以ASCII形式编码的字符,而真正数字的机器码,人眼是看不到的。一般计算机语言都会提供一种语法,来确定最后编译得到的是哪种形式的编码。

五、汇编与反汇编

汇编过程:

语句“ mov ax 5 ”在机器内部的表示为一堆ASCII编码:

符号形式 二进制 十六进制

m▂ █ █ ▂ █ █ ▂ █ 01101101 6d

o▂ █ █ ▂ █ █ █ █ 01101111 6f

v▂ █ █ █ ▂ █ █ ▂ 01110110 76

空格( ▂ ▂ █ ▂ ▂ ▂ ▂ ▂ 00100000 20

a▂ █ █ ▂ ▂ ▂ ▂ █ 01100001 61

x▂ █ █ █ █ ▂ ▂ ▂ 01111000 78

,( ▂ ▂ █ ▂ █ █ ▂ ▂ 00101100 2c

5▂ ▂ █ █ ▂ █ ▂ █ 00110101 35

经过汇编程序得到cpu可以运行的真实的指令:

符号形式: █ █ █

二进制形式:101110000000010100000000

十六进制形式:0x b80500

反汇编过程,这里只反汇编为16进制数表示的结果:

还是这个语句:

█ █ █

以半字节为单位,转换为16进制数的ASCII

█ █ 0x b 转换为ASCII 编码的 “b0x62 为:█ █

0x 8 转换为ASCII 编码的 “8 0x38为: █ █ █ ▂ ▂

0x 0 转换为ASCII 编码的 “00x30为:▂ ▂ █ █

0x 5 转换为ASCII 编码的 “50x35为:▂ ▂ █ █

0x 0 转换为ASCII 编码的 “00x30为:▂ ▂ █ █

0x 0 转换为ASCII 编码的 “00x30为:▂ ▂ █ █

把这些反汇编后的数据送到显存,屏幕上就显示出ASCII b80500 了。

CS Abstraction

一.万事万物皆抽象

1. 人的智力是有限的,世界是越来越复杂的,人之所以能够用有限的智力建设并操控越来越复杂的世界靠的是层层抽象。

2. 原理:(建造大楼为例)

a. 最原始的状态:人的所有精力都放在研究制造砖头上,此时没有也不可能顾及如何造大楼(因为人的智力是有限的)

b. 砖头造出来后,人就不再考虑砖头,把砖头当作原料进行更高一层的抽象,开始把所有精力放在用砖头造墙上。

c. 墙造出来后,人就不再考虑墙,把墙当作原料进行更高一层的抽象,开始把所有精力放在用墙造房间上。

………………

最后造成大楼。



计 算机科学,层的概念特别突出(与其自身复杂性有关),例如:汇编层、高级语言层…,操作系统层、网络层…,每一层都有自己关心的问题、对象及处理方法,并 对低一层的结论成果等直接拿来用,而不考虑其构成(那是低一层次要做的事)。面向过程编程中的自上而下层层分解的方法,也属于此范围。

每一个单独的层面如果仍十分巨大的话,还可以按逻辑功能把这一层分成一个个独立的模块,使思维规模进一步缩小。比如在操作系统层面,可以分成:引导模块、中断模块、进程处理模块。。。

不按照每一层面每一模块上特定的规则处理问题,企图越过层面和模块办事,必然会引起混乱。

进一步说明:在做操作系统时,主要考虑的是机器的组成,工作方式,指令集,在操作时也主要使用汇编和c这样的底层、细微的工具。高层面的概念不用考虑。

而在操作系统的基础上做应用开发时,主要考虑的是系统提供的功能调用,问题对象的描述等。使用的工具也是更高级的,如object pascal,c++,basic,python等等。低层次的概念如寄存器、内存地址等概念不需要考虑(不一个层级,属于低层)。

问题的粒度不同,使用的知识与工具也不相同。

不可纠缠于细节,应该在一个层次上熟练以后,向更高级的层面延伸。如在汇编层面掌握以后,应该用汇编的知识开发操作系统和高级语言编译器(如c),然后向操作系统和高级语言层迈进,在这个层面熟练以后,应该开发组件和解释型语言,然后向这个层面进军。




当你被各种新奇的概念、瑰丽的界面和无休止的名词弄的筋疲力尽的时候,闭上眼睛,想象一下内存中机械的、冰冷的一条条高高低低的电位指令,想想地址、寄存器、callmovinout指令,你会发现:所有概念都是人自己创造的,真实的世界原来是那么的单纯明了。

·高级语言如:cc++java,都是假的。高级的概念如:段、对象、组件都是假的。他们最终都要变成内存中一个个机器指令(电信号)。

·闭上眼睛,想象下操作系统、dll模块、编译器、链接器、载入器、各个api函数、在内存中都是一块一块的指令集合。你编的程序进行编译、链接、装载、系统调用时,其实就是在这些指令块里面callret去,就是跟这些地址打交道。看看c函数编译出来的汇编吧,就是push,然后call

·当你试图弄清一个层面的问题时,往往需要借助更深层面的知识。比如c语言的函数其实就是汇编里的几个pushcall

·程序的本质在“序”。几个简单的有限的东西通过不同的排列(序),可以构成复杂而强大的东西。比如26个字母构成英语、简单的机器指令构成缤纷的软件。(这种思想是朴实而自然的,比如简单的砖头和泥灰可以盖成各式各异的建筑,简单的原子构成复杂的世界。古代有“道生一,一生二,二生三,三生万物”的说法,可见,图灵的通用计算机思想也没有什么新奇之处。)

·计算机学科的基石。

思想方面:

1. 协议。就是大家都遵从的一种约定,这样才能把力量用在一处。数据结构也是协议。

2. 模块化。把能复用的东西,抽出来,避免重复劳动。子程序(过程,函数),就是最好的例子。

·人应该做有创造性的工作,单调重复乏味的事情让计算机去做。

About pointers of C

good article on c pointer.


c与机器联系紧密,抽象程度不是太高,所以机器层面的地址也拿过来了,改名叫指针。

遇到指针,要读成“地址变量”,其实指针就是一个存放整数(地址就是一个整数)的变量而已,多少级都是如此。

一、定义一个指针 int * p 与访问指针指向的变 * p

这两个语句里面的 * ,是两个不同的符号,要区别对待,不要把他看成一样的东西。第一个*表示:要定义变量p是个存放地址的变量。第二个*表示:要寻址了,要找p里存放的地址指向的变量了。

二、语句int *p;有三个意思:

1. p是一个变量,用来存放地址。

2. 需要几次寻址才能找到最终的那个变量(这与有几颗*有关)。

3. 最终指向的那个变量是什么类型的(这里就是 int )。

这里第一个意思最重要,看到一个定义要首先想到这一点。其他两个基本上是编译器检查时需要的信息,在类型转换,赋值时稍微注意下就ok了。

三、当看到:

int *p;

char ****p;

float ************************************p;

等等时,脑子里第一个概念就是:p 是一个地址变量,用来存放地址的。

当看到:

**p;

*p;

****************p;

等等时,脑子里第一个概念就是:要找地址p指向的变量了。

四、见过*连用,如 int ***p;或 **p 但从没见过 & 连用的,如 &&x;这是错的,&只能一个变量一个变量的取地址,如:

int x=0;

int *p=&x;

int **p2=&p;

五、char ***cc;

int *******ii;

他们的共同点: cc ii 都是一个地址变量,用来存放地址,32位机器的地址是32位,所以他们都占4个字节。

六、多重指针之所以让人迷惑,在于它间接了好多次,比如 int ***p;好像c里面有什么神秘的东西,可以一次存储下那个变态的多重指针,然后还可以通过某种神秘的方式,一下子找到那被间接了好多次的变量。其实,从编译后的汇编来看,p就是一个普通的单一的变量,而寻址的过程也是简单的重复而已。具体看下面的分析:

int main()

{

int i;

int *p1;

int **p2;

int ***p3;

int e;

i=1;

p1=&i;

p2=&p1;

p3=&p2;

e = ***p3 ;

}

_i$ = -16

_p1$ = -8

_p2$ = -12

_p3$ = -20

_e$ = -4

……

mov DWORD PTR _i$[ebp], 1

lea eax, DWORD PTR _i$[ebp]

mov DWORD PTR _p1$[ebp], eax

lea ecx, DWORD PTR _p1$[ebp]

mov DWORD PTR _p2$[ebp], ecx

lea edx, DWORD PTR _p2$[ebp]

mov DWORD PTR _p3$[ebp], edx

mov eax, DWORD PTR _p3$[ebp]

mov ecx, DWORD PTR [eax]

mov edx, DWORD PTR [ecx]

mov eax, DWORD PTR [edx]

mov DWORD PTR _e$[ebp], eax

看下绿色的地方,所谓的复杂的 三重指针p3,编译后就是一条简单的赋值语句而已。再看下红色标注的地方,***p3,编译后成了那5条简单的语句(而确切的说是那5条中间的3条,头尾都不算在间接之内),就是一个简单的重复 mov 而已啦。有什么复杂的?!

七、“本来面目”

当碰到十分复杂的指针声明时,如:int ******p; 脑子里只有一个感念,p是一个地址变量,用来存放地址。(这就是他编译后的本来面目)心中默念:100级指针,也是指针,就是一个存放整数(地址就是整数)的变量而已,怕你个p啊。

C and ACM

From Yahoo blog:

推荐几本关于C好书

2007-08-15 19:37

第一本:谭浩强的《C程序设计》。
优点在于,面向中国学生,语言易于理解,而且全书篇幅小,看过的人多,容易找到人请教。但是各方面的概念不深入,浅尝辄止。因此适合第一次入门学习,例题做完基本就可以抛弃了。有实体书和电子版下载。

第二本:Brian W.Kernighan和Dennis M.Ritchie 的《C程序设计语言》。
此书简称K&R,由C语言的创建者撰写,是全球销售最多的C语言教材,其品质不容置疑。不过不适合入门,适于在入门之后巩固提高。实体书有原版和译本购买,电子版英文较多。

第三本:H.M.Deitel和P.J.Deitel的《C程序设计教程》。
作者是父子,从事语言教材的编写合计超过40年,本书被美国众多高校选用为教材,既可入门也可翻阅,甚好。

第四本:王大刚先生的《C语言编程宝典》。
质量不错,与老谭的书处于同一级别。无实体书,只有网络版,在本群论坛有转载:http://www.vcgood.com/bbs/forum_posts.asp?TID=1641&PN=1

第五本:无名作者的《C语言初学者入门讲座》
本群论坛转载,作者未知,质量同上一本。http://www.vcgood.com/bbs/forum_posts.asp?TID=1294&PN=4

第六本:Kenneth A.Reek 的《C和指针 》
进阶读物,把指针一章演绎为一本书可见其技术精度。指针是C的灵魂,能领会本书中的理念,才能算是真正明白了C指针的奥妙,才算懂得了C。

第七本:Andrew Koenig 的 《C陷阱与缺陷》
进阶读物,在认真读完上面的入门书之前,暂勿阅读。本书畅销14年历久不衰,足证品质。

最后一本:ISO C89规范/C99规范
一切关于C语言的疑问,只有一件东西最权威,那就是ISO的规范。但这不是正式出版物,是类似法律文本的技术说明,而且只有英文版。新手阅读,提防吐血而亡。


光看书似乎不解决问题,难道要做题
当然要做题,而且做题也要循序渐进。
首先是教材正文中的示例,然后是书后的练习题。
第二步可以做国家等级考试二级三级的C语言题。在线上可以找到,大量二级题库。三级题推荐南开的三级100题。此外本群共享文件中也有一些这一难度的题目。
正如前面讲过,当第二步的题目都做完后,语法已经不是问题,应该开始学习数据结构和算法。这个方面的练习题可以去各大OJ。OJ是什么,请看下一节。


OJ是什么
OJ是OnlineJudge的缩写。OnlineJudge是一种在线裁判系统。她可以对程序原代码进行编译和执行,并通过预先设计的测试数据来检验程序原代码的正确性。
首先,他是一个在线的题库,有很多习题,你可以任选其中之一来解答。然后,系统的编译器能够编译执行你所提交的代码。如果编译通过,针对每个题目,系统将 使用自带的多组测试数据检验你的程序,如果程序都能得到正确的输出,那么你顺利地解决了这个问题。最后,系统还能对所有参与答题的用户进行统计和排名。
OJ的题目大部分是关于算法的。题目的输入输出通常是命令行方式,而非图形界面。也就是说,要关注的不是平台的兼容性、文件的格式抑或窗口的布置这种无关 紧要的细节,而是问题本身的逻辑实现。一个用户提交的程序在Online Judge系统下执行时将受到比较严格的限制,包括运行时间限制,内存使用限制和安全限制等。用户程序执行的结果将被Online Judge系统捕捉并保存,然后再转交给一个裁判程序。该裁判程序或者比较用户程序的输出数据和标准输出样例的差别,或者检验用户程序的输出数据是否满足 一定的逻辑条件。最后系统返回给用户一个状态,通过、错误、超时、溢出或是无法编译。
Online Judge系统来自ACM和IOI大赛。由美国计算机协会(ACM Association for Computing Machinery)发起和组织的ACM国际大学生程序设计竞赛(简称ACM/ICPC)是目前世界上规模最大的计算机学科赛事。IOI则是国际信息学奥 林匹克竞赛,和数学奥林匹克竞赛一样著名。Online Judge系统是IOI或ACM的训练题库,同时也是ACM和IOI的评分系统。


哪里有可用的OJ
以下几个OJ都很不错,请随意进入。
myOJ http://myoj.kuye.cn/
建站人员自称是专为中学生OI选手训练使用,由中学生和中学教师共同开发的,也是给中学生和中学教师使用的。优点是题目难度较低,而且题目都是中文版易于阅读。

浙江大学宁波理工学院 http://acm.nit.net.cn/
也是适合新手的OJ,题目难度由低到高不等。中文题不少。

USACO http://ace.delos.com/usacogate/
全美计算机奥林匹克竞赛(USACO)的训练网站,特点是做完一关才能继续往下做,与前面的OJ不同的是测试数据可以看到,并且做对后可以看标准解答,所 以如果大家刚开始的时候在上面那些OJ上总WA却找不到原因的话,可以试着来这里做做,看看测试数据一般是从什么地方阴你的。
以上三个推荐新手前往。

浙江大学的Online Judge http://acm.zju.edu.cn/
国内最早也是最有名气的OJ,有很多高手在上面做题。特点是数据比较刁钻,经常会有你想不到的边界数据,很能考验思维的全面性。

北京大学的Online Judge http://acm.pku.edu.cn/
建立较ZOJ晚一些,但题目加得很快,现在题数和ZOJ不相上下,特点是举行在线比赛比较多,数据比ZOJ上的要弱,有时候同样的题同样的程序,在ZOJ上WA(wrong answer),在POJ上就能AC(accomplished)

西班牙的Universidad de Valladolid http://acm.uva.es/
世界上最大最有名的OJ,题目巨多而且巨杂,数据也很刁钻,全世界的顶尖高手都在上面。据说如果你能在UVA上AC一千道题以上,就尽管向IBM、微软什么的发简历吧,绝对不会让你失望的。

俄罗斯乌拉尔大学 http://acm.timus.ru/
也是一个老牌的OJ,题目不多,但题题经典。


哪里有代码示例可看
这个可以自己google或baidu。
如果你已经能够做一些OJ的题了,那么可以考虑看一些开源软件的代码。http://sourceforge.net/ 是全球最大的开源软件集中地,确定一个应用主题上去找就会有收获。

About dog

From CSDN, very impressive.
 薪酬与绩效(转载)     

一条猎狗将兔子赶出了窝,一直追赶他,追了很久仍没有捉到。
  牧羊看到此种情景,讥笑猎狗说“你们两个之间小的反而跑得快得多。“
  猎狗回答说:“你不知道我们两个的跑是完全不同的!我仅仅为了一顿饭而跑,他却是为了性命而跑呀!“
  .....................目标................................
  
  
  二
  
   这话被猎人听到了,猎人想:猎狗说的对啊,那我要想得到更多的猎物,得想个好法子. 于是,猎人又买来几条猎狗,凡是能够在打猎中捉到兔子的,就可以得到几根骨头,捉不到的就没有饭吃.这一招果然有用,猎狗们纷纷去努力追兔子,因为谁都不 愿意看着别人有骨头吃,自已没的吃.就这样过了一段时间,问题又出现了.大兔子非常难捉到,小兔子好捉.但捉到大兔子得到的奖赏和捉到小兔子得到的骨头差 不多,猎狗们善于观察,发现了这个窍门,专门去捉小兔子.慢慢的,大家都发现了这个窍门.猎人对猎狗说:最近你们捉的兔子越来越小了,为什么?猎狗们说: 反正没有什么大的区别,为什么费那么大的劲去捉那些大的呢?
  .............................动力.........................
  
  
  三
  
   猎人经过思考后,决定不将分得骨头的数量与是否捉到兔子挂钩,而是采用每过一段时 间,就统计一次猎狗捉到兔子的总重量.按照重量来评价猎狗,决定一段时间内的待遇.于是猎狗们捉到兔子的数量和重量都增加了.猎人很开心. 但是过了一段时间,猎人发现,猎狗们捉兔子的数量又少了,而且越有经验的猎狗,捉兔子的数量下降的就越利害.于是猎人又去问猎狗.
  猎狗说“我们把最好的时间都奉献给了您,主人,但是我们随着时间的推移会老,当我们捉不到兔子的时候,您还会给我们骨头吃吗?“
  ........................长期的骨头........................
  
  
  四
  
   猎人做了论功行赏的决定.分析与汇总了所有猎狗捉到兔子的数量与重量,规定如果捉到的兔子超过了一定的数量后,即使捉不到兔子,每顿饭也可以得到一定数 量的骨头.猎狗们都很高兴,大家都努力去达到猎人规定的数量.一段时间过后,终于有一些猎狗达到了猎人规定的数量.这时,其中有一只猎狗说:我们这么努 力,只得到几根骨头,而我们捉的猎物远远超过了这几根骨头.我们为什么不能给自己捉兔子呢?“于是,有些猎狗离开了猎人,自己捉兔子去了.
  .............................骨头与肉兼而有之...........
  
  
  五
  
   猎人意识到猎狗正在流失,并且那些流失的猎狗像野狗一般和自己的猎狗抢兔子.情况变得越来越糟,猎人不得已引诱了一条野狗,问他到底野狗比猎狗强在那 里。野狗说:“猎狗吃的是骨头,吐出来的是肉啊!”,接着又道:“也不是所有的野狗都顿顿有肉吃,大部分最后骨头都没的舔!不然也不至于被你诱惑。” 于是猎人进行了改革,使得每条猎狗除基本骨头外,可获得其所猎兔肉总量的n%,而且随着服务时间加长,贡献变大,该比例还可递增,并有权分享猎人总兔肉的 m%。就这样,猎狗们与猎人一起努力,将野狗们逼得叫苦连天,纷纷强烈要求重归猎狗队伍。
  
  
  故事还在继续................
  
  
  --------------------只有永远的利益,没有永远的朋友--------------
  
   日子一天一天地过去,冬天到了,兔子越来越少,猎人们的收成也一天不如一天。而那些服务时间长的老猎狗们老得不能捉到兔子,但仍然在无忧无虑地享受着那 些他们自以为是应得的大份食物。终于有一天猎人再也不能忍受,把他们扫地出门,因为猎人更需要身强力壮的猎狗。。。。。
  
  --------------------- Birth of MicroBone Co. --------------
  
   被扫地出门的老猎狗们得了一笔不菲的赔偿金,于是他们成立了MicroBone公司。他们采用连锁加盟的方式招募野狗,向野狗们传授猎兔的技巧,他们从 猎得的兔子中抽取一部分作为管理费。当赔偿金几乎全部用于广告后,他们终于有了足够多的野狗加盟。公司开始赢利。一年后,他们收购了猎人的家当...
  
  ------Development of MicroBone Co. -----------------------
  
   MicroBone公司许诺给加盟的野狗能得到公司n%的股份。这实在是太有诱惑力了。这些自认为是怀才不遇的野狗们都以为找到了知音:终于做公司的主 人了,不用再忍受猎人们呼来唤去的不快,不用再为捉到足够多的兔子而累死累活,也不用眼巴巴地乞求猎人多给两跟骨头而扮得楚楚可怜。这一切对这些野狗来 说,这比多吃两根骨头更加受用。于是野狗们拖家带口地加入了MicroBone,一些在猎人门下的年轻猎口也开始蠢蠢欲动,甚至很多自以为聪明实际愚蠢的 猎人也想加入。好多同类型的公司象雨后春笋般地成立了,BoneEase, Bone.com, ChinaBone....一时间,森林里热闹起来。
  
  ------------------- F4 的诞生 ----------------------------
  
   猎人凭借出售公司的钱走上了老猎狗走过的路,最后千辛万苦要与MicroBone公司谈判的时候,老猎狗出人意料的顺利答应了猎人,把 MicroBone公司卖给了猎人。老猎狗们从此不再经营公司,转而开始写自转《老猎狗的一生》,又写:《如何成为出色的猎狗》,《如何从一只普通猎狗成 为一只管理层的猎狗》《猎狗成功秘诀》《成功猎狗500条》《穷猎狗,富猎狗》,并且将老猎狗的故事搬上屏幕,取名《猎狗花园》,四只老猎狗成为了家喻户 晓的明星F4. 收版权费,没有风险,利润更高。
  
  ps: 干活的总是拿得少的,拿得多的都是不干活的。

Regular Expressions

From CSDN:

正则表达式系统教程出处:oo8h
[ 2005-01-11 10:10:01 ] 作者:笑容 责任编辑:huangpeidan

正则表达式(regular expression)




 关键字:正则表达式,Regular Expression
  原著:笑容
 创作于:2004年05月03日
最后更新:2004年05月04日 21:12
引用地址:正则表达式(regular expression)
版权声明:使用创作公用版权协议




前言

  正则表达式是烦琐的,但是强大的,学会之后的应用会让你除了提高效率外,会给你带来绝对的成就感。只要认真去阅读这些资料,加上应用的时候进行一定的参考,掌握正则表达式不是问题。

1. 引子
  目前,正则表达式已经在很多软件中得到广泛的应用,包括*nix(Linux, Unix等),HP等操作系统,PHP,C#,Java等开发环境,以及很多的应用软件中,都可以看到正则表达式的影子。

  正则表达式的使用,可以通过简单的办法来实现强大的功能。为了简单有效而又不失强大,造成了正则表达式代码的难度较大,学习起来也不是很容易,所以需要付出一些努力才行,入门之后参照一定的参考,使用起来还是比较简单有效的。

  例子: ^.+@.+\\..+$

  这样的代码曾经多次把我自己给吓退过。可能很多人也是被这样的代码给吓跑的吧。继续阅读本文将让你也可以自由应用这样的代码。

  注意:这里的第7部分跟前面的内容看起来似乎有些重复,目的是把前面表格里的部分重新描述了一次,目的是让这些内容更容易理解。

2. 正则表达式的历史

  正则表达式的“祖先”可以一直上溯至对人类神经系统如何工作的早期研究。Warren McCulloch 和 Walter Pitts 这两位神经生理学家研究出一种数学方式来描述这些神经网络。

   1956 年, 一位叫 Stephen Kleene 的数学家在 McCulloch 和 Pitts 早期工作的基础上,发表了一篇标题为“神经网事件的表示法”的论文,引入了正则表达式的概念。正则表达式就是用来描述他称为“正则集的代数”的表达式,因 此采用“正则表达式”这个术语。

  随后,发现可以将这一工作应用于使用 Ken Thompson 的计算搜索算法的一些早期研究,Ken Thompson 是 Unix 的主要发明人。正则表达式的第一个实用应用程序就是 Unix 中的 qed 编辑器。

  如他们所说,剩下的就是众所周知的历史了。从那时起直至现在正则表达式都是基于文本的编辑器和搜索工具中的一个重要部分。

3. 正则表达式定义

  正则表达式(regular expression)描述了一种字符串匹配的模式,可以用来检查一个串是否含有某种子串、将匹配的子串做替换或者从某个串中取出符合某个条件的子串等。

  列目录时, dir *.txt或ls *.txt中的*.txt就不是一个正则表达式,因为这里*与正则式的*的含义是不同的。

  正则表达式是由普通字符(例如字符 a 到 z)以及特殊字符(称为元字符)组成的文字模式。正则表达式作为一个模板,将某个字符模式与所搜索的字符串进行匹配。

  3.1 普通字符

  由所有那些未显式指定为元字符的打印和非打印字符组成。这包括所有的大写和小写字母字符,所有数字,所有标点符号以及一些符号。

  3.2 非打印字符

字符 含义
\cx 匹配由x指明的控制字符。例如, \cM 匹配一个 Control-M 或回车符。x 的值必须为 A-Z 或 a-z 之一。否则,将 c 视为一个原义的 'c' 字符。
\f 匹配一个换页符。等价于 \x0c 和 \cL。
\n 匹配一个换行符。等价于 \x0a 和 \cJ。
\r 匹配一个回车符。等价于 \x0d 和 \cM。
\s 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。
\S 匹配任何非空白字符。等价于 [^ \f\n\r\t\v]。
\t 匹配一个制表符。等价于 \x09 和 \cI。
\v 匹配一个垂直制表符。等价于 \x0b 和 \cK。

 
  3.3 特殊字符

  所谓特殊字符,就是一些有特殊含义的字符,如上面说的"*.txt"中的*,简单的说就是表示任何字符串的意思。如果要查找文件名中有*的文件,则需要对*进行转义,即在其前加一个\。ls \*.txt。正则表达式有以下特殊字符。

特别字符说明
$匹配输入字符串的结尾位置。如果设置了 RegExp 对象的 Multiline 属性,则 $ 也匹配 '\n' 或 '\r'。要匹配 $ 字符本身,请使用 \$。
( )标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符,请使用 \( 和 \)。
*匹配前面的子表达式零次或多次。要匹配 * 字符,请使用 \*。
+匹配前面的子表达式一次或多次。要匹配 + 字符,请使用 \+。
.匹配除换行符 \n之外的任何单字符。要匹配 .,请使用 \。
[ 标记一个中括号表达式的开始。要匹配 [,请使用 \[。
?匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。要匹配 ? 字符,请使用 \?。
\将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符。例如, 'n' 匹配字符 'n'。'\n' 匹配换行符。序列 '\\' 匹配 "\",而 '\(' 则匹配 "("。
^匹配输入字符串的开始位置,除非在方括号表达式中使用,此时它表示不接受该字符集合。要匹配 ^ 字符本身,请使用 \^。
{标记限定符表达式的开始。要匹配 {,请使用 \{。
|指明两项之间的一个选择。要匹配 |,请使用 \|。
 

  构造正则表达式的方法和创建数学表达式的方法一样。也就是用多种元字符与操作符将小的表达式结合在一起来创建更大的表达式。正则表达式的组件可以是单个的字符、字符集合、字符范围、字符间的选择或者所有这些组件的任意组合。
 

  3.4 限定符

  限定符用来指定正则表达式的一个给定组件必须要出现多少次才能满足匹配。有*或+或?或{n}或{n,}或{n,m}共6种。

  *、+和?限定符都是贪婪的,因为它们会尽可能多的匹配文字,只有在它们的后面加上一个?就可以实现非贪婪或最小匹配。

  正则表达式的限定符有:

字符 描述
* 匹配前面的子表达式零次或多次。例如,zo* 能匹配 "z" 以及 "zoo"。* 等价于{0,}。
+ 匹配前面的子表达式一次或多次。例如,'zo+' 能匹配 "zo" 以及 "zoo",但不能匹配 "z"。+ 等价于 {1,}。
? 匹配前面的子表达式零次或一次。例如,"do(es)?" 可以匹配 "do" 或 "does" 中的"do" 。? 等价于 {0,1}。
{n} n 是一个非负整数。匹配确定的 n 次。例如,'o{2}' 不能匹配 "Bob" 中的 'o',但是能匹配 "food" 中的两个 o。
{n,} n 是一个非负整数。至少匹配n 次。例如,'o{2,}' 不能匹配 "Bob" 中的 'o',但能匹配 "foooood" 中的所有 o。'o{1,}' 等价于 'o+'。'o{0,}' 则等价于 'o*'。
{n,m} m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。例如,"o{1,3}" 将匹配 "fooooood" 中的前三个 o。'o{0,1}' 等价于 'o?'。请注意在逗号和两个数之间不能有空格。


  3.5 定位符

  用来描述字符串或单词的边界,^和$分别指字符串的开始与结束,\b描述单词的前或后边界,\B表示非单词边界。不能对定位符使用限定符。

  3.6 选择

  用圆括号将所有选择项括起来,相邻的选择项之间用|分隔。但用圆括号会有一个副作用,是相关的匹配会被缓存,此时可用?:放在第一个选项前来消除这种副作用。

  其中?:是非捕获元之一,还有两个非捕获元是?=和?!,这两个还有更多的含义,前者为正向预查,在任何开始匹配圆括号内的正则表达式模式的位置来匹配搜索字符串,后者为负向预查,在任何开始不匹配该正则表达式模式的位置来匹配搜索字符串。

  3.7 后向引用

   对一个正则表达式模式或部分模式两边添加圆括号将导致相关匹配存储到一个临时缓冲区中,所捕获的每个子匹配都按照在正则表达式模式中从左至右所遇到的内 容存储。存储子匹配的缓冲区编号从 1 开始,连续编号直至最大 99 个子表达式。每个缓冲区都可以使用 '\n' 访问,其中 n 为一个标识特定缓冲区的一位或两位十进制数。

  可以使用非捕获元字符 '?:', '?=', or '?!' 来忽略对相关匹配的保存。
4. 各种操作符的运算优先级

  相同优先级的从左到右进行运算,不同优先级的运算先高后低。各种操作符的优先级从高到低如下:

操作符 描述
\ 转义符
(), (?:), (?=), [] 圆括号和方括号
*, +, ?, {n}, {n,}, {n,m} 限定符
^, $, \anymetacharacter 位置和顺序
| “或”操作

5. 全部符号解释

字符 描述
\ 将下一个字符标记为一个特殊字符、或一个原义字符、或一个 向后引用、或一个八进制转义符。例如,'n' 匹配字符 "n"。'\n' 匹配一个换行符。序列 '\\' 匹配 "\" 而 "\(" 则匹配 "("。
^ 匹配输入字符串的开始位置。如果设置了 RegExp 对象的 Multiline 属性,^ 也匹配 '\n' 或 '\r' 之后的位置。
$ 匹配输入字符串的结束位置。如果设置了RegExp 对象的 Multiline 属性,$ 也匹配 '\n' 或 '\r' 之前的位置。
* 匹配前面的子表达式零次或多次。例如,zo* 能匹配 "z" 以及 "zoo"。* 等价于{0,}。
+ 匹配前面的子表达式一次或多次。例如,'zo+' 能匹配 "zo" 以及 "zoo",但不能匹配 "z"。+ 等价于 {1,}。
? 匹配前面的子表达式零次或一次。例如,"do(es)?" 可以匹配 "do" 或 "does" 中的"do" 。? 等价于 {0,1}。
{n} n 是一个非负整数。匹配确定的 n 次。例如,'o{2}' 不能匹配 "Bob" 中的 'o',但是能匹配 "food" 中的两个 o。
{n,} n 是一个非负整数。至少匹配n 次。例如,'o{2,}' 不能匹配 "Bob" 中的 'o',但能匹配 "foooood" 中的所有 o。'o{1,}' 等价于 'o+'。'o{0,}' 则等价于 'o*'。
{n,m} m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。例如,"o{1,3}" 将匹配 "fooooood" 中的前三个 o。'o{0,1}' 等价于 'o?'。请注意在逗号和两个数之间不能有空格。
? 当 该字符紧跟在任何一个其他限制符 (*, +, ?, {n}, {n,}, {n,m}) 后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串 "oooo",'o+?' 将匹配单个 "o",而 'o+' 将匹配所有 'o'。
. 匹配除 "\n" 之外的任何单个字符。要匹配包括 '\n' 在内的任何字符,请使用象 '[.\n]' 的模式。
(pattern) 匹配 pattern 并获取这一匹配。所获取的匹配可以从产生的 Matches 集合得到,在VBScript 中使用 SubMatches 集合,在JScript 中则使用 $0…$9 属性。要匹配圆括号字符,请使用 '\(' 或 '\)'。
(?:pattern) 匹 配 pattern 但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。这在使用 "或" 字符 (|) 来组合一个模式的各个部分是很有用。例如, 'industr(?:y|ies) 就是一个比 'industry|industries' 更简略的表达式。
(?=pattern) 正 向预查,在任何匹配 pattern 的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如,'Windows (?=95|98|NT|2000)' 能匹配 "Windows 2000" 中的 "Windows" ,但不能匹配 "Windows 3.1" 中的 "Windows"。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。
(?!pattern) 负 向预查,在任何不匹配 pattern 的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如'Windows (?!95|98|NT|2000)' 能匹配 "Windows 3.1" 中的 "Windows",但不能匹配 "Windows 2000" 中的 "Windows"。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始
x|y 匹配 x 或 y。例如,'z|food' 能匹配 "z" 或 "food"。'(z|f)ood' 则匹配 "zood" 或 "food"。
[xyz] 字符集合。匹配所包含的任意一个字符。例如, '[abc]' 可以匹配 "plain" 中的 'a'。
[^xyz] 负值字符集合。匹配未包含的任意字符。例如, '[^abc]' 可以匹配 "plain" 中的'p'。
[a-z] 字符范围。匹配指定范围内的任意字符。例如,'[a-z]' 可以匹配 'a' 到 'z' 范围内的任意小写字母字符。
[^a-z] 负值字符范围。匹配任何不在指定范围内的任意字符。例如,'[^a-z]' 可以匹配任何不在 'a' 到 'z' 范围内的任意字符。
\b 匹配一个单词边界,也就是指单词和空格间的位置。例如, 'er\b' 可以匹配"never" 中的 'er',但不能匹配 "verb" 中的 'er'。
\B 匹配非单词边界。'er\B' 能匹配 "verb" 中的 'er',但不能匹配 "never" 中的 'er'。
\cx 匹配由 x 指明的控制字符。例如, \cM 匹配一个 Control-M 或回车符。x 的值必须为 A-Z 或 a-z 之一。否则,将 c 视为一个原义的 'c' 字符。
\d 匹配一个数字字符。等价于 [0-9]。
\D 匹配一个非数字字符。等价于 [^0-9]。
\f 匹配一个换页符。等价于 \x0c 和 \cL。
\n 匹配一个换行符。等价于 \x0a 和 \cJ。
\r 匹配一个回车符。等价于 \x0d 和 \cM。
\s 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。
\S 匹配任何非空白字符。等价于 [^ \f\n\r\t\v]。
\t 匹配一个制表符。等价于 \x09 和 \cI。
\v 匹配一个垂直制表符。等价于 \x0b 和 \cK。
\w 匹配包括下划线的任何单词字符。等价于'[A-Za-z0-9_]'。
\W 匹配任何非单词字符。等价于 '[^A-Za-z0-9_]'。
\xn 匹配 n,其中 n 为十六进制转义值。十六进制转义值必须为确定的两个数字长。例如,'\x41' 匹配 "A"。'\x041' 则等价于 '\x04' & "1"。正则表达式中可以使用 ASCII 编码。.
\num 匹配 num,其中 num 是一个正整数。对所获取的匹配的引用。例如,'(.)\1' 匹配两个连续的相同字符。
\n 标识一个八进制转义值或一个向后引用。如果 \n 之前至少 n 个获取的子表达式,则 n 为向后引用。否则,如果 n 为八进制数字 (0-7),则 n 为一个八进制转义值。
\nm 标 识一个八进制转义值或一个向后引用。如果 \nm 之前至少有 nm 个获得子表达式,则 nm 为向后引用。如果 \nm 之前至少有 n 个获取,则 n 为一个后跟文字 m 的向后引用。如果前面的条件都不满足,若 n 和 m 均为八进制数字 (0-7),则 \nm 将匹配八进制转义值 nm。
\nml 如果 n 为八进制数字 (0-3),且 m 和 l 均为八进制数字 (0-7),则匹配八进制转义值 nml。
\un 匹配 n,其中 n 是一个用四个十六进制数字表示的 Unicode 字符。例如, \u00A9 匹配版权符号 (?)。
 
6. 部分例子

正则表达式说明
/\b([a-z]+) \1\b/gi一个单词连续出现的位置
/(\w+):\/\/([^/:]+)(:\d*)?([^# ]*)/ 将一个URL解析为协议、域、端口及相对路径
/^(?:Chapter|Section) [1-9][0-9]{0,1}$/定位章节的位置
/[-a-z]/A至z共26个字母再加一个-号。
/ter\b/可匹配chapter,而不能terminal
/\Bapt/可匹配chapter,而不能aptitude
/Windows(?=95 |98 |NT )/可匹配Windows95或Windows98或WindowsNT,当找到一个匹配后,从Windows后面开始进行下一次的检索匹配。
 
7. 正则表达式匹配规则

  7.1 基本模式匹配

  一切从最基本的开始。模式,是正规表达式最基本的元素,它们是一组描述字符串特征的字符。模式可以很简单,由普通的字符串组成,也可以非常复杂,往往用特殊的字符表示一个范围内的字符、重复出现,或表示上下文。例如:

  ^once

   这个模式包含一个特殊的字符^,表示该模式只匹配那些以once开头的字符串。例如该模式与字符串"once upon a time"匹配,与"There once was a man from NewYork"不匹配。正如如^符号表示开头一样,$符号用来匹配那些以给定模式结尾的字符串。

  bucket$

  这个模式与"Who kept all of this cash in a bucket"匹配,与"buckets"不匹配。字符^和$同时使用时,表示精确匹配(字符串与模式一样)。例如:

  ^bucket$

  只匹配字符串"bucket"。如果一个模式不包括^和$,那么它与任何包含该模式的字符串匹配。例如:模式

  once

  与字符串

  There once was a man from NewYork
  Who kept all of his cash in a bucket.

  是匹配的。

   在该模式中的字母(o-n-c-e)是字面的字符,也就是说,他们表示该字母本身,数字也是一样的。其他一些稍微复杂的字符,如标点符号和白字符(空 格、制表符等),要用到转义序列。所有的转义序列都用反斜杠(\)打头。制表符的转义序列是:\t。所以如果我们要检测一个字符串是否以制表符开头,可以 用这个模式:

  ^\t

  类似的,用\n表示“新行”,\r表示回车。其他的特殊符号,可以用在前面加上反斜杠,如反斜杠本身用\\表示,句号.用\.表示,以此类推。

  7.2 字符簇

  在INTERNET的程序中,正规表达式通常用来验证用户的输入。当用户提交一个FORM以后,要判断输入的电话号码、地址、EMAIL地址、信用卡号码等是否有效,用普通的基于字面的字符是不够的。

  所以要用一种更自由的描述我们要的模式的办法,它就是字符簇。要建立一个表示所有元音字符的字符簇,就把所有的元音字符放在一个方括号里:

  [AaEeIiOoUu]

  这个模式与任何元音字符匹配,但只能表示一个字符。用连字号可以表示一个字符的范围,如:

  [a-z] //匹配所有的小写字母
  [A-Z] //匹配所有的大写字母
  [a-zA-Z] //匹配所有的字母
  [0-9] //匹配所有的数字
  [0-9\.\-] //匹配所有的数字,句号和减号
  [ \f\r\t\n] //匹配所有的白字符

  同样的,这些也只表示一个字符,这是一个非常重要的。如果要匹配一个由一个小写字母和一位数字组成的字符串,比如"z2"、"t6"或"g7",但不是"ab2"、"r2d3" 或"b52"的话,用这个模式:

  ^[a-z][0-9]$

  尽管[a-z]代表26个字母的范围,但在这里它只能与第一个字符是小写字母的字符串匹配。

  前面曾经提到^表示字符串的开头,但它还有另外一个含义。当在一组方括号里使用^是,它表示“非”或“排除”的意思,常常用来剔除某个字符。还用前面的例子,我们要求第一个字符不能是数字:

  ^[^0-9][0-9]$

  这个模式与"&5"、"g7"及"-2"是匹配的,但与"12"、"66"是不匹配的。下面是几个排除特定字符的例子:

  [^a-z] //除了小写字母以外的所有字符
  [^\\\/\^] //除了(\)(/)(^)之外的所有字符
  [^\"\'] //除了双引号(")和单引号(')之外的所有字符

  特殊字符"." (点,句号)在正规表达式中用来表示除了“新行”之外的所有字符。所以模式"^.5$"与任何两个字符的、以数字5结尾和以其他非“新行”字符开头的字符串匹配。模式"."可以匹配任何字符串,除了空串和只包括一个“新行”的字符串。

  PHP的正规表达式有一些内置的通用字符簇,列表如下:

  字符簇含义

  [[:alpha:]] 任何字母
  [[:digit:]] 任何数字
  [[:alnum:]] 任何字母和数字
  [[:space:]] 任何白字符
  [[:upper:]] 任何大写字母
  [[:lower:]] 任何小写字母
  [[:punct:]] 任何标点符号
  [[:xdigit:]] 任何16进制的数字,相当于[0-9a-fA-F]

  7.3 确定重复出现

  到现在为止,你已经知道如何去匹配一个字母或数字,但更多的情况下,可能要匹配一个单词或一组数字。一个单词有若干个字母组成,一组数字有若干个单数组成。跟在字符或字符簇后面的花括号({})用来确定前面的内容的重复出现的次数。

  字符簇 含义
  ^[a-zA-Z_]$ 所有的字母和下划线
  ^[[:alpha:]]{3}$ 所有的3个字母的单词
  ^a$ 字母a
  ^a{4}$ aaaa
  ^a{2,4}$ aa,aaa或aaaa
  ^a{1,3}$ a,aa或aaa
  ^a{2,}$ 包含多于两个a的字符串
  ^a{2,} 如:aardvark和aaab,但apple不行
  a{2,} 如:baad和aaa,但Nantucket不行
  \t{2} 两个制表符
  .{2} 所有的两个字符

   这些例子描述了花括号的三种不同的用法。一个数字,{x}的意思是“前面的字符或字符簇只出现x次”;一个数字加逗号,{x,}的意思是“前面的内容出 现x或更多的次数”;两个用逗号分隔的数字,{x,y}表示“前面的内容至少出现x次,但不超过y次”。我们可以把模式扩展到更多的单词或数字:

  ^[a-zA-Z0-9_]{1,}$ //所有包含一个以上的字母、数字或下划线的字符串
  ^[0-9]{1,}$ //所有的正数
  ^\-{0,1}[0-9]{1,}$ //所有的整数
  ^\-{0,1}[0-9]{0,}\.{0,1}[0-9]{0,}$ //所有的小数

   最后一个例子不太好理解,是吗?这么看吧:与所有以一个可选的负号(\-{0,1})开头(^)、跟着0个或更多的数字([0-9]{0,})、和一个 可选的小数点(\.{0,1})再跟上0个或多个数字([0-9]{0,}),并且没有其他任何东西($)。下面你将知道能够使用的更为简单的方法。

  特殊字符"?"与{0,1}是相等的,它们都代表着:“0个或1个前面的内容”或“前面的内容是可选的”。所以刚才的例子可以简化为:

  ^\-?[0-9]{0,}\.?[0-9]{0,}$

  特殊字符"*"与{0,}是相等的,它们都代表着“0个或多个前面的内容”。最后,字符"+"与 {1,}是相等的,表示“1个或多个前面的内容”,所以上面的4个例子可以写成:

  ^[a-zA-Z0-9_]+$ //所有包含一个以上的字母、数字或下划线的字符串
  ^[0-9]+$ //所有的正数
  ^\-?[0-9]+$ //所有的整数
  ^\-?[0-9]*\.?[0-9]*$ //所有的小数

  当然这并不能从技术上降低正规表达式的复杂性,但可以使它们更容易阅读。


参考文献:

  JScript 和 VBScript 正则表达式