词法分析是把构成源程序的字符串转换成单词符号序列。词法规则可用 3型文法 (正规文法)或正规表达式描述,他产生的集合是语言基本字符集Σ(字母表)上的字符串的一个子集,称为正规集。
正规式是一种表示正规集的工具,正规式是描述程序语言单词的表达式,对于字母表Σ。
正规式(正规表达式)的运算符有3个,优化级从高到低顺序排列为:(闭包)、·(连接,可省略)、|(或)。
正规式 → 正规集
ab → 字符串ab构成的集合
a|b → 字符串a、b构成的集合
a → 由0个或多个a构成的字符串集合
(a|b) → 所有字符a和b构成的串的集合
a(a|b) → 以a为首字符的a、b字符串的集合
(a|b)abb → 以abb结尾的a、b字符串的集合
有限自动机可以转换为正规式,正规式也可以转换为有限自动机。
词法分析器的构造步骤:
1)用正规式描述语言中的单词构成规则。
2)为每个正规式构造一个NFA,它识别正规式所表示的正规集。
3)将构造出的NFA转换成等价的DFA。
4)对DFA进行最小化处理,使其最简。
5)从DFA构造词法分析器。
正则表达式
是由普通字符(例如字符 a 到 z)以及特殊字符(称为元字符)组成的文字模式。正则表达式作为一个模板,将某个字符模式与所搜索的字符串进行匹配。
可以通过在一对分隔符之间放入表达式模式的各种组件来构造一个正则表达式,即/expression/
普通字符
由所有那些未显式指定为元字符的打印和非打印字符组成。这包括所有的大写和小写字母字符,所有数字,所有标点符号以及一些符号。
非打印字符
字符 含义
\cx 匹配由x指明的控制字符。例如, \cM 匹配一个 Control-M 或回车符。x 的值必须为 A-Z 或 a-z 之一。否则,将 c 视为一个原义的 'c' 字符。
\f 匹配一个换页符。等价于 \x0c 和 \cL。
\n 匹配一个换行符。等价于 \ 和 \cJ。
\r 匹配一个回车符。等价于 \x0d 和 \cM。
\s 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。
\S 匹配任何非空白字符。等价于 [^ \f\n\r\t\v]。
\t 匹配一个制表符。等价于 \x09 和 \cI。
\v 匹配一个垂直制表符。等价于 \x0b 和 \cK。
特殊字符
所谓特殊字符,就是一些有特殊含义的字符,如上面说的"txt"中的,简单的说就是表示任何字符串的意思。如果要查找文件名中有*的文件,则需要对*进行转义,即在其前加一个\。ls \txt。正则表达式有以下特殊字符。
特别字符 说明
$ 匹配输入字符串的结尾位置。如果设置了 RegExp 对象的 Multiline 属性,则 $ 也匹配 '\n' 或 '\r'。要匹配 $ 字符本身,请使用 \$。
( ) 标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符,请使用 \( 和 \)。
匹配前面的子表达式零次或多次。要匹配 字符,请使用 \。
+ 匹配前面的子表达式一次或多次。要匹配 + 字符,请使用 \+。
匹配除换行符 \n之外的任何单字符。要匹配 ,请使用 \。
[ 标记一个中括号表达式的开始。要匹配 [,请使用 \[。
匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。要匹配 字符,请使用 \。
\ 将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符。例如, 'n' 匹配字符 'n'。'\n' 匹配换行符。序列 '\\' 匹配 "\",而 '\(' 则匹配 "("。
^ 匹配输入字符串的开始位置,除非在方括号表达式中使用,此时它表示不接受该字符集合。要匹配 ^ 字符本身,请使用 \^。
{ 标记限定符表达式的开始。要匹配 {,请使用 \{。
| 指明两项之间的一个选择。要匹配 |,请使用 \|。
构造正则表达式的方法和创建数学表达式的方法一样。也就是用多种元字符与 *** 作符将小的表达式结合在一起来创建更大的表达式。正则表达式的组件可以是单个的字符、字符集合、字符范围、字符间的选择或者所有这些组件的任意组合。
限定符
限定符用来指定正则表达式的一个给定组件必须要出现多少次才能满足匹配。有或+或或{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'。请注意在逗号和两个数之间不能有空格。
定位符
用来描述字符串或单词的边界,^和$分别指字符串的开始与结束,\b描述单词的前或后边界,\B表示非单词边界。不能对定位符使用限定符。
选择
用圆括号将所有选择项括起来,相邻的选择项之间用|分隔。但用圆括号会有一个副作用,是相关的匹配会被缓存,此时可用:放在第一个选项前来消除这种副作用。
其中:是非捕获元之一,还有两个非捕获元是=和!,这两个还有更多的含义,前者为正向预查,在任何开始匹配圆括号内的正则表达式模式的位置来匹配搜索字符串,后者为负向预查,在任何开始不匹配该正则表达式模式的位置来匹配搜索字符串。
后向引用
对一个正则表达式模式或部分模式两边添加圆括号将导致相关匹配存储到一个临时缓冲区中,所捕获的每个子匹配都按照在正则表达式模式中从左至右所遇到的内容存储。存储子匹配的缓冲区编号从 1 开始,连续编号直至最大 99 个子表达式。每个缓冲区都可以使用 '\n' 访问,其中 n 为一个标识特定缓冲区的一位或两位十进制数。
可以使用非捕获元字符 ':', '=', or '!' 来忽略对相关匹配的保存。
各种 *** 作符的运算优先级
相同优先级的从左到右进行运算,不同优先级的运算先高后低。各种 *** 作符的优先级从高到低如下:
*** 作符 描述
\ 转义符
(), (:), (=), [] 圆括号和方括号
, +, , {n}, {n,}, {n,m} 限定符
^, $, \anymetacharacter 位置和顺序
| “或” *** 作
全部符号解释
字符 描述
\ 将下一个字符标记为一个特殊字符、或一个原义字符、或一个 向后引用、或一个八进制转义符。例如,'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 31" 中的 "Windows"。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。
(!pattern) 负向预查,在任何不匹配 pattern 的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如'Windows (!95|98|NT|2000)' 能匹配 "Windows 31" 中的 "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 匹配一个换行符。等价于 \ 和 \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 匹配版权符号 ()。
VBScript内的使用方法:
function gfCheck(obj)
dim strCheck '待检字符串
dim objRE '正则式对象
dim strRtn '正则式判断结果
strCheck = objvalue
set objRE = New RegExp
objREPattern = "^[A-Za-z0-9]{13}$" '13位的英文字符和数字串
gfCheck = objRETest(strCheck) '符合正则式则返回true,反之则返回false
set objRE = nothing
end function
常用的正则式
1、非负整数:”^\d+$”
2、正整数:”^[0-9][1-9][0-9]$”
3、非正整数:”^((-\d+)|(0+))$”
4、负整数:”^-[0-9][1-9][0-9]$”
5、整数:”^-\d+$”
6、非负浮点数:”^\d+(\\d+)$”
7、正浮点数:”^((0-9)+\[0-9][1-9][0-9])|([0-9][1-9][0-9]\[0-9]+)|([0-9][1-9][0-9]))$”
8、非正浮点数:”^((-\d+\\d+))|(0+(\0+)))$”
9、负浮点数:”^(-((正浮点数正则式)))$”
10、英文字符串:”^[A-Za-z]+$”
11、英文大写串:”^[A-Z]+$”
12、英文小写串:”^[a-z]+$”
13、英文字符数字串:”^[A-Za-z0-9]+$”
14、英数字加下划线串:”^\w+$”
15、E-mail地址:”^[\w-]+(\[\w-]+)@[\w-]+(\[\w-]+)+$”
16、URL:”^[a-zA-Z]+://(\w+(-\w+))(\(\w+(-\w+)))(\\s)$”
在Sun的Java JDK 140版本中,Java自带了支持正则表达式的包,本文就抛砖引玉地介绍了如何使用javautilregex包。
可粗略估计一下,除了偶尔用Linux的外,其他Linu x用户都会遇到正则表达式。正则表达式是个极端强大工具,而且在字符串模式-匹配和字符串模式-替换方面富有d性。在Unix世界里,正则表达式几乎没有什么限制,可肯定的是,它应用非常之广泛。
正则表达式的引擎已被许多普通的Unix工具所实现,包括grep,awk,vi和Emacs等。此外,许多使用比较广泛的脚本语言也支持正则表达式,比如Python,Tcl,JavaScript,以及最著名的Perl。
我很早以前就是个Perl方面的黑客,如果你和我一样话,你也会非常依赖你手边的这些强大的text-munging工具。近几年来,像其他程序开发者一样,我也越来越关注Java的开发。
Java作为一种开发语言,有许多值得推荐的地方,但是它一直以来没有自带对正则表达式的支持。直到最近,借助于第三方的类库,Java开始支持正则表达式,但这些第三方的类库都不一致、兼容性差,而且维护代码起来很糟糕。这个缺点,对我选择Java作为首要的开发工具来说,一直是个巨大的顾虑之处。
你可以想象,当我知道Sun的Java JDK 140版本包含了javautilregex(一个完全开放、自带的正则表达式包)时,是多么的高兴!很搞笑的说,我花好些时间去挖掘这个被隐藏起来的宝石。我非常惊奇的是,Java这样的一个很大改进(自带了javautilregex包)为什么不多公开一点呢!
最近,Java双脚都跳进了正则表达式的世界。javautilregex包在支持正则表达也有它的过人之处,另外Java也提供详细的相关说明文档。使得朦朦胧胧的regex神秘景象也慢慢被拨开。有一些正则表达式的构成(可能最显著的是,在于糅合了字符类库)在Perl都找不到。
在regex包中,包括了两个类,Pattern(模式类)和Matcher(匹配器类)。Pattern类是用来表达和陈述所要搜索模式的对象,Matcher类是真正影响搜索的对象。另加一个新的例外类,PatternSyntaxException,当遇到不合法的搜索模式时,会抛出例外。
即使对正则表达式很熟悉,你会发现,通过java使用正则表达式也相当简单。要说明的一点是,对那些被Perl的单行匹配所宠坏的Perl狂热爱好者来说,在使用java的regex包进行替换 *** 作时,会比他们所以前常用的方法费事些。
本文的局限之处,它不是一篇正则表达式用法的完全教程。如果读者要对正则表达进一步了解的话,推荐阅读Jeffrey Frieldl的Mastering Regular Expressions,该书由O’Reilly出版社出版。我下面就举一些例子来教读者如何使用正则表达式,以及如何更简单地去使用它。
设计一个简单的表达式来匹配任何电话号码数字可能是比较复杂的事情,原因在于电话号码格式有很多种情况。所有必须选择一个比较有效的模式。比如:(212) 555-1212, 212-555-1212和212 555 1212,某些人会认为它们都是等价的。
首先让我们构成一个正则表达式。为简单起见,先构成一个正则表达式来识别下面格式的电话号码数字:(nnn)nnn-nnnn。
第一步,创建一个pattern对象来匹配上面的子字符串。一旦程序运行后,如果需要的话,可以让这个对象一般化。匹配上面格式的正则表达可以这样构成:(\d{3})\s\d{3}-\d{4},其中\d单字符类型用来匹配从0到9的任何数字,另外{3}重复符号,是个简便的记号,用来表示有3个连续的数字位,也等效于(\d\d\d)。\s也另外一个比较有用的单字符类型,用来匹配空格,比如Space键,tab键和换行符。
是不是很简单但是,如果把这个正则表达式的模式用在java程序中,还要做两件事。对java的解释器来说,在反斜线字符(\)前的字符有特殊的含义。在java中,与regex有关的包,并不都能理解和识别反斜线字符(\),尽管可以试试看。但为避免这一点,即为了让反斜线字符(\)在模式对象中被完全地传递,应该用双反斜线字符(\)。此外圆括号在正则表达中两层含义,如果想让它解释为字面上意思(即圆括号),也需要在它前面用双反斜线字符(\)。也就是像下面的一样:
\\(\\d{3}\\)\\s\\d{3}-\\d{4}
现在介绍怎样在java代码中实现刚才所讲的正则表达式。要记住的事,在用正则表达式的包时,在你所定义的类前需要包含该包,也就是这样的一行:
import javautilregex;
下面的一段代码实现的功能是,从一个文本文件逐行读入,并逐行搜索电话号码数字,一旦找到所匹配的,然后输出在控制台。
BufferedReader in;
Pattern pattern = Patterncompile("\\(\\d{3}\\)\\s\\d{3}-\\d{4}");
in = new BufferedReader(new FileReader("phone"));
String s;
while ((s = inreadLine()) != null)
{
Matcher matcher = patternmatcher(s);
if (matcherfind())
{
Systemoutprintln(matchergroup());
}
}
inclose();
对那些熟悉用Python或Javascript来实现正则表达式的人来说,这段代码很平常。在Python和Javascript这些语言中,或者其他的语言,这些正则表达式一旦明确地编译过后,你想用到哪里都可以。与Perl的单步匹配相比,看起来多多做了些工作,但这并不很费事。
find()方法,就像你所想象的,用来搜索与正则表达式相匹配的任何目标字符串,group()方法,用来返回包含了所匹配文本的字符串。应注意的是,上面的代码,仅用在每行只能含有一个匹配的电话号码数字字符串时。可以肯定的说,java的正则表达式包能用在一行含有多个匹配目标时的搜索。本文的原意在于举一些简单的例子来激起读者进一步去学习java自带的正则表达式包,所以对此就没有进行深入的探讨。
这相当漂亮吧! 但是很遗憾的是,这仅是个电话号码匹配器。很明显,还有两点可以改进。如果在电话号码的开头,即区位号和本地号码之间可能会有空格。我们也可匹配这些情况,则通过在正则表达式中加入\s来实现,其中元字符表示在模式可能有0或1个空格符。
第二点是,在本地号码位的前三位和后四位数字间有可能是空格符,而不是连字号,更有胜者,或根本就没有分隔符,就是7位数字连在一起。对这几种情况,我们可以用(-|)来解决。这个结构的正则表达式就是转换器,它能匹配上面所说的几种情况。在()能含有管道符|时,它能匹配是否含有空格符或连字符,而尾部的元字符表示是否根本没有分隔符的情况。
最后,区位号也可能没有包含在圆括号内,对此可以简单地在圆括号后附上元字符,但这不是一个很好的解决方法。因为它也包含了不配对的圆括号,比如"(555" 或 "555)"。相反,我们可以通过另一种转换器来强迫让电话号码是否带有有圆括号:(\(\d{3}\)|\d{3})。如果我们把上面代码中的正则表达式用这些改进后的来替换的话,上面的代码就成了一个非常有用的电话号码数字匹配器:
Pattern pattern =
Patterncompile("(\\(\\d{3}\\)|\\d{3})\\s\\d{3}(-|)\\d{4}");
可以确定的是,你可以自己试着进一步改进上面的代码。
现在看看第二个例子,它是从Friedl的中改编过来的。其功能是用来检查文本文件中是否有重复的单词,这在印刷排版中会经常遇到,同样也是个语法检查器的问题。
匹配单词,像其他的一样,也可以通过好几种的正则表达式来完成。可能最直接的是\b\w+\b,其优点在于只需用少量的regex元字符。其中\w元字符用来匹配从字母a到u的任何字符。+元字符表示匹配匹配一次或多次字符,\b元字符是用来说明匹配单词的边界,它可以是空格或任何一种不同的标点符号(包括逗号,句号等)。
现在,我们怎样来检查一个给定的单词是否被重复了三次为完成这个任务,需充分利用正则表达式中的所熟知的向后扫描。如前面提到的,圆括号在正则表达式中有几种不同的用法,一个就是能提供组合类型,组合类型用来保存所匹配的结果或部分匹配的结果(以便后面能用到),即使遇到有相同的模式。在同样的正则表达中,可能(也通常期望)不止有一个组合类型。在第n个组合类型中匹配结果可以通过向后扫描来获取到。向后扫描使得搜索重复的单词非常简单:\b(\w+)\s+\1\b。
圆括号形成了一个组合类型,在这个正则表示中它是第一组合类型(也是仅有的一个)。向后扫描\1,指的是任何被\w+所匹配的单词。我们的正则表达式因此能匹配这样的单词,它有一个或多个空格符,后面还跟有一个与此相同的单词。注意的是,尾部的定位类型(\b)必不可少,它可以防止发生错误。如果我们想匹配"Paris in the the spring",而不是匹配"Java's regex package is the theme of this article"。根据java现在的格式,则上面的正则表达式就是:Pattern pattern =Patterncompile("\\b(\\w+)\\s+\\1\\b");
最后进一步的修改是让我们的匹配器对大小写敏感。比如,下面的情况:"The the theme of this article is the Java's regex package",这一点在regex中能非常简单地实现,即通过使用在Pattern类中预定义的静态标志CASE_INSENSITIVE :
Pattern pattern =Patterncompile("\\b(\\w+)\\s+\\1\\b",
PatternCASE_INSENSITIVE);
有关正则表达式的话题是非常丰富,而且复杂的,用Java来实现也非常广泛,则需要对regex包进行的彻底研究,我们在这里所讲的只是冰山一角。即使你对正则表达式比较陌生,使用regex包后会很快发现它强大功能和可伸缩性。如果你是个来自Perl或其他语言王国的老练的正则表达式的黑客,使用过regex包后,你将会安心地投入到java的世界,而放弃其他的工具,并把java的regex包看成是手边必备的利器。
这个没有一个好老师,自己咬文嚼字看懂是很累的
二义性文法
定义 若文法中存在这样的句型,它具有两棵不同的语法树,则称该文法是二义性文法。
二义性文法会引起歧义,应尽量避免之!
G(E):E -> E+E | EE | (E) | i
这两种展开
E E
E + E E E
i E E E + E i
i i i i
都可以表示i+ii
所以;文法具有二义性。
0型文法(Type-0 Grammar)
1型文法(Type-1 Grammar)
2型文法(Type-2 Grammar)
3型文法(Type-3 Grammar)
无限制文法(Unrestricted Grammar) /短语结构文法(Phrase Structure Grammar, PSG )
∀α → β∈P, α中至少包含1个非终结符
0型语言
由0型文法G生成的语言L(G )
上下文有关文法(Context-Sensitive Grammar , CSG )
∀α → β∈P,|α|≤|β|
产生式的一般形式: α1Aα2 → α1βα2 ( β≠ε )
上下文有关语言(1型语言)
由上下文有关文法(1型文法) G生成的语言L(G )
上下文无关文法(Context-Free Grammar, CFG )
∀α → β∈P,α ∈ VN
产生式的一般形式:A→β
上下文无关语言(2型语言)
由上下文无关文法(2型文法) G生成的语言L(G )
正则文法(Regular Grammar, RG )
右线性(Right Linear)文法: A→wB 或 A→w
左线性(Left Linear) 文法: A→Bw 或 A→w
左线性文法和右线性文法都称为正则文法
0型文法:α中至少包含1个非终结符
1型文法(CSG) :|α|≤|β|
2型文法(CFG) :α ∈ VN
3型文法(RG):A→wB 或 A→w (A→Bw 或A→w)
0型文法包含1型文法,1型文法包含2型文法,2型文法包含3型文法
public abstract class RegularExpressionConverter<T>
{
protected RegularExpressionConverter() { }
public T Convert(RegularExpression expression)
{
if (expression == null)
{
return default(T);
}
return expressionAccept(this);
}
public abstract T ConvertAlternation(AlternationExpression exp);
public abstract T ConvertSymbol(SymbolExpression exp);
public abstract T ConvertEmpty(EmptyExpression exp);
public abstract T ConvertConcatenation(ConcatenationExpression exp);
public abstract T ConvertAlternationCharSet(AlternationCharSetExpression exp);
public abstract T ConvertStringLiteral(StringLiteralExpression exp);
public abstract T ConvertKleeneStar(KleeneStarExpression exp);
}
然后我们给RegularExpression类加上一个Accept抽象方法,让其子类分别实现。比如,KleeneStarExpression类的Accept就可以写成这样:
internal override T Accept<T>(RegularExpressionConverter<T> converter)
{
return converterConvertKleeneStar(this);
}
最后我们实现一个NFAConverter,实现抽象类RegularExpressionConverter<NFAModel>。其中NFAModel是我们自己定义的NFA对象模型,其中定义了状态节点、边等概念。下面就是NFAConverter中翻译克林闭包的规则:
public override NFAModel ConvertKleeneStar(KleeneStarExpression exp)
{
var innerNFA = Convert(expInnerExpression);
var newTail = new NFAState();
var entry = new NFAEdge(newTail);
innerNFATailStateAddEmptyEdgeTo(newTail);
newTailAddEdge(innerNFAEntryEdge);
var kleenStarNFA = new NFAModel();
kleenStarNFAAddStates(innerNFAStates);
kleenStarNFAAddState(newTail);
kleenStarNFAEntryEdge = entry;
kleenStarNFATailState = newTail;
return kleenStarNFA;
}
以上就是关于程序设计语言|正规式全部的内容,包括:程序设计语言|正规式、如何写正则表达式、如何使用正则表达式等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)