Python基础知识(六)--字符串

 
 
  1. #字符串
  2. #字符串是用固定的str数据类型表示的,用来存放Unicode字符序列
  3. #str数据类型可以用来创建一个字符串对象,参数为空时返回一个空字符串
  4. a = str()
  5. print(a) #
  6. a = str("abcdef")
  7. print(a) #abcdef
  8. #str()函数可以用来进行类型转换
  9. a = str(123)
  10. print(a) #123
  11. #字符串是使用引号创建的,可以使用双引号,也可以使用单引号,
  12. #字符串两端所用引号必须相同
  13. #还可以使用三引号包含的字符串,这是Python对两端都使用三个引号的字符串的叫法
  14. text = """A triple quoted string like this can include 'quotes' and
  15. "quotes" without formality. We can also escape newlines \
  16. so this particular string is actually only two lines long."""
  17. #如果字符串中使用的引号与包含字符串所用引号不同时,
  18. #可以直接使用,如果相同时,需要进行转义
  19. a = "Single 'quotes' are fine; \"doubles\" must be escaped."
  20. b ='single \'quotes\' must be escaped; "doubles" are fine.'
  21. #在三引号内可以直接使用换行,通过\n可以在任何字符串中包含换行
 
 
  1. #Python字符串转义
  2. \newline #忽略换行?
  3. \\ #反斜杠
  4. \' #单引号
  5. \" #双引号
  6. \a #ASCII蜂鸣 (BEL)
  7. \b #ASCII退格(BS)
  8. \f #ASCII走纸(FF)
  9. \n #ASCII换行(LF)
  10. \n{name} #给定名称的Unicode字符
  11. \ooo #给定八进制的字符
  12. \r #ASCII回国符(CR)
  13. \t #ASCII制表符(TAB)
  14. \uhhhh #给定16位十六进制的Unicode字符
  15. \Uhhhhhhhh #给定32位十六进制的Unicode字符
  16. \v #ASCII垂直指标(VT)
  17. \xhh #给定8位十六进制的Unicode字符
 
 
  1. #在使用正则表达式的时候,由于需要使用大量字面意义反斜杠,
  2. #由于每个反斜杠都需要进行转义处理,从而造成了不便:
  3. import re
  4. phone1 = re.compile("^((?:[(}\\d+[)])?\\s*\\d+(?:-\\d+)?)$")
  5. #解决的方法是使用原始字符串
  6. #这种引号或三引号包含的字符串的第一个引号由r引导
  7. phone2 = re.compile(r"((?:[(}\d+[)])?\s*\d+(?:-\d+)?)$")
  8. #如果有一个长字符串跨越了两行或更多行,但不使用三引号包含,有两种方法:
  9. t = "This is not the best way to join two long strings " + \
  10. "together since it relies on ugly newline escaping"
  11. s = ("this is the nice way to join two long strings"
  12. "together; it relies on string literal concatenation.")
  13. #第二种情况,用圆括号将其包含在一起,构成一个单独的表达式,
  14. #如果不使用圆括号就只会对第一个字符串赋值,
  15. #第二个字符串会引起IndentationError异常
  16. #.py文件默认使用UTF-8 Unicode编码,因此可以写入任何Unicode字符
  17. #(这点我就遇到过问题,出现SyntaxError: Non-UTF-8 code)难道是Eclipse搞的鬼?)
  18. #(改变文件编码可以解决这个问题)
  19. #(但IDLE支持倒是真的)
  20. euros = "€\N{euro sign}\u20AC\U000020AC"
  21. #Unicode字符非大小写敏感
  22. print(euros) #€€€€
  23. #而且连标志符也可以
  24. 姓名 = "张小三"
  25. print(姓名) #张小三
  26. #也就是说支持中文变量名的,虽然这样用的人很少,但我倒是觉得以后可以这么试试了
  27. #如果想知道字符串中某个字符的Unicode字元,可以用内置的ord()函数
  28. print(ord(euros[0])) #8364
  29. print(hex(ord(euros[0]))) #0x20ac
  30. #同样,也可以用表示有效字元的任意整数转换成Unicode字符
  31. #这需要使用内置chr()函数
  32. s = "anarchists are " + chr(8734) + chr(0x23B7)
  33. print(s) #anarchists are ∞⎷
  34. print(ascii(s)) #'anarchists are \u221e\u23b7'

 
 
  1. #比较字符串
  2. #字符串支持的比较运算符包括:< <= == != > >=
  3. #对于使用Unicode的字符串,比较运算存在两个问题:
  4. #1.字符可以有三种不同的UTF-8编码字节的表示方式
  5. # 解决方法导入unicodedata模块
  6. # 以"NFKD"为第一个参数,调用unicodedata.normalize()
  7. # 该函数返回的UTF-8编码字节表示的字符串总是字节序列
  8. #2.有些字符的排序是特定于某种语言的,而有些字符并不具备有意义的排序位置
  9. #字符串分片与步距
  10. #序列中的单个数据或字符串中的单个字符可以用数据项存取操作符[]来提取
  11. #索引值从0开始,直到字符串长度-1
  12. #负索引值最后一个字符为-1,向前逐渐递减
  13. #存取超过索引范围的字符会产生IndexError
  14. #分片操作符的语法格式
  15. #seq[start:] #提取star开始到字符串结尾
  16. #seq[start:end] #提取start到end-1的字符串
  17. #seq[start:end:step] #提取start到end-1的字符串,每次间隔step
  18. text = "abcdefghijklmnopqrstuvwxyz"
  19. print(text[0]) #a
  20. print(text[0:]) #abcdefghijklmnopqrstuvwxyz
  21. print(text[2:10]) #cdefghij
  22. print(text[:20]) #abcdefghijklmnopqrst
  23. print(text[::2]) #acegikmoqsuwy
  24. print(text[10::2]) #kmoqsuwy
  25. print(text[10:26:2]) #kmoqsuwy
  26. print(text[26::-1]) #zyxwvutsrqponmlkjihgfedcba
  27. print(text[::-1]) #zyxwvutsrqponmlkjihgfedcba
  28. #字符串操作符与方法
  29. #字符串是固定序列,所有用于固定序列的功能都可用于字符串
  30. #包括in进行成员关系测试,+=进行追加操作 * 进行复制 *= 进行增加的复制
  31. subtext = "def"
  32. print(subtext in text) #True
  33. subtext += "ghi"
  34. print(subtext) #defghi
  35. subtext *= 3
  36. print(subtext) #defghidefghidefghi

 
 
  1. #字符串方法
  2. #--------------------------------------------------------------------
  3. s.capitalize() #返回字符串s的副本,并将首字符大写
  4. text = "this is a test text"
  5. print(text.capitalize()) #This is a test text
  6. #--------------------------------------------------------------------
  7. s.center(width, char) #返回一个长度为width的字符串
  8. #字符串s在返回字符串的中间位置
  9. #其余部份用char添充,char默认为空格
  10. s = "abd"
  11. print(s.center(20)) # abd
  12. print(s.center(20, "*")) #********abd*********
  13. #--------------------------------------------------------------------
  14. s.count(t, start, end) #返回在s字符串中,start:end分片中,
  15. #子串t出现的次数
  16. s = "abcdabcdabcd"
  17. s.count("bc") #3
  18. s.count("bcda") #2
  19. s.count("bcda", 1, 8) #1
  20. #--------------------------------------------------------------------
  21. s.encode(encoding, err) #返回一个bytes对象用指定编码格式来表示该字符串
  22. #并根据可选的err处理错误
  23. s = "中国"
  24. print(s.encode(encoding='utf_8', errors='strict'))
  25. #b'\xe4\xb8\xad\xe5\x9b\xbd'
  26. print(s.encode(encoding='GB2312', errors='strict'))
  27. #b'\xd6\xd0\xb9\xfa'
  28. print(s.encode(errors='strict')) #b'\xe4\xb8\xad\xe5\x9b\xbd'
  29. #默认的encoding是'utf_8'
  30. #--------------------------------------------------------------------
  31. s.endswith(x, start, end) #如果在s或s[start:end]分片中从字符串x或
  32. #元组x中的任意字符串结尾,则返回True,否则返回False
  33. s = "中华人民共和国"
  34. x = "国"
  35. print(s.endswith(x)) #True
  36. print(s.endswith(x, 2, 5)) #False
  37. x = ('一', '国')
  38. print(s.endswith(x)) #True
  39. #--------------------------------------------------------------------
  40. s.expandtabs(size) #返回s的一个副本,其中制表符用8(默认)或size个空格替换
  41. #这个替换不是直接在tab的位置上插入size个空格,而是与前文相关联计算空格数
  42. s = "abc\tdef\tghi"
  43. print(s.expandtabs(4)) #abc def ghi
  44. print(s.expandtabs(8)) #abc def ghi
  45. print(s.expandtabs()) #abc def ghi
  46. #--------------------------------------------------------------------
  47. s.find(t, start, end) #返回t在s或s[start:end]之中的最左位置,如果没有找到返回-1
  48. #使用s.rfind()可以返回相应的最右位置
  49. s = "this is a test text"
  50. print(s.find('is')) #2
  51. print(s.rfind('is')) #5
  52. #--------------------------------------------------------------------
  53. s.format(...) #格式化字符串,这个在后面详细解释
  54. #--------------------------------------------------------------------
  55. s.index(t, start, end) #返回t在s或s[start:end]之中的最左位置,如果没有找到返回ValueError
  56. #使用s.rindex()可以从最右边开始搜索
  57. #用法同s.find()
  58. #--------------------------------------------------------------------
  59. s.isalnum() #如果s非空,并且其中每个字符都是字母数字的就返回True
  60. s = "abd123"
  61. print(s.isalnum()) #True
  62. s += "_"
  63. print(s.isalnum()) #False
  64. #--------------------------------------------------------------------
  65. s.isalpha() #如果s非空,并且其中每个字符都是字母的就返回True
  66. s = "abd"
  67. print(s.isalnum()) #True
  68. s += "123"
  69. print(s.isalnum()) #False
  70. #--------------------------------------------------------------------
  71. s.isdecimal() #如果s非空,并且每个字符都是Unicode的基数为10的数字就返回True
  72. s = "1234"
  73. print(s.isdecimal()) #True
  74. s = "0x1304"
  75. print(s.isdecimal()) #False
  76. #--------------------------------------------------------------------
  77. s.isdigit() #如果非空,并且每个字符都是ASCII数字,则返回True
  78. s = "1234"
  79. print(s.isdigit()) #True
  80. s += "a"
  81. print(s.isdigit()) #False
  82. #--------------------------------------------------------------------
  83. s.isidentifier() #如果s非空,并且是一个有效的标识符,则返回True
  84. s = "abc"
  85. print(s.isidentifier()) #True
  86. s = "abc#%^#"
  87. print(s.isidentifier()) #False
  88. #--------------------------------------------------------------------
  89. s.islower() #如果s有至少一个小写字符,并且所有小写字符都是小写就返回True
  90. s = "abc"
  91. print(s.islower()) #True
  92. s = "Abc"
  93. print(s.islower()) #False
  94. s = "123"
  95. print(s.islower()) #False
  96. #--------------------------------------------------------------------
  97. s.isnumeric() #同s.isdigit(),字符为Unicode字符
  98. #--------------------------------------------------------------------
  99. s.isprintable() #如果s非空,并且每个字符都是可打印字符,
  100. #包括空格但不包括换行,则返回True
  101. s = "this is a text"
  102. print(s.isprintable()) #True
  103. s = "this is a text\n"
  104. print(s.isprintable()) #False
  105. #--------------------------------------------------------------------
  106. s.isspace() #如果s非空,并且所有字符都是空白,则返回True
  107. s = " "
  108. print(s.isspace()) #True
  109. s = " 1 "
  110. print(s.isspace()) #False
  111. #--------------------------------------------------------------------
  112. s.istitle() #如果s是非空的且首字母大写的字符串就返回True
  113. s = "This is a test"
  114. print(s.istitle()) #False
  115. s = "This Is A Test"
  116. print(s.istitle()) #True
  117. #--------------------------------------------------------------------
  118. s.isupper() #如果s有至少一个可大写字符且所有可大写字符均为大写则返回True
  119. #可参考s.islower()
  120. #--------------------------------------------------------------------
  121. s.join(seq) #返回序列中所有项连接起来的结果,
  122. #并以s(可以为空)在每两项之间分隔
  123. s = "*"
  124. seqs = ("this", "is", "a", "test")
  125. print(s.join(seqs)) #this*is*a*test
  126. print(" ".join(seqs)) #this is a test
  127. print("".join(seqs)) #thisisatest
  128. print(" ".join(["this", "is", "a", "test"]))
  129. #this is a test
  130. #--------------------------------------------------------------------
  131. s.ljust(width, char) #返回一个长度为width的字符串,并以char来添充s左侧
  132. #可参考s.center(),s.rjust()为右添充
  133. #--------------------------------------------------------------------
  134. s.lower() #将s中的字符变为小写
  135. s = "ABC123"
  136. print(s.lower()) #abc123
  137. #--------------------------------------------------------------------
  138. s.maketrans() #与s.translate()对应,可以产生一个转换表
  139. a = "abcde"
  140. b = "Hello"
  141. x = a.maketrans(a, b)
  142. print(a.translate(x)) #Hello
  143. #貌似可以进行替换,或是小小的加密也不错
  144. #--------------------------------------------------------------------
  145. s.partition(t) #返回三个字符串的无级,分别是:
  146. #s中在t子串之前的部分
  147. #t
  148. #s中在t子串之后的部分
  149. #如果t不在s中,则返回s与两个空字符串
  150. #使用s.lpartition(t)可以在s最右边分区
  151. s = "My country is China"
  152. t = "country"
  153. print(s.partition(t)) #('My ', 'country', ' is China')
  154. t = "ABCD"
  155. print(s.partition(t)) #('My country is China', '', '')
  156. t = "is"
  157. print(s.rpartition(t)) #('My country ', 'is', ' China')
  158. #--------------------------------------------------------------------
  159. s.replace(t, u, n) #返回字符串s的一个副本,其中每个或n个t用u替换
  160. s = "this is a text"
  161. print(s.replace("is", "Is")) #thIs Is a text
  162. print(s.replace("is", "Is", 1)) #thIs is a text
  163. #--------------------------------------------------------------------
  164. s.split(t, n) #返回一个字符串列表,在t处最多分割n次
  165. #如果n没指定,就尽可能分割多次
  166. #如果t没指定,就以空白处分割
  167. #s.rsplit(t, n)是从右侧开始分割,只有指定n,
  168. #且n小于可分割的最大次数时才有效
  169. s = "this is a test text"
  170. print(s.split("s")) #['thi', ' i', ' a te', 't text']
  171. print(s.split('s', 2)) #['thi', ' i', ' a test text']
  172. print(s.rsplit('s', 2)) #['this i', ' a te', 't text']
  173. #--------------------------------------------------------------------
  174. s.splitlines(f) #返回的行终结符处分割产生的行列表
  175. #并剥离行终结符(除非f为True)
  176. print(s.splitlines()) #['this', 'is', 'a', 'test', 'text']
  177. print(s.splitlines(True)) #['this\n', 'is\n', 'a\n', 'test\n', 'text']
  178. #--------------------------------------------------------------------
  179. s.startswith(x, start, end)
  180. #如果字符串s或s[start:end]是以字符串x,
  181. #或元组中任一项开始,则返回True,否则返回False
  182. #可参考s.endswith()
  183. #--------------------------------------------------------------------
  184. s.strip(chars) #将字符串开始和结尾处的chars中的字符移除
  185. #chars默认为空格
  186. #s.lstrip(chars)为去除字符串开始处的chars
  187. #s.rstrip(chars)为去除字符串结尾处的chars
  188. print(" my name is Xiaoming. ".strip())
  189. #my name is Xiaoming.
  190. print(" my name is Xiaoming. ".lstrip())
  191. #my name is Xiaoming.
  192. print(" my name is Xiaoming. ".rstrip())
  193. # my name is Xiaoming.
  194. #--------------------------------------------------------------------
  195. s.swepcase() #将字符串中大写转换成小写,小写转换成大写
  196. s = "This Is A Test Text"
  197. print(s.swapcase()) #tHIS iS a tEST tEXT
  198. #--------------------------------------------------------------------
  199. s.title() #将每个单词的首字母转成大写,其它字母转成小写
  200. s ="tHIS iS a tEST tEXT"
  201. print(s.title()) #This Is A Test Text
  202. #--------------------------------------------------------------------
  203. s.upper() #将字符全部转换成大写,可参考s.lower()
  204. #--------------------------------------------------------------------
  205. s.zfill(width) #返回s的副本,如果s长度小于width则在开始处添加0使之长度为width
  206. s = "test"
  207. print(s.zfill(15)) #00000000000test
  208. #--------------------------------------------------------------------
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
最重要的是——请给我30分钟,如果你没有使用正则表达式的经验,请不要试图在30秒内入门——除非你是超人 :) 别被下面那些复杂的表达式吓倒,只要跟着我一步一步来,你会发现正则表达式其实并没有你想像中的那么困难。当然,如果你看完了这篇教程之后,发现自己明白了很多,却又几乎什么都记不得,那也是很正常的——我认为,没接触过正则表达式的人在看完这篇教程后,能把提到过的语法记住80%以上的可能性为零。这里只是让你明白基本的原理,以后你还需要多练习,多使用,才能熟练掌握正则表达式。 除了作为入门教程之外,本文还试图成为可以在日常工作中使用的正则表达式语法参考手册。就作者本人的经历来说,这个目标还是完成得不错的——你看,我自己也没能把所有的东西记下来,不是吗? 恢复格式 文本格式约定:专业术语 元字符/语法格式 正则表达式 正则表达式中的一部分(用于分析) 对其进行匹配的源字符串正则表达式或其中一部分的说明 隐藏边注 本文右边有一些注释,主要是用来提供一些相关信息,或者给没有程序员背景的读者解释一些基本概念,通常可以忽略。 正则表达式到底是什么东西? 字符是计算机软件处理文字时最基本的单位,可能是字母,数字,标点符号,空格,换行符,汉字等等。字符串是0个或更多个字符的序列。文本也就是文字,字符串。说某个字符串匹配某个正则表达式,通常是指这个字符串里有一部分(或几部分分别)能满足表达式给出的条件。 在编写处理字符串的程序或网页时,经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说,正则表达式就是记录文本规则的代码。 很可能你使用过Windows/Dos下用于文件查找的通配符(wildcard),也就是*和?。如果你想查找某个目录下的所有的Word文档的话,你会搜索*.doc。在这里,*会被解释成任意的字符串。和通配符类似,正则表达式也是用来进行文本匹配的工具,只不过比起通配符,它能更精确地描述你的需求——当然,代价就是更复杂——比如你可以编写一个正则表达式,用来查找所有以0开头,后面跟着2-3个数字,然后是一个连字号“-”,最后是7或8位数字的字符串(像010-12345678或0376-7654321)。 入门 学习正则表达式的最好方法是从例子开始,理解例子之后再自己对例子进行修改,实验。下面给出了不少简单的例子,并对它们作了详细的说明。 假设你在一篇英文小说里查找hi,你可以使用正则表达式hi。 这几乎是最简单的正则表达式了,它可以精确匹配这样的字符串:由两个字符组成,前一个字符是h,后一个是i。通常,处理正则表达式的工具会提供一个忽略大小写的选项,如果选中了这个选项,它可以匹配hi,HI,Hi,hI这四种情况中的任意一种。 不幸的是,很多单词里包含hi这两个连续的字符,比如him,history,high等等。用hi来查找的话,这里边的hi也会被找出来。如果要精确地查找hi这个单词的话,我们应该使用\bhi\b。 \b是正则表达式规定的一个特殊代码(好吧,某些人叫它元字符,metacharacter),代表着单词的开头或结尾,也就是单词的分界处。虽然通常英文的单词是由空格,标点符号或者换行来分隔的,但是\b并不匹配这些单词分隔字符中的任何一个,它只匹配一个位置。 如果需要更精确的说法,\b匹配这样的位置:它的前一个字符和后一个字符不全是(一个是,一个不是或不存在)\w。 假如你要找的是hi后面不远处跟着一个Lucy,你应该用\bhi\b.*\bLucy\b。 这里,.是另一个元字符,匹配除了换行符以外的任意字符。*同样是元字符,不过它代表的不是字符,也不是位置,而是数量——它指定*前边的内容可以连续重复使用任意次以使整个表达式得到匹配。因此,.*连在一起就意味着任意数量的不包含换行的字符。现在\bhi\b.*\bLucy\b的意思就很明显了:先是一个单词hi,然后是任意个任意字符(但不能是换行),最后是Lucy这个单词。 换行符就是'\n',ASCII编码为10(十进制0x0A)的字符。 如果同时使用其它元字符,我们就能构造出功能更强大的正则表达式。比如下面这个例子: 0\d\d-\d\d\d\d\d\d\d\d匹配这样的字符串:以0开头,然后是两个数字,然后是一个连字号“-”,最后是8个数字(也就是中国的电话号码。当然,这个例子只能匹配区号为3位的情形)。 这里的\d是个新的元字符,匹配一位数字(0,或1,或2,或……)。-不是元字符,只匹配它本身——连字符(或者减号,或者中横线,或者随你怎么称呼它)。 为了避免那么多烦人的重复,我们也可以这样写这个表达式:0\d{2}-\d{8}。 这里\d后面的{2}({8})的意思是前面\d必须连续重复匹配2次(8次)。 测试正则表达式 其它可用的测试工具: • RegexBuddy • Javascript正则表达式在线测试工具 如果你不觉得正则表达式很难读写的话,要么你是一个天才,要么,你不是地球人。正则表达式的语法很令人头疼,即使对经常使用它的人来说也是如此。由于难于读写,容易出错,所以找一种工具对正则表达式进行测试是很有必要的。 不同的环境下正则表达式的一些细节是不相同的,本教程介绍的是微软 .Net Framework 2.0下正则表达式的行为,所以,我向你介绍一个.Net下的工具Regex Tester。首先你确保已经安装了.Net Framework 2.0,然后下载Regex Tester。这是个绿色软件,下载完后打开压缩包,直接运行RegexTester.exe就可以了。 下面是Regex Tester运行时的截图: 元字符 现在你已经知道几个很有用的元字符了,如\b,.,*,还有\d.正则表达式里还有更多的元字符,比如\s匹配任意的空白符,包括空格,制表符(Tab),换行符,中文全角空格等。\w匹配字母或数字或下划线或汉字等。 对中文/汉字的特殊处理是由.Net提供的正则表达式引擎支持的,其它环境下的具体情况请查看相关文档。 下面来看看更多的例子: \ba\w*\b匹配以字母a开头的单词——先是某个单词开始处(\b),然后是字母a,然后是任意数量的字母或数字(\w*),最后是单词结束处(\b)。 好吧,现在我们说说正则表达式里的单词是什么意思吧:就是不少于一个的连续的\w。不错,这与学习英文时要背的成千上万个同名的东西的确关系不大 :) \d+匹配1个或更多连续的数字。这里的+是和*类似的元字符,不同的是*匹配重复任意次(可能是0次),而+则匹配重复1次或更多次。 \b\w{6}\b 匹配刚好6个字符的单词。 表1.常用的元字符 代码 说明 . 匹配除换行符以外的任意字符 \w 匹配字母或数字或下划线或汉字 \s 匹配任意的空白符 \d 匹配数字 \b 匹配单词的开始或结束 ^ 匹配字符串的开始 $ 匹配字符串的结束 正则表达式引擎通常会提供一个“测试指定的字符串是否匹配一个正则表达式”的方法,如JavaScript里的RegExp.test()方法或.NET里的Regex.IsMatch()方法。这里的匹配是指是字符串里有没有符合表达式规则的部分。如果不使用^和$的话,对于\d{5,12}而言,使用这样的方法就只能保证字符串里包含5到12连续位数字,而不是整个字符串就是5到12位数字。 元字符^(和数字6在同一个键位上的符号)和$都匹配一个位置,这和\b有点类似。^匹配你要用来查找的字符串的开头,$匹配结尾。这两个代码在验证输入的内容时非常有用,比如一个网站如果要求你填写的QQ号必须为5位到12位数字时,可以使用:^\d{5,12}$。 这里的{5,12}和前面介绍过的{2}是类似的,只不过{2}匹配只能不多不少重复2次,{5,12}则是重复的次数不能少于5次,不能多于12次,否则都不匹配。 因为使用了^和$,所以输入的整个字符串都要用来和\d{5,12}来匹配,也就是说整个输入必须是5到12个数字,因此如果输入的QQ号能匹配这个正则表达式的话,那就符合要求了。 和忽略大小写的选项类似,有些正则表达式处理工具还有一个处理多行的选项。如果选中了这个选项,^和$的意义就变成了匹配行的开始处和结束处。 字符转义 如果你想查找元字符本身的话,比如你查找.,或者*,就出现了问题:你没办法指定它们,因为它们会被解释成别的意思。这时你就得使用\来取消这些字符的特殊意义。因此,你应该使用\.和\*。当然,要查找\本身,你也得用\\. 例如:unibetter\.com匹配unibetter.com,C:\\Windows匹配C:\Windows。 重复 你已经看过了前面的*,+,{2},{5,12}这几个匹配重复的方式了。下面是正则表达式中所有的限定符(指定数量的代码,例如*,{5,12}等): 表2.常用的限定符 代码/语法 说明 * 重复零次或更多次 + 重复一次或更多次 ? 重复零次或一次 {n} 重复n次 {n,} 重复n次或更多次 {n,m} 重复n到m次 下面是一些使用重复的例子: Windows\d+匹配Windows后面跟1个或更多数字 ^\w+匹配一行的第一个单词(或整个字符串的第一个单词,具体匹配哪个意思得看选项设置) 字符类 要想查找数字,字母或数字,空白是很简单的,因为已经有了对应这些字符集合的元字符,但是如果你想匹配没有预定义元字符的字符集合(比如元音字母a,e,i,o,u),应该怎么办? 很简单,你只需要在方括号里列出它们就行了,像[aeiou]就匹配任何一个英文元音字母,[.?!]匹配标点符号(.或?或!)。 我们也可以轻松地指定一个字符范围,像[0-9]代表的含意与\d就是完全一致的:一位数字;同理[a-z0-9A-Z_]也完全等同于\w(如果只考虑英文的话)。 下面是一个更复杂的表达式:\(?0\d{2}[) -]?\d{8}。 “(”和“)”也是元字符,后面的分组节里会提到,所以在这里需要使用转义。 这个表达式可以匹配几种格式的电话号码,像(010)88886666,或022-22334455,或02912345678等。我们对它进行一些分析吧:首先是一个转义字符\(,它能出现0次或1次(?),然后是一个0,后面跟着2个数字(\d{2}),然后是)或-或空格中的一个,它出现1次或不出现(?),最后是8个数字(\d{8})。 分枝条件 不幸的是,刚才那个表达式也能匹配010)12345678或(022-87654321这样的“不正确”的格式。要解决这个问题,我们需要用到分枝条件。正则表达式里的分枝条件指的是有几种规则,如果满足其中任意一种规则都应该当成匹配,具体方法是用|把不同的规则分隔开。听不明白?没关系,看例子: 0\d{2}-\d{8}|0\d{3}-\d{7}这个表达式能匹配两种以连字号分隔的电话号码:一种是三位区号,8位本地号(如010-12345678),一种是4位区号,7位本地号(0376-2233445)。 \(0\d{2}\)[- ]?\d{8}|0\d{2}[- ]?\d{8}这个表达式匹配3位区号的电话号码,其中区号可以用小括号括起来,也可以不用,区号与本地号间可以用连字号或空格间隔,也可以没有间隔。你可以试试用分枝条件把这个表达式扩展成也支持4位区号的。 \d{5}-\d{4}|\d{5}这个表达式用于匹配美国的邮政编码。美国邮编的规则是5位数字,或者用连字号间隔的9位数字。之所以要给出这个例子是因为它能说明一个问题:使用分枝条件时,要注意各个条件的顺序。如果你把它改成\d{5}|\d{5}-\d{4}的话,那么就只会匹配5位的邮编(以及9位邮编的前5位)。原因是匹配分枝条件时,将会从左到右地测试每个条件,如果满足了某个分枝的话,就不会去再管其它的条件了。 分组 我们已经提到了怎么重复单个字符(直接在字符后面加上限定符就行了);但如果想要重复多个字符又该怎么办?你可以用小括号来指定子表达式(也叫做分组),然后你就可以指定这个子表达式的重复次数了,你也可以对子表达式进行其它一些操作(后面会有介绍)。 (\d{1,3}\.){3}\d{1,3}是一个简单的IP地址匹配表达式。要理解这个表达式,请按下列顺序分析它:\d{1,3}匹配1到3位的数字,(\d{1,3}\.){3}匹配三位数字加上一个英文句号(这个整体也就是这个分组)重复3次,最后再加上一个一到三位的数字(\d{1,3})。 IP地址中每个数字都不能大于255,大家千万不要被《24》第三季的编剧给忽悠了…… 不幸的是,它也将匹配256.300.888.999这种不可能存在的IP地址。如果能使用算术比较的话,或许能简单地解决这个问题,但是正则表达式中并不提供关于数学的任何功能,所以只能使用冗长的分组,选择,字符类来描述一个正确的IP地址:((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)。 理解这个表达式的关键是理解2[0-4]\d|25[0-5]|[01]?\d\d?,这里我就不细说了,你自己应该能分析得出来它的意义。 反义 有时需要查找不属于某个能简单定义的字符类的字符。比如想查找除了数字以外,其它任意字符都行的情况,这时需要用到反义: 表3.常用的反义代码 代码/语法 说明 \W 匹配任意不是字母,数字,下划线,汉字的字符 \S 匹配任意不是空白符的字符 \D 匹配任意非数字的字符 \B 匹配不是单词开头或结束的位置 [^x] 匹配除了x以外的任意字符 [^aeiou] 匹配除了aeiou这几个字母以外的任意字符 例子:\S+匹配不包含空白符的字符串。 ]+>匹配用尖括号括起来的以a开头的字符串。 后向引用 使用小括号指定一个子表达式后,匹配这个子表达式的文本(也就是此分组捕获的内容)可以在表达式或其它程序中作进一步的处理。默认情况下,每个分组会自动拥有一个组号,规则是:从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推。 呃……其实,组号分配还不像我刚说得那么简单: • 分组0对应整个正则表达式 • 实际上组号分配过程是要从左向右扫描两遍的:第一遍只给未命名组分配,第二遍只给命名组分配--因此所有命名组的组号都大于未命名的组号 • 你可以使用(?:exp)这样的语法来剥夺一个分组对组号分配的参与权. 后向引用用于重复搜索前面某个分组匹配的文本。例如,\1代表分组1匹配的文本。难以理解?请看示例: \b(\w+)\b\s+\1\b可以用来匹配重复的单词,像go go, 或者kitty kitty。这个表达式首先是一个单词,也就是单词开始处和结束处之间的多于一个的字母或数字(\b(\w+)\b),这个单词会被捕获到编号为1的分组中,然后是1个或几个空白符(\s+),最后是分组1中捕获的内容(也就是前面匹配的那个单词)(\1)。 你也可以自己指定子表达式的组名。要指定一个子表达式的组名,请使用这样的语法:(?\w+)(或者把尖括号换成'也行:(?'Word'\w+)),这样就把\w+的组名指定为Word了。要反向引用这个分组捕获的内容,你可以使用\k,所以上一个例子也可以写成这样:\b(?\w+)\b\s+\k\b。 使用小括号的时候,还有很多特定用途的语法。下面列出了最常用的一些: 表4.常用分组语法 分类 代码/语法 说明 捕获 (exp) 匹配exp,并捕获文本到自动命名的组里 (?exp) 匹配exp,并捕获文本到名称为name的组里,也可以写成(?'name'exp) (?:exp) 匹配exp,不捕获匹配的文本,也不给此分组分配组号 零宽断言 (?=exp) 匹配exp前面的位置 (?<=exp) 匹配exp后面的位置 (?!exp) 匹配后面跟的不是exp的位置 (?<!exp) 匹配前面不是exp的位置 注释 (?#comment) 这种类型的分组不对正则表达式的处理产生任何影响,用于提供注释让人阅读 我们已经讨论了前两种语法。第三个(?:exp)不会改变正则表达式的处理方式,只是这样的组匹配的内容不会像前两种那样被捕获到某个组里面,也不会拥有组号。“我为什么会想要这样做?”——好问题,你觉得为什么呢? 零宽断言 地球人,是不是觉得这些术语名称太复杂,太难记了?我也有同感。知道有这么一种东西就行了,它叫什么,随它去吧!人若无名,便可专心练剑;物若无名,便可随意取舍…… 接下来的四个用于查找在某些内容(但并不包括这些内容)之前或之后的东西,也就是说它们像\b,^,$那样用于指定一个位置,这个位置应该满足一定的条件(即断言),因此它们也被称为零宽断言。最好还是拿例子来说明吧: 断言用来声明一个应该为真的事实。正则表达式中只有当断言为真时才会继续进行匹配。 (?=exp)也叫零宽度正预测先行断言,它断言自身出现的位置的后面能匹配表达式exp。比如\b\w+(?=ing\b),匹配以ing结尾的单词的前面部分(除了ing以外的部分),如查找I'm singing while you're dancing.时,它会匹配sing和danc。 (?<=exp)也叫零宽度正回顾后发断言,它断言自身出现的位置的前面能匹配表达式exp。比如(?<=\bre)\w+\b会匹配以re开头的单词的后半部分(除了re以外的部分),例如在查找reading a book时,它匹配ading。 假如你想要给一个很长的数字中每三位间加一个逗号(当然是从右边加起了),你可以这样查找需要在前面和里面添加逗号的部分:((?<=\d)\d{3})+\b,用它对1234567890进行查找时结果是234567890。 下面这个例子同时使用了这两种断言:(?<=\s)\d+(?=\s)匹配以空白符间隔的数字(再次强调,不包括这些空白符)。 负向零宽断言 前面我们提到过怎么查找不是某个字符或不在某个字符类里的字符的方法(反义)。但是如果我们只是想要确保某个字符没有出现,但并不想去匹配它时怎么办?例如,如果我们想查找这样的单词--它里面出现了字母q,但是q后面跟的不是字母u,我们可以尝试这样: \b\w*q[^u]\w*\b匹配包含后面不是字母u的字母q的单词。但是如果多做测试(或者你思维足够敏锐,直接就观察出来了),你会发现,如果q出现在单词的结尾的话,像Iraq,Benq,这个表达式就会出错。这是因为[^u]总要匹配一个字符,所以如果q是单词的最后一个字符的话,后面的[^u]将会匹配q后面的单词分隔符(可能是空格,或者是句号或其它的什么),后面的\w*\b将会匹配下一个单词,于是\b\w*q[^u]\w*\b就能匹配整个Iraq fighting。负向零宽断言能解决这样的问题,因为它只匹配一个位置,并不消费任何字符。现在,我们可以这样来解决这个问题:\b\w*q(?!u)\w*\b。 零宽度负预测先行断言(?!exp),断言此位置的后面不能匹配表达式exp。例如:\d{3}(?!\d)匹配三位数字,而且这三位数字的后面不能是数字;\b((?!abc)\w)+\b匹配不包含连续字符串abc的单词。 同理,我们可以用(?<!exp),零宽度负回顾后发断言来断言此位置的前面不能匹配表达式exp:(?<![a-z])\d{7}匹配前面不是小写字母的七位数字。 请详细分析表达式(?<=).*(?=),这个表达式最能表现零宽断言的真正用途。 一个更复杂的例子:(?<=).*(?=)匹配不包含属性的简单HTML标签内里的内容。()指定了这样的前缀:被尖括号括起来的单词(比如可能是),然后是.*(任意的字符串),最后是一个后缀(?=)。注意后缀里的\/,它用到了前面提过的字符转义;\1则是一个反向引用,引用的正是捕获的第一组,前面的(\w+)匹配的内容,这样如果前缀实际上是的话,后缀就是了。整个表达式匹配的是和之间的内容(再次提醒,不包括前缀和后缀本身)。 注释 小括号的另一种用途是通过语法(?#comment)来包含注释。例如:2[0-4]\d(?#200-249)|25[0-5](?#250-255)|[01]?\d\d?(?#0-199)。 要包含注释的话,最好是启用“忽略模式里的空白符”选项,这样在编写表达式时能任意的添加空格,Tab,换行,而实际使用时这些都将被忽略。启用这个选项后,在#后面到这一行结束的所有文本都将被当成注释忽略掉。例如,我们可以前面的一个表达式写成这样: (?<= # 断言要匹配的文本的前缀 # 查找尖括号括起来的字母或数字(即HTML/XML标签) ) # 前缀结束 .* # 匹配任意文本 (?= # 断言要匹配的文本的后缀 # 查找尖括号括起来的内容:前面是一个"/",后面是先前捕获的标签 ) # 后缀结束 贪婪与懒惰 当正则表达式中包含能接受重复的限定符时,通常的行为是(在使整个表达式能得到匹配的前提下)匹配尽可能多的字符。以这个表达式为例:a.*b,它将会匹配最长的以a开始,以b结束的字符串。如果用它来搜索aabab的话,它会匹配整个字符串aabab。这被称为贪婪匹配。 有时,我们更需要懒惰匹配,也就是匹配尽可能少的字符。前面给出的限定符都可以被转化为懒惰匹配模式,只要在它后面加上一个问号?。这样.*?就意味着匹配任意数量的重复,但是在能使整个匹配成功的前提下使用最少的重复。现在看看懒惰版的例子吧: a.*?b匹配最短的,以a开始,以b结束的字符串。如果把它应用于aabab的话,它会匹配aab(第一到第三个字符)和ab(第四到第五个字符)。 为什么第一个匹配是aab(第一到第三个字符)而不是ab(第二到第三个字符)?简单地说,因为正则表达式有另一条规则,比懒惰/贪婪规则的优先级更高:最先开始的匹配拥有最高的优先权——The match that begins earliest wins。 表5.懒惰限定符 代码/语法 说明 *? 重复任意次,但尽可能少重复 +? 重复1次或更多次,但尽可能少重复 ?? 重复0次或1次,但尽可能少重复 {n,m}? 重复n到m次,但尽可能少重复 {n,}? 重复n次以上,但尽可能少重复 处理选项 在C#中,你可以使用Regex(String, RegexOptions)构造函数来设置正则表达式的处理选项。如:Regex regex = new Regex(@"\ba\w{6}\b", RegexOptions.IgnoreCase); 上面介绍了几个选项如忽略大小写,处理多行等,这些选项能用来改变处理正则表达式的方式。下面是.Net中常用的正则表达式选项: 表6.常用的处理选项 名称 说明 IgnoreCase(忽略大小写) 匹配时不区分大小写。 Multiline(多行模式) 更改^和$的含义,使它们分别在任意一行的行首和行尾匹配,而不仅仅在整个字符串的开头和结尾匹配。(在此模式下,$的精确含意是:匹配\n之前的位置以及字符串结束前的位置.) Singleline(单行模式) 更改.的含义,使它与每一个字符匹配(包括换行符\n)。 IgnorePatternWhitespace(忽略空白) 忽略表达式中的非转义空白并启用由#标记的注释。 ExplicitCapture(显式捕获) 仅捕获已被显式命名的组。 一个经常被问到的问题是:是不是只能同时使用多行模式和单行模式中的一种?答案是:不是。这两个选项之间没有任何关系,除了它们的名字比较相似(以至于让人感到疑惑)以外。 平衡组/递归匹配 这里介绍的平衡组语法是由.Net Framework支持的;其它语言/库不一定支持这种功能,或者支持此功能但需要使用不同的语法。 有时我们需要匹配像( 100 * ( 50 + 15 ) )这样的可嵌套的层次性结构,这时简单地使用\(.+\)则只会匹配到最左边的左括号和最右边的右括号之间的内容(这里我们讨论的是贪婪模式,懒惰模式也有下面的问题)。假如原来的字符串里的左括号和右括号出现的次数不相等,比如( 5 / ( 3 + 2 ) ) ),那我们的匹配结果里两者的个数也不会相等。有没有办法在这样的字符串里匹配到最长的,配对的括号之间的内容呢? 为了避免(和\(把你的大脑彻底搞糊涂,我们还是用尖括号代替圆括号吧。现在我们的问题变成了如何把xx <aa aa> yy这样的字符串里,最长的配对的尖括号内的内容捕获出来? 这里需要用到以下的语法构造: • (?'group') 把捕获的内容命名为group,并压入堆栈(Stack) • (?'-group') 从堆栈上弹出最后压入堆栈的名为group的捕获内容,如果堆栈本来为空,则本分组的匹配失败 • (?(group)yes|no) 如果堆栈上存在以名为group的捕获内容的话,继续匹配yes部分的表达式,否则继续匹配no部分 • (?!) 零宽负向先行断言,由于没有后缀表达式,试图匹配总是失败 如果你不是一个程序员(或者你自称程序员但是不知道堆栈是什么东西),你就这样理解上面的三种语法吧:第一个就是在黑板上写一个"group",第二个就是从黑板上擦掉一个"group",第三个就是看黑板上写的还有没有"group",如果有就继续匹配yes部分,否则就匹配no部分。 我们需要做的是每碰到了左括号,就在压入一个"Open",每碰到一个右括号,就弹出一个,到了最后就看看堆栈是否为空--如果不为空那就证明左括号比右括号多,那匹配就应该失败。正则表达式引擎会进行回溯(放弃最前面或最后面的一些字符),尽量使整个表达式得到匹配。 < #最外层的左括号 [^]* #最外层的左括号后面的不是括号的内容 ( ( (?'Open'<) #碰到了左括号,在黑板上写一个"Open" [^]* #匹配左括号后面的不是括号的内容 )+ ( (?'-Open'>) #碰到了右括号,擦掉一个"Open" [^]* #匹配右括号后面不是括号的内容 )+ )* (?(Open)(?!)) #在遇到最外层的右括号前面,判断黑板上还有没有没擦掉的"Open";如果还有,则匹配失败 > #最外层的右括号 平衡组的一个最常见的应用就是匹配HTML,下面这个例子可以匹配嵌套的标签:]*>[^]*(((?'Open']*>)[^]*)+((?'-Open')[^]*)+)*(?(Open)(?!)). 还有些什么东西没提到 上边已经描述了构造正则表达式的大量元素,但是还有很多没有提到的东西。下面是一些未提到的元素的列表,包含语法和简单的说明。你可以在网上找到更详细的参考资料来学习它们--当你需要用到它们的时候。如果你安装了MSDN Library,你也可以在里面找到.net下正则表达式详细的文档。 这里的介绍很简略,如果你需要更详细的信息,而又没有在电脑上安装MSDN Library,可以查看关于正则表达式语言元素的MSDN在线文档。 表7.尚未详细讨论的语法 代码/语法 说明 \a 报警字符(打印它的效果是电脑嘀一声) \b 通常是单词分界位置,但如果在字符类里使用代表退格 \t 制表符,Tab \r 回车 \v 竖向制表符 \f 换页符 \n 换行符 \e Escape \0nn ASCII代码中八进制代码为nn的字符 \xnn ASCII代码中十进制代码为nn的字符 \unnnn Unicode代码中十进制代码为nnnn的字符 \cN ASCII控制字符。比如\cC代表Ctrl+C \A 字符串开头(类似^,但不受处理多行选项的影响) \Z 字符串结尾或行尾(不受处理多行选项的影响) \z 字符串结尾(类似$,但不受处理多行选项的影响) \G 当前搜索的开头 \p{name} Unicode中命名为name的字符类,例如\p{IsGreek} (?>exp) 贪婪子表达式 (?-exp) 平衡组 (?im-nsx:exp) 在子表达式exp中改变处理选项 (?im-nsx) 为表达式后面的部分改变处理选项 (?(exp)yes|no) 把exp当作零宽正向先行断言,如果在这个位置能匹配,使用yes作为此组的表达式;否则使用no (?(exp)yes) 同上,只是使用空表达式作为no (?(name)yes|no) 如果命名为name的组捕获到了内容,使用yes作为表达式;否则使用no (?(name)yes) 同上,只是使用空表达式作为no 联系作者 好吧,我承认,我骗了你,读到这里你肯定花了不止30分钟.相信我,这是我的错,而不是因为你太笨.我之所以说"30分钟",是为了让你有信心,有耐心继续下去.既然你看到了这里,那证明我的阴谋成功了.被忽悠的感觉很爽吧? 要投诉我,或者觉得我其实可以忽悠得更高明,或者有任何其它问题,欢迎来我的博客让我知道. 最后,来点广告…… 网上的资源及本文参考文献 • 微软的正则表达式教程 • System.Text.RegularExpressions.Regex类(MSDN) • 专业的正则表达式教学网站(英文) • 关于.Net下的平衡组的详细讨论(英文) • Mastering Regular Expressions (Second Edition) 更新纪录 1. 2006-3-27 第一版 2. 2006-10-12 第二版 o 修正了几个细节上的错误和不准确的地方 o 增加了对处理中文时的一些说明 o 更改了几个术语的翻译(采用了MSDN的翻译方式) o 增加了平衡组的介绍 o 放弃了对The Regulator的介绍,改用Regex Tester 3. 2007-3-12 V2.1 o 修正了几个小的错误 o 增加了对处理选项(RegexOptions)的介绍 4. 2007-5-28 V2.2 o 重新组织了对零宽断言的介绍 o 删除了几个不太合适的示例,添加了几个实用的示例 o 其它一些微小的更改 5. 2007-8-3 V2.21 o 修改了几处文字错误 o 修改/添加了对$,\b的精确说明 o 承认了作者是个骗子 o 给RegexTester添加了Singleline选项的相关功能 6. 2008-4-13 v2.3 o 调整了部分章节的次序 o 修改了页面布局,删除了专门的参考节 o 针对读者的反馈,调整了部分内容 7. 2009-4-11 v2.31 o 修改了几处文字错误 o 添加了一些注释说明 o 调整了一些措词
"^The": 匹配以 "The"开头的字符串; "of despair$": 匹配以 "of despair" 结尾的字符串; "^abc$": 匹配以abc开头和以abc结尾的字符串,实际上是只有abc与之匹配 "notice": 匹配包含notice的字符串 你可以看见如果你没有用我们提到的两个字符(最后一个例子),就是说 模式(正则表达式) 可以出现在被检验字符串的任何地方,你没有把他锁定到两边 这里还有几个字符 '*', '+',和 '?', 他们用来表示一个字符可以出现的次数或者顺序. 他们分别表示:"zero or more", "one or more", and "zero or one." 这里是一些例子: "ab*": 匹配字符串a和0个或者更多b组成的字符串("a", "ab", "abbb", etc.); "ab+": 和上面一样,但最少有一个b ("ab", "abbb", etc.); "ab?":匹配0个或者一个b; "a?b+$": 匹配以一个或者0个a再加上一个以上的b结尾的字符串. 你也可以在大括号里面限制字符出现的个数,比如 "ab{2}": 匹配一个a后面跟两个b(一个也不能少)("abb"); "ab{2,}": 最少更两个b("abb", "abbbb", etc.); "ab{3,5}": 2-5个b("abbb", "abbbb", or "abbbbb"). 你还要注意到你必须总是指定 (i.e, "{0,2}", not "{,2}").同样,你必须注意到, '*', '+', 和'?' 分别和一下三个范围标注是一样的,"{0,}", "{1,}", 和 "{0,1}"。 现在把一定数量的字符放到小括号里,比如: "a(bc)*": 匹配 a 后面跟0个或者一个"bc"; "a(bc){1,5}": 一个到5个 "bc." 还有一个字符 '│', 相当于OR 操作: "hi│hello": 匹配含有"hi" 或者 "hello" 的 字符串; "(b│cd)ef": 匹配含有 "bef" 或者 "cdef"的字符串; "(a│b)*c": 匹配含有这样 - 多个(包括0个)a或b,后面跟一个c 的字符串字符串; 一个点('.')可以代表所有的 单一字符: "a.[0-9]": 一个a跟一个字符再跟一个数字的 (含有这样一个字符串字符串将被匹配,以后省略此括号) "^.{3}$": 以三个字符结尾 . 中括号括住的内容只匹配一个 单一的字符 "[ab]": 匹配单个的 a 或者 b ( 和 "a│b" 一样); "[a-d]": 匹配'a' 到'd'的单个字符 (和"a│b│c│d" 还有 "[abcd]"效果一样); "^[a-zA-Z]": 匹配以字母开头的字符串 "[0-9]%": 匹配含有 形如 x% 的字符串 ",[a-zA-Z0-9]$": 匹配以逗号在加一个数字或字母结尾的字符串 你也可以把你不想要得字符列在中括号里,你只需要在总括号里面使用'^' 作为开头 (i.e., "%[^a-zA-Z]%" 匹配含有 两个百分号里面有一个非字母 的字符串). 为了能够解释,但"^.[$()│*+?{\"作为有特殊意义的字符的时候,你必须在这些字符面前加'', 还有在php3中你应该避免在模式的最前面使用\, 比如说,正则表达式 "(\$│?[0-9]+" 应该这样调用 ereg("(\\$│?[0-9]+", $str) (不知道php4是不是一样) 不要忘记在中括号里面的字符是这条规路的例外—在中括号里面, 所有的特殊字符,包括(''), 都将失去他们的特殊性质(i.e., "[*\+?{}.]"匹配含有这些字符的字符串). 还有,正如regx的手册告诉我们: "如果列表里含有 ']', 最好把它作为列表里的第一个字符(可能跟在'^'后面). 如果含有'-', 最好把它放在最前面或者最后面, or 或者一个范围的第二个结束点(i.e. [a-d-0-9]中间的‘-’将有效. 为了完整, 我应该涉及到 collating sequences, character classes, 同埋 equivalence classes. 但我在这些方面不想讲的太详细, 这些在下面的文章仲都不需要涉及到. 你们可以在regex man pages 那里得到更多消息. 如何构建一个模式来匹配 货币数量 的输入 好了,现在我们要用我们所学的来干一些有用的事:构建一个匹配模式去检查输入的信息是否为一个表示money的数字。我们认为一个表示money的数量有四种方式: "10000.00" 和 "10,000.00",或者没有小数部分, "10000" and "10,000". 现在让我们开始构建这个匹配模式: ^[1-9][0-9]*$ 这是所变量必须以非0的数字开头.但这也意味着 单一的 "0" 也不能通过测试. 以下是解决的方法: ^(0│[1-9][0-9]*)$ "只有0和不以0开头的数字与之匹配",我们也可以允许一个负号再数字之前: ^(0│-?[1-9][0-9]*)$ 这就是: "0 或者 一个以0开头可能有一个负号在前面的数字." 好了, 好了现在让我们别那么严谨,允许以0开头.现在让我们放弃 负号 , 因为我们在表示钱币的时候并不需要用到. 我们现在指定 模式 用来匹配小数部分: ^[0-9]+(\.[0-9]+)?$ 这暗示匹配的字符串必须最少以一个阿拉伯数字开头. 但是注意,在上面模式中 "10." 是不匹配的, 只有 "10" 和 "10.2" 才可以. (你知道为什么吗) ^[0-9]+(\.[0-9]{2})?$ 我们上面指定小数点后面必须有两位小数.如果你认为这样太苛刻,你可以改成: ^[0-9]+(\.[0-9]{1,2})?$ 这将允许小数点后面有一到两个字符. 现在我们加上用来增加可读性的逗号(每隔三位), 我们可以这样表示: ^[0-9]{1,3}(,[0-9]{3})*(\.[0-9]{1,2})?$ 不要忘记加号 '+' 可以被乘号 '*' 替代如果你想允许空白字符串被输入话 (为什么?). 也不要忘记反斜杆 ’\’ 在php字符串中可能会出现错误 (很普遍的错误). 现在,我们已经可以确认字符串了, 我们现在把所有逗号都去掉 str_replace(",", "", $money) 然后在把类型看成 double然后我们就可以通过他做数学计算了. 构造检查email的正则表达式 好,让我们继续讨论怎么验证一个email地址. 在一个完整的email地址中有三个部分: POP3 用户名 (在 '@' 左边的一切), '@', 服务器名(就是剩下那部分). 用户名可以含有大小写字母阿拉伯数字,句号 ('.'), 减号('-'), and 下划线 ('_'). 服务器名字也是符合这个规则,当然下划线除外. 现在, 用户名的开始和结束都不能是句点. 服务器也是这样. 还有你不能有两个连续的句点他们之间至少存在一个字符,好现在我们来看一下怎么为用户名写一个匹配模式: ^[_a-zA-Z0-9-]+$ 现在还不能允许句号的存在. 我们把它加上: ^[_a-zA-Z0-9-]+(\.[_a-zA-Z0-9-]+)*$ 上面的意思就是说: "以至少一个规范字符(除.意外)开头,后面跟着0个或者多个以点开始的字符串." 简单化一点, 我们可以用 eregi()取代 ereg().eregi()对大小写不敏感, 我们就不需要指定两个范围 "a-z" 和 "A-Z" – 只需要指定一个就可以了: ^[_a-z0-9-]+(\.[_a-z0-9-]+)*$ 后面的服务器名字也是一样,但要去掉下划线: ^[a-z0-9-]+(\.[a-z0-9-]+)*$ Done. 现在只需要用”@”把两部分连接: ^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*$ 这就是完整的email认证匹配模式了,只需要调用 eregi(‘^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*$ ’,$eamil) 就可以得到是否为email了 正则表达式的其他用法 提取字符串 ereg() and eregi() 有一个特性是允许用户通过正则表达式去提取字符串的一部分(具体用法你可以阅读手册). 比如说,我们想从 path/URL 提取文件名 – 下面的代码就是你需要: ereg("([^\\/]*)$", $pathOrUrl, $regs); echo $regs[1]; 高级的代换 ereg_replace() 和 eregi_replace()也是非常有用的: 假如我们想把所有的间隔负号都替换成逗号: ereg_replace("[ \n\r\t]+", ",", trim($str)); PHP被大量的应用于Web的后台CGI开发,通常是在用户数据数据之后得出某种结果,但是如果用户输入的数据不正确,就会出现问题,比如说某人的生日是"2月30日"!那应该怎么样来检验暑假是否正确呢? 在PHP中加入了正则表达式的支持,让我们可以十分方便的进行数据匹配。 2 什么是正则表达式: 简单的说,正则表达式是一种可以用于模式匹配和替换的强大工具。在几乎所有的基于UNIX/LINUX系统的软件工具中找到正则表达式的痕迹,例如:Perl或PHP脚本语言。此外,JavaScript这种客户端的脚本语言也提供了对正则表达式的支持,现在正则表达式已经成为了一个通用的概念和工具,被各类技术人员所广泛使用。 在某个Linux网站上面有这样的话:"如果你问一下Linux爱好者最喜欢什么,他可能会回答正则表达式;如果你问他最害怕什么,除了繁琐的安装配置外他肯定会说正则表达式。" 正如上面说的,正则表达式看起来非常复杂,让人害怕,大多数的PHP初学者都会跳过这里,继续下面的学习,但是PHP中的正则表达式有着可以利用模式匹配找到符合条件的字符串、判断字符串是否合乎条件或者用指定的字符串来替代符合条件的字符串等强大的功能,不学实在太可惜了…… 3 正则表达式的基本语法: 一个正则表达式,分为三个部分:分隔符,表达式和修饰符。 分隔符可以是除了特殊字符以外的任何字符(比如"/ !"等等),常用的分隔符是"/"。表达式由一些特殊字符(特殊字符详见下面)和非特殊的字符串组成,比如"[a-z0-9_-]+@[a-z0-9_-.]+"可以匹配一个简单的电子邮件字符串。修饰符是用来开启或者关闭某种功能/模式。下面就是一个完整的正则表达式的例子: /hello.+?hello/is 上面的正则表达式"/"就是分隔符,两个"/"之间的就是表达式,第二个"/"后面的字符串"is"就是修饰符。 在表达式中如果含有分隔符,那么就需要使用转义符号"\",比如"/hello.+?\/hello/is"。转义符号除了用于分隔符外还可以执行特殊字符,全部由字母构成的特殊字符都需要"\"来转义,比如"\d"代表全体数字。 4 正则表达式的特殊字符: 正则表达式中的特殊字符分为元字符、定位字符等等。 元字符是正则表达式中一类有特殊意义的字符,用来描述其前导字符(即元字符前面的字符)在被匹配的对象中出现的方式。元字符本身是一个个单一的字符,但是不同或者相同的元字符组合起来可以构成大的元字符。 元字符: 大括号:大括号用来精确指定匹配元字符出现的次数,例如"/pre{1,5}/"表示匹配的对象可以是"pre"、"pree"、"preeeee"这样在"pr"后面出现1个到5个"e"的字符串。或者"/pre{,5}/"代表pre出现0此到5次之间。 加号:"+"字符用来匹配元字符前的字符出现一次或者多次。例如"/ac+/"表示被匹配的对象可以是"act"、"account"、"acccc"等在"a"后面出现一个或者多个"c"的字符串。"+"相当于"{1,}"。 星号:"*"字符用来匹配元字符前的字符出现零次或者多次。例如"/ac*/"表示被匹配的对象可以是"app"、"acp"、"accp"等在"a"后面出现零个或者多个"c"的字符串。"*"相当于"{0,}"。 问号:"?"字符用来匹配元字符前的字符出现零次或者1次。例如"/ac?/"表示匹配的对象可以是"a"、"acp"、"acwp"这样在"a"后面出现零个或者1个"c"的字符串。"?"在正则表达式中还有一个非常重要的作用,即"贪婪模式"。 还有两个很重要的特殊字符就是"[ ]"。他们可以匹配"[]"之中出现过的字符,比如"/[az]/"可以匹配单个字符"a"或者"z";如果把上面的表达式改成这样"/[a-z]/",就可以匹配任何单个小写字母,比如"a"、"b"等等。 如果在"[]"中出现了"^",代表本表达式不匹配"[]"内出现的字符,比如"/[^a-z]/"不匹配任何小写字母!并且正则表达式给出了几种"[]"的默认值: [:alpha:]:匹配任何字母 [:alnum:]:匹配任何字母和数字 [:digit:]:匹配任何数字 [:space:]:匹配空格符 [:upper:]:匹配任何大写字母 [:lower:]:匹配任何小写字母 [:punct:]:匹配任何标点符号 [:xdigit:]:匹配任何16进制数字 另外下面这些特殊字符在转义符号"\"转义后代表的含义如下: s:匹配单个的空格符 S:用于匹配除单个空格符之外的所有字符。 d:用于匹配从0到9的数字,相当于"/[0-9]/"。 w:用于匹配字母,数字或下划线字符,相当于"/[a-zA-Z0-9_]/"。 W:用于匹配所有与w不匹配的字符,相当于"/[^a-zA-Z0-9_]/"。 D:用于匹配任何非10进制的数字字符。 .:用于匹配除换行符之外的所有字符,如果经过修饰符"s"的修饰,"."可以代表任意字符。 利用上面的特殊字符可以很方便的表达一些比较繁琐的模式匹配。例如"/\d0000/"利用上面的正则表达式可以匹配万以上,十万一下的整数字符串。 定位字符: 定位字符是正则表达式中又一类非常重要的字符,它的主要作用是用于对字符在匹配对象中的位置进行描述。 ^:表示匹配的模式出现在匹配对象的开头(和在"[]"里面不同) $:表示匹配的模式出现在匹配对象的末尾 空格:表示匹配的模式出现在开始和结尾的两个边界之一 "/^he/":可以匹配以"he"字符开头的字符串,比如hello、height等等; "/he$/":可以匹配以"he"字符结尾的字符串即she等; "/ he/":空格开头,和^的作用一样,匹配以he开头的字符串; "/he /":空格结束,和$的作用一样,匹配以he结尾的字符串; "/^he$/":表示只和字符串"he"匹配。 括号: 正则表达式除了可以用户匹配,还可以用括号"()"来记录需要的信息,储存起来,给后面的表达式读取。比如: /^([a-zA-Z0-9_-]+)@([a-zA-Z0-9_-]+)(.[a-zA-Z0-9_-])$/ 就是记录邮件地址的用户名,和邮件地址的服务器地址(形式为[email protected]之类的),在后面如果想要读取记录下来的字符串,只是需要用"转义符+记录的次序"来读取。比如"\1"就相当于第一个"[a-zA-Z0-9_-]+","\2"相当于第二个([a-zA-Z0-9_-]+),"\3"就是第三个(.[a-zA-Z0-9_-])。但是在PHP中,"\"是一个特殊的字符,需要转义,所以""到了PHP的表达式中就应该写成"\\1"。 其他特殊符号: "|":或符号"|"和PHP里面的或一样,不过是一个"|",而不是PHP的两个"||"!意思就是可以是某个字符或者另一个字符串,比如"/abcd|dcba/"可能匹配"abcd"或者"dcba"。 5 贪婪模式: 前面在元字符中提到过"?"还有一个重要的作用,即"贪婪模式",什么是"贪婪模式"呢? 比如我们要匹配以字母"a"开头字母"b"结尾的字符串,但是需要匹配的字符串在"a"后面含有很多个"b",比如"a bbbbbbbbbbbbbbbbb",那正则表达式是会匹配第一个"b"还是最后一个"b"呢?如果你使用了贪婪模式,那么会匹配到最后一个"b",反之只是匹配到第一个"b"。 使用贪婪模式的表达式如下: /a.+?b/ /a.+b/U 不使用贪婪模式的如下: /a.+b/ 上面使用了一个修饰符U,详见下面的部分。 6 修饰符: 在正则表达式里面的修饰符可以改变正则的很多特性,使得正则表达式更加适合你的需要(注意:修饰符对于大小写是敏感的,这意味着"e"并不等于"E")。正则表达式里面的修饰符如下: i :如果在修饰符中加上"i",则正则将会取消大小写敏感性,即"a"和"A" 是一样的。 m:默认的正则开始"^"和结束"$"只是对于正则字符串如果在修饰符中加上"m",那么开始和结束将会指字符串的每一行:每一行的开头就是"^",结尾就是"$"。 s:如果在修饰符中加入"s",那么默认的"."代表除了换行符以外的任何字符将会变成任意字符,也就是包括换行符! x:如果加上该修饰符,表达式中的空白字符将会被忽略,除非它已经被转义。 e:本修饰符仅仅对于replacement有用,代表在replacement中作为PHP代码。 A:如果使用这个修饰符,那么表达式必须是匹配的字符串中的开头部分。比如说"/a/A"匹配"abcd"。 E:与"m"相反,如果使用这个修饰符,那么"$"将匹配绝对字符串的结尾,而不是换行符前面,默认就打开了这个模式。 U:和问号的作用差不多,用于设置"贪婪模式"。 7 PCRE相关的正则表达式函数: PHP的Perl兼容正则表达式提供的多个函数,分为模式匹配,替换和匹配数目等等: 1、preg_match : 函数格式:int preg_match(string pattern, string subject, array [matches]); 这个函数会在string中使用pattern表达式来匹配,如果给定了[regs],就会将string记录到[regs][0]中,[regs][1]代表使用括号"()"记录下来的第一个字符串,[regs][2]代表记录下来的第二个字符串,以此类推。preg如果在string中找到了匹配的pattern,就会返回"true",否则返回"false"。 2、preg_replace : 函数格式:mixed preg_replace(mixed pattern, mixed replacement, mixed subject); 这个函数会使用将string中符合表达式pattern的字符串全部替换为表达式replacement。如果replacement中需要包含pattern的部分字符,则可以使用"()"来记录,在replacement中只是需要用"\1"来读取。 3、preg_split : 函数格式:array preg_split(string pattern, string subject, int [limit]); 这个函数和函数split一样,区别仅在与split可以使用简单正则表达式来分割匹配的字符串,而preg_split使用完全的Perl兼容正则表达式。第三个参数limit代表允许返回多少个符合条件的值。 4、preg_grep : 函数格式:array preg_grep(string patern , array input); 这个函数和preg_match功能基本上,不过preg_grep可以将给定的数组input中的所有元素匹配,返回一个新的数组。 下面举一个例子,比如我们要检查Email地址的格式是否正确: <?php function emailIsRight($email) { if (preg_match("^[_\.0-9a-z-]+@([0-9a-z][0-9a-z-]+\.)+[a-z]{2,3}$",$email)) { return 1; } return 0; } if(emailIsRight('[email protected]')) echo '正确<br>'; if(!emailIsRight('y10k@fffff')) echo '不正确<br>'; ?> 上面的程序会输出"正确<br>不正确"。 8.PHP中的Perl兼容正则表达式和Perl/Ereg正则表达式的区别: 虽然叫做“Perl兼容正则表达式”,但是和Perl的正则表达式相比,PHP的还是由一些不同,比如修饰符“G”在Perl里面代表全部匹配,但是在PHP中没有加入对这个修饰符的支持。 还有就是和ereg系列函数的区别,ereg也是PHP中提供的正则表达式函数,不过和preg相比,要弱上很多。 1、ereg里面是不需要也不能使用分隔符和修饰符的,所以ereg的功能比preg要弱上不少。 2、关于".":点在正则里面一般是除了换行符以外的全部字符,但是在ereg里面的"."是任意字符,即包括换行符!如果在preg里面希望"."能够包括换行符,可以在修饰符中加上"s"。 3、ereg默认使用贪婪模式,并且不能修改,这个给很多替换和匹配带来麻烦。 4、速度:这个或许是很多人关心的问题,会不会preg功能强大是以速度来换取的?不用担心,preg的速度要远远比ereg快,笔者做了一个程序测试: time test: PHP代码: <?php echo "Preg_replace used time:"; $start = time(); for($i=1;$i<=100000;$i++) { $str = "ssssssssssssssssssssssssssss"; preg_replace("/s/","",$str); } $ended = time()-$start; echo $ended; echo " ereg_replace used time:"; $start = time(); for($i=1;$i<=100000;$i++) { $str = "ssssssssssssssssssssssssssss"; ereg_replace("s","",$str); } $ended = time()-$start; echo $ended; echo " str_replace used time:"; $start = time(); for($i=1;$i<=100000;$i++) { $str = "sssssssssssssssssssssssssssss"; str_replace("s","",$str); } $ended = time()-$start; echo $ended; ?> 结果: Preg_replace used time:5 ereg_replace used time:15 str_replace used time:2 str_replace因为不需要匹配所以速度非常快,而preg_replace的速度比ereg_replace要快上不少。 9.关于PHP3.0对于preg的支持: 在PHP 4.0中默认加入了preg支持,但是在3.0中确没有。如果在3.0中希望使用preg函数,必须加载php3_pcre.dll文件,只要在php.ini的extension部分设置加入"extension = php3_pcre.dll"然后从新启动PHP就可以了! 其实正则表达式还常用于UbbCode的实现,很多PHP论坛都使用了这个方法(比如zForum zphp.com或者vB vbullent.com),但是具体的代码比较长。 本文来自http://blog.csdn.net/kkobebryant/archive/2005/01/25/267527.aspx
数据结构算法演示(Windows版) 使 用 手 册 一、 功能简介 本课件是一个动态演示数据结构算法执行过程的辅助教学软件, 它可适应读者对算法的输入数据和过程执行的控制方式的不同需求, 在计算机的屏幕上显示算法执行过程中数据的逻辑结构或存储结构的变化状况或递归算法执行过程中栈的变化状况。整个系统使用菜单驱动方式, 每个菜单包括若干菜单项。每个菜单项对应一个动作或一个子菜单。系统一直处于选择菜单项或执行动作状态, 直到选择了退出动作为止。 二、 系统内容 本系统内含84个算法,分属13部分内容,由主菜单显示,与《数据结构》教科书中自第2章至第11章中相对应。各部分演示算法如下: 1. 顺序表 (1)在顺序表中插入一个数据元素(ins_sqlist) (2)删除顺序表中一个数据元素(del_sqlist) (3)合并两个有序顺序表(merge_sqlist) 2. 链表 (1)创建一个单链表(Crt_LinkList) (2)在单链表中插入一个结点(Ins_LinkList) (3)删除单链表中的一个结点(Del_LinkList) (4)两个有序链表求并(Union) (5)归并两个有序链表(MergeList_L) (6)两个有序链表求交(ListIntersection_L) (7)两个有序链表求差(SubList_L) 3. 栈和队列 (1)计算阿克曼函数(AckMan) (2)栈的输出序列(Gen、Perform) (3)递归算法的演示  汉诺塔的算法(Hanoi)  解皇后问题的算法(Queen)  解迷宫的算法(Maze)  解背包问题的算法(Knap) (4)模拟银行(BankSimulation) (5)表达式求值(Exp_reduced) 4. 串的模式匹配 (1)古典算法(Index_BF) (2)求Next 函数值(Get_next)和按Next 函数值进行匹配 (Index_KMP(next)) (3)求 Next 修正值(Get_nextval)和按 Next 修正值进行匹配(Index_KMP(nextval)) 5. 稀疏矩阵 (1)矩阵转置 (Trans_Sparmat) (2)快速矩阵转置 (Fast_Transpos) (3)矩阵乘法 (Multiply_Sparmat) 6. 广义表 (1)求广义表的深度(Ls_Depth) (2)复制广义表(Ls_Copy) (3)创建广义表的存储结构(Crt_Lists) 7. 二叉树 (1)遍历二叉树  二叉树的线索化  先序遍历(Pre_order)  中序遍历(In_order)  后序遍历(Post_order) (2) 按先序建二叉树(CrtBT_PreOdr) (3) 线索二叉树  二叉树的线索化  生成先序线索(前驱或后继) (Pre_thre)  中序线索(前驱或后继) (In_thre)  后序线索(前驱或后继) (Post_thre)  遍历中序线索二叉树(Inorder_thlinked)  中序线索树的插入(ins_lchild_inthr)和删除(del_lchild_inthr)结点 (4)建赫夫曼树和求赫夫曼编码(HuffmanCoding) (5)森林转化成二叉树(Forest2BT) (6)二叉树转化成森林(BT2Forest) (7)按表达式建树(ExpTree)并求值(CalExpTreeByPostOrderTrav) 8. 图 (1)图的遍历  深度优先搜索(Travel_DFS)  广度优先搜索(Travel_BFS) (2)求有向图的强连通分量(Strong_comp) (3)有向无环图的两个算法  拓扑排序(Toposort)  关键路径(Critical_path) (4)求最小生成树  普里姆算法(Prim)  克鲁斯卡尔算法(Kruscal) (5)求关节点和重连通分量(Get_artical) (6)求最短路径  弗洛伊德算法(shortpath_Floyd)  迪杰斯特拉算法(shortpath_DIJ) 9. 存储管理 (1)边界标识法 (Boundary_tag_method) (2)伙伴系统 (Buddy_system) (3)紧缩无用单元 (Storage_compaction) 10. 静态查找 (1)顺序查找(Search_Seq) (2)折半查找 (Serch_Bin) (3)插值查找 (Search_Ins) (4)斐波那契查找 (Search_Fib) (5)次优查找树(BiTree_SOSTree) 11. 动态查找 (1)在二叉排序树上进行查找(bstsrch)、插入结点(ins_bstree)和删除结点(del_bstree) (2)在二叉平衡树上插入结点(ins_AVLtree) 和删除结点(del_AVLtree) (3)在 B-树上插入结点(Ins_BTree) 和 删除结点(Del_BTree) (4)在 B+树上插入结点(Ins_PBTree) 和 删除结点(Del_PBTree) 12. 内部排序 (1)简单排序法  直接插入排序(Insert_sort)  表插入排序(内含插入(Ins_Tsort) 重排(Arrange)两个算法)  起泡排序(BubbleSort)  简单选择排序(SelectSort) (2)复杂排序法  堆排序(HeapSort)  快速排序(QuickSort)  锦标赛排序(Tournament) (3)其他  快速地址排序(QkAddrst)  基数排序(RadixSort) 13. 外部排序 (1)多路平衡归并排序(K-Merge) (2)置换-选择排序(Repl_Selection) 三、 运行环境 1. 硬件:Pentium100以上PC机。 2. 软件:Windows95及以上版本的操作系统。 四、 运行 本系统的执行文件为DSDEMOW.EXE。 五、 如何使用本课件 读者可以利用鼠标移动光标选择“演示算法”或“菜单命令”来控制课件的运行过程。 1. 课件的演示算法菜单为页式菜单。第一级菜单中的各项与上述“系统内容”中各大项相对应,读者运行“算法演示课件”后, 即进入“算法选择一级菜单”画面,此时可移动光标进行选择,当光标所在菜单项改为红色时,单击鼠标即进入“算法选择二级菜单”,进行相同操作之后,或进入算法选择三级菜单(如图1所示),或进入算法演示的执行状态(如图2所示)。 图1 图2 在算法选择菜单画面中,形如 的图标意为尚有下级菜单,形如 的图标则表示将直接进入算法演示状态。此时也可直接单击一级菜单或二级菜单的标题直接返回之,注意:菜单右侧上方的“退出”按钮意为退出整个演示课件。 2. 算法演示执行状态下的屏幕分为三部分:第一行为“标题行”,第二行为“菜单命令”,以下为算法演示屏上各菜单的说明。 菜单命令中各项自左至右的功能分别为:  数据——设置算法演示的数据(数据结构)。  调用栈——察看当前函数(或过程)嵌套或递归的历程。  说明——察看算法说明。  暂停——中断演示过程。  执行——连续执行算法直至所设断点或至算法执行完毕。  单步——执行一行算法,遇到子程序调用时,连续执行完子程序。  跟踪——执行一行算法,遇到子程序调用时,进入子程序。  执行到——演示算法到当前所设最近的断点或算法窗口中的当前行。  恢复——重置屏幕为当前算法执行前的初始状态。  断点——在算法窗口的当前选择行设置断点或清除断点。  设置——设置连续演示时的速度或开/闭背景音乐(或动作音效)开关。  返回——返回算法选择菜单。 3. 断点的设置方法为:移动光标至“断点语句”所在行,点击鼠标后即出现绿色光条,之后单击“断点”菜单中的“设置断点”命令项即可,此时该断点语句所在行上将出现红色光条。 、 算法演示屏的详细说明 本系统对屏幕设计的基本原则是集数据结构、算法和其他重要信息(如栈等)于同一屏幕。一般情况下演示屏由图示、算法和变量三个窗口组成,特殊情况下将根据演示内容适当增加。一般情况下, 左侧图示窗口显示演示数据的逻辑结构或存储结构,右侧上方窗口显示算法文本,右侧下方窗口显示当前算法中各变量的值或递归工作栈的状态。各窗口间的边界大小均可自由调节,且可按需扩大至全屏。 算法窗口显示当前演示的算法文本,并以深蓝色的光条覆盖将要执行的语句。若算法中含有函数或过程调用语句,则当光条覆盖到该过程调用语句时,随即自动隐去原算法文本而显示子过程的文本,而从此过程返回时再重新显示原算法文本。类似地,在演示递归算法执行过程时,每当执行递归调用本过程的语句时,随即隐去当前层次的算法文本而显示下一层的算法文本,并且以不同颜色的算法文本表示递归的不同层次。如第一层的算法文本为深绿色,第二层为紫色,第三层为深红色,第四层为深蓝色,第五层为浅蓝色,第层为玫瑰红色等。 当演示递归算法执行过程中递归工作栈的变化状态时,递归工作栈显示在右侧下窗口,递归工作栈的状态和算法文本窗口中相应语句执行后的结果相对应,栈顶记录为当前递归层的参量值。每进入一层递归时,就产生一个新的工作记录(包括调用语句行号、变量参数或全程变量、数值参数和局部变量)压入栈顶;每退出一层递归时,先根据栈顶的调用语句行号返回至上层,然后在传递完变量参数的值后退栈。 各个算法演示屏的补充说明如下: 1. 顺序表和链表的插入、删除和链表的生成 算法演示屏由显示顺序表或链表的图示、算法文本及变量等三个窗口组成。在演示算法之前,需先在弹出的小窗口中输入线性表的数据元素及算法参数 i(插入或删除的位置)和 b(被插的数据元素)的值。顺序表的图示窗口在演示屏的上方,链表的图示窗口在左侧。 2. 有序表的操作 算法演示屏的状态和 1 中所述相同。 3. 汉诺塔问题 算法演示屏由4个窗口组成。右侧上方为算法文本,在算法中有4个形式参量,其中值参 n 为圆盘个数,x、y、和 z 分别表示3个塔座;右侧下方为递归工作栈,栈中每个记录包含调用语句行号 adr 及值参 n 和 x、y、z;左侧上方显示汉诺塔图形及移动操作结果;左侧下方显示移动操作的记录。 4. 迷宫问题 左侧窗口显示迷宫的逻辑结构,由 N×N 个方格组成,左上[1,1]为入口,右下[N,N]为出口,并且以红色钉子填充表示障碍,空白表示通路,红色交通灯表示已游历过的路,箭头表示继续游历的方向,演示结束时显示一条通路或迷宫不通的信息。右侧下窗口为递归工作栈,栈中每个记录含6个数据项,其中 adr 指示调用语句行号,k 指示步数,(x,y) 表示当前坐标,i 指示路径方向(起始方向为 1,顺时针方向旋转搜索)。 5. 皇后问题 左侧图示窗口包含棋盘和递归工作栈两部分,栈中记录含3个数据项,其中 adr 指示调用语句行号,k 指示列号,i 指示行号。此算法演示可求得所有可行结果,在求得每一种排布的结果之后,均会弹出一个窗口显示“找到第 j (j=1,2,…) 种排布”,单击“确定”按钮将继续进行,直至找到所有可能构成的排布。 6. 背包问题 右侧图示窗口的上方显示背包、物件及其体积。 若有解,则在求得每一组结果之后,均会弹出一个窗口提示求得一组解,单击提示窗口中的小人将继续进行。该窗口的下方为递归工作栈,栈中的记录含3个数据项,其中 adr 指示调用语句所在行号,n 指示物件个数,t 指示背包总体积。 7. 阿克曼函数 整个演示屏只有显示算法文本和显示算法执行过程中栈的状态两个窗口。在执行算法之前,首先应按照提示输入参数 m 和 n 的值。 8. 栈的输出序列 图示窗口的内容为:由算法 Gen 生成的栈的操作序列(列出在窗口的下方)、算法 Perform 执行时栈的操作过程(该窗口的上方)以及算法 Perform 执行的结果——栈的输出序列(列出在图示窗口的右侧)。 9. 表达式求值 图示窗口的内容主要为显示表达式求值过程中操作数栈和运算符栈的变化情况以及主要操作。上方的小窗口显示在算法演示之前设定的表达式。 10. 离散事件模拟 图示窗口分成3部分:中间部分或显示客户流动情况的动画,或显示程序执行过程中事件表和4个队列的数值,上方两个按钮用以切换动画或静态数据,下方则显示客户总人数、客户逗留的累计时间以及调节动画中小人移动速度的指针。 11. 串的模式匹配 上窗口显示算法文本,下窗口显示串的匹配过程或求 next 函数的过程。 12. 稀疏矩阵 图示窗口显示矩阵的状态或其三元组的表示。 13. 求广义表的深度 图示窗口显示广义表的存储结构,图中指针 ls 指向当前所求深度的广义表,值为空时不显示。演示结束时弹出窗口显示求得的深度。 14. 复制广义表 图示窗口的上方显示已知广义表的存储结构,图示窗口的下方显示复制求得的广义表的存储结构。递归工作栈中含调用语句行号 adr、变参 nls 和值参 ls。 15. 创建广义表的存储结构 图示窗口显示广义表存储结构的建立过程和算法执行过程中参数Sub的当前值。 16. 遍历二叉树 图示窗口显示二叉树的逻辑结构和遍历结果输出的结点序列,图中指针 bt 指向当前遍历的二叉树的根结点。 17. 线索二叉树 图示窗口显示二叉树的存储结构,但结点中只含标志域,而以结点间的黑色连线表示指针,红色连线表示前驱线索,蓝色连线表示后继线索。在二叉树线索化的过程中,图中指针 p 指向当前层二叉树的根结点,指针 pre 指向当前被访问的结点的前驱。在演示线索树的插入和删除过程时,图示窗口的下方还包括“输入行”和“提示行”。 18. 按先序序列建二叉链表 图示窗口显示输入的先序序列和生成二叉链表的过程。 19. 森林和二叉树的相互转换 图示窗口在显示屏的上方,其左侧为森林,右侧为二叉树。 20. 赫夫曼树和赫夫曼编码 图示窗口显示生成的赫夫曼树的逻辑结构和每个叶子结点的编码。 21. 图的深度优先搜索 图示窗口的上半部分显示图的逻辑结构,初始状态用青色圆圈表示顶点,结点间的黑色连线表示边或弧(连线上画有箭头)。演示过程中用红色覆盖已访问的顶点和边(或弧)。窗口下方显示图的邻接表,演示过程中以红色框边表示已访问过的弧。图示窗口的下方显示遍历后输出的顶点序列。 22. 图的广度优先搜索 与深度优先不同的是,在窗口的下方增加一个队列,其左端为队头,右端为队尾。 23. 求有向图的强连通分量 图示窗口自上而下分别显示有向图的逻辑结构、存储结构和 Finished 数组在算法执行过程中的变化情况。所求得的各个强连通分量,将以不同颜色的顶点组表示。 24. 求关节点和重连通分量 图示窗口的上半部分显示无向图,下半部分自上而下分别显示 Vexnum、Vexdata、Visited、Low、Squlow(求得 low 值的顺序)和 artpoint(关节点)的信息。 25. 有向图的拓扑排序 图示窗口由5部分组成。其中左上显示有向图的邻接表;左下显示有向图,其中顶点和弧的初始状态分别为绿色和黑色,从栈中退出的顶点(i)用红色表示,分别以蓝色和红色指示当前访问的邻接点(k)和它们之间的弧(ik),灰白色表示已经输出的顶点;右下显示顶点的入度;右上显示入度为零的栈。当拓扑排序不成功时,在演示屏的中央将会弹出一个窗口,显示提示信息“网中存在自环!”,此时用户可在左下显示的有向图中由绿色顶点和黑色弧构成的子图中找到这个环。 26. 有向图的关键路径 图示窗口包含5部分信息。左上显示带入度域的邻接表;左下显示有向网的逻辑结构和顶点的入度及各顶点事件的最早发生时间和最迟发生时间;右下显示拓扑排序过程中入度为零的顶点的栈S,右上显示的栈 T 存放拓扑序列,其入栈顺序和栈 S 的出栈顺序相同,从栈顶到栈底的顶点顺序即为顶点的逆拓扑序列。算法执行结束后将弹出窗口列出全部结果,其中红色字体的弧表示关键活动。 27. 普里姆算法 图示窗口包含3部分内容。右上是邻接矩阵;左上是无向网的逻辑结构,图中顶点的初始状态为黄色,算法执行过程中,红色覆盖的顶点和边则表示已加入生成树的顶点和生成树上的边;窗口的下方则显示 closedge 数组中的值。 28. 克鲁斯卡尔算法 图示窗口的左侧为无向网,以红色标定已落在生成树上的边;右侧自上而下列出各条边的信息以及选择生成树的边的执行过程。 29. 边界标识法 图示窗口的初始状态为 64KB 的模拟存储器,演示过程中,以绿色覆盖占用块。各个存储块的头部左侧所示为该块的起始地址,头部结构或其他信息参见教科书。用户可根据弹出窗口的操作提示信息进行操作,输入请求分配的空间大小或释放块的首地址。 30. 伙伴系统 在图示窗口中,左侧为可利用空间链表的逻辑结构,右侧为存储结构,其中红色覆盖部分为占用块。弹出窗口为输入窗口,由用户输入请求分配的空间大小或释放块的首地址。 31. 紧缩无用单元 右侧显示存储空间,空白表示空闲块,其他颜色覆盖表示占用块,在存储空间不足分配时将进行空闲块压缩。左侧显示存储映像。弹出窗口为输入窗口,由用户输入请求分配的空间大小和分配或释放块的块名。 32. 静态查找 上窗口为图示窗口,演示查找过程;左下和右下分别为算法文本和变量窗口。 33. B-树和B+树 整个屏幕分为上、下两个窗口,上窗口演示插入或删除结点过程中B-树或B+ 树结构的变化状况;下窗口内显示如查找关键字、插入位置、结点分裂等操作信息。下窗口上面左侧的小窗口为编辑窗口,由用户输入待插或待删的关键字,输入之后其右侧的操作命令将由隐式状态改为显式状态。 34. 内部排序 图示窗口演示排序过程以及排序过程中关键字之间进行的比较次数和记录移动的次数。 七、 用户自行输入数据指南 算法操作的对象——数据结构,或由用户自行输入,或由系统随机产生,并在获得用户的确认之前,可反复随机产生,直至用户满意,用鼠标点击“OK”按钮确认为止。 多数情况下的输入界面上有足够的提示信息,以指示用户需要进行何种操作。补充说明如下: 1. 表的数据元素为任意的单个字符。 2. 迷宫的输入界面如图3所示。图中砖墙图案表示障碍,连续点击鼠标可将光标所在位置设置成通道或者障碍,建议用户先点击“随机生成”按钮随机生成一个迷宫,然后移动鼠标调整成所需。所设迷宫可以利用“保存数据”按钮生成dat类型文件,并在需要时可以利用“取出数据”按钮读入。 图3 3. 演示背包问题的算法之前,首先需要输入物品个数,之后将出现如图4所示的输入界面,可以利用“随机生成”的按钮或各个相应的小窗口输入物品体积 wi 和背包体积 T 。背包的总体积不得超过 30 ,单个物品的体积不得超过 10 。 图4 4. “表达式求值”和“建表达式树”时的输入界面如图5所示。用户可在窗口内自行输入,并以“Enter”键为结束符;也可以连续点击左侧蓝色的表达式由系统自动生成,直至用户点击右侧的计算器表示确认为止。“求值”可实现带括弧的四则运算和幂次运算,并支持sin、cos、tan、arcsin 和 arccos 等函数计算,其操作数为实数。“建树”的表达式仅限于带括弧的四则运算,其操作数为单个字母的字符。 图5 5. 稀疏矩阵的输入界面如图6所示。用户可随意进行矩阵中任意位置元素的输入,只要将光标移动至待输入的元素位置,单击鼠标后将弹出计算器,单击数字按钮,可进行随意输入,之后点击“OK”按钮表示确认。 图6 6. 广义表的数据输入方式为自左向右顺序输入广义表的字符串。输入过程中,图7所示输入界面中的“确定”为灰色字体,只有当用户正确输入完毕时,“确定”两字才改为黑色字体,此时用户可单击此按钮表示确认。 图7 7. 图的输入界面如图8所示。之前尚需确认是否为有向图和带权图。在用户自行输入图时,首先按下“创建节点”按钮,之后可移动光标至窗口的任意位置单击鼠标创建顶点;然后单击“创建弧”按钮,可在任意两个顶点之间构建弧或边。构建弧(或边)的操作为:先将光标移动至弧尾的顶点,单击一次鼠标,然后移动光标至弧头位置,再单击一次鼠标。对于带权的图,则在构建弧(或边)的同时,在当时弹出的窗口中输入权值,权值的默认值为 1。 图8

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值