java expression_Java Regular Expression的学习笔记

虽然Reqular Expressions(以下简称REs)在这个论坛或是其他网站都可以找到相当多的资料,但是当我自己要学的时候才发现有很多小地方还是看不懂,所以才以java API裡面的说明为主,把每个符号的解释一一弄懂,终于对REs有了初步的认识。

所以这份文件是以java API提到的符号解释,加上我自己的心得及范例所整理出来的,我用"字元"和"字元组成",这两大部分来解释REs的符号,大部分的解释都有范例,这样比较容易瞭解,没有范例的部分不是太简单,就是我找不到例子,不过对于认识REs应该没有多大影响。

因为我算是REs的初学者,所以难免有观念上的错误,加上很多"冷门"的符号,要找到正确的解释都很困难,因此我没把握所有的资料都是正确的,如果各位有发现错误,还请加以指正。

首先是字元,REs的基本元素就是字元,所以对于字元有相当细腻的描述方式,而且很多时候描述方式都不是唯一的,所以不必拘泥于找到最完美的写法。

字元分两部分来解释。

1.一般字元,在还没做字元组合时,下面这些都只是"一个"字元,先要有这个观念,才不容易弄不清楚字串和字元的区别。

x : 一个字元,例如 "a" 表示要找含有这个字元的部分。

[abc] : 这个字元可能是a或b或c

[^abc] : 这个字元是除了"a" "b" "c"以外的。

[a-zA-Z] : 这个字元是a到z或是A到Z。

[a-d[m-p]] : 这个字元是a到d或是m到p (联集)。

[a-z&&[def]] : 这个字元是"d", "e", or "f" (交集)。

[a-z&&[^bc]] : 这个字元是a-z但b c 除外,等价于 [ad-z] (差集)。

[a-z&&[^m-p]] : 这个字元是a-z但 m-p 除外,等价于 [a-lq-z] (差集)。

除了放在一开始的"^"以及两个"&&"以外,放在[]裡面的都当一般字元。

可是\比较特殊,要\\才能当一个\,所以程式裡面必须写\\\\,例如,想查"\"用[]包起来要写成"[\\]",可是java在字串裡面要写String pattern="[\\\\]"。

2.特殊字元

. : 所有的字元,不一定包含换行。java REs的"."预设是不包含"\r"和"\n"的,但是可以用(?s)来让"."等于所有字元,(?s)的用法下面还有说明。

\r : Carriage return。

\t : TAB。

\n : 换行。

\f : 换页。

\e : escape。

\d : 数字0-9。

\D : 非数字,数字的除外集合。

\s : 会产生空白的字元也就是 [ \t\n\x0B\f\r],也就是 " "(空白)、"\t"、"\n"、"\x0B"、"\f"、"\r"。

\S : 非会产生空白的字元,上面的除外集合。

\w : 文字 a-z A-Z _ 0-9,所有的英文大小写,数字和底线。

\W : 非文字,文字的除外集合。

\ : 把之后的特殊字元当作是一般是字元,例如"\\"等于一个"\","\["等于"["。

\Q \E:\Q到\E符号之间的特殊符号都当一般字元处理,例如"\Q(?:X)\E",符合的字串是"(?:X)"

^ : 行首,例如"^e"会找出所有在行首的"e",在[]裡面如果是第一个代表反相,如果不是第一个也当一般字元。

$ : 行尾,例如"e$"会找出所有在结尾的"e"。

\b : 符合文字边界(word boundary)。也就是说在字与空格之间的位置 例如,'re\b' 符合"are" 裡的're'但是不符合"area"裡的're'

\B : 符合非文字边界 例如,'re\B' 符合"area"裡的're'但是不符合"are" 裡的're'

\A : 输入的开始。

\G : 前一个符合的结尾的地方。

\Z : 输入的结尾去掉结尾符号的部分。例如字串"ABC\n",pattern "ABC\Z"就可以取得"ABC"。

\z : 输入的结尾。例如字串"ABC\n",pattern "ABC\z"就不相符。

所谓的输入,是指一次的处理资料,例如String s="ABC\nABC\tABC",就算一个输入(input sequence)。

到这裡为止是对字元的描述,字元的所有的可能情况应该都可以涵盖了,可是光是字元是没办法构成字串,所以接下来是把字元组成字串的方法。

字元组成,字元组成分三部分来解释,下面符号的X和Y可以是一个字元也可以是一个Group。

1.简单组合,就是把字元排在一起。

XY : 单纯排列在一起,例如"ab"就是找"ab"这个字串,和"[ab]"不一样,"[ab]"是代表"一个字元"可能是a或b。

X|Y : X or Y,例如 a|b,而以字元而言a|b就等于[ab],所以对单一字元效果不大,主要是用于字元范围的[]|[]或者群组()|()比较有意义。

例如[a-z]|[0-9]表示不是小写就是数字,(abc)|(123)表示是"abc"或是"123"。

实例 "c|Car"相符字串是"c"或"Car"。

另外abc|def是指"abc"或"def",而不是"ab[cd]ef"。

(X) : 群组,将多个字元包装成一个群组,和单纯排列不同的地方是,群组可以参照,也可以对群组设定出现次数,

例如(abc)+是指"abc"出现一次以上,abc+是指ab和一次以上的c。

群组参照举例来说比较容易懂,例如"(.)(.)(.).?\3\2\1",可以找出3个字的回文。如"abccba"、"xcdfdcx"。

群组还有一个值得注意的是,群组0是留给整体的比对的结果,例如上面的例子group 0是"abccba",group 1是"a"、group 2是"b"、group 3是"c"。

有了群组参照的观念,后面的non-capturing group就会比较容易瞭解。。

Group的另一个对应符号是

\m : m是数字,表示参照前面的group,如上述的范例。

2.重複次数

出现次数接在字元之后,表示这个字元出现的次数,接在Group之后就表示group的出现次数。

次数描述有三种quantifiers,

Greedy quantifiers

X?: X出现0或一次

X+: X出现一次以上

X*: X出现0或一次以上

X{n,}: X出现至少n次

X{n,m}: X出现n到m次

X{n}: X出现n次

Reluctant quantifiers

X??: X出现0或一次

X+?: X出现一次以上

X*?: X出现0或一次以上

X{n,}?: X出现至少n次

X{n,m}?: X出现n到m次

X{n}?: X出现n次

Possessive quantifiers

X?+: X出现0或一次

X++: X出现一次以上

X*+: X出现0或一次以上

X{n,}+: X出现至少n次

X{n,m}+: X出现n到m次

X{n}+: X出现n次

光看这样的说明是无法分出三者不同,以下举例说明。

Greedy quantifiers

字串 "xfooxxxxxxfoo"

pattern ".*foo"

结果 xfooxxxxxxfoo

Greedy字面翻译是贪婪,也就是尽可能的取字串,其实最贪婪的是第三种方法,因为Greedy还会把之后相符的资料留下来,Possessive吃的连骨头都不剩。

Reluctant quantifiers

字串 "xfooxxxxxxfoo"

pattern ".*?foo"

结果 xfoo 和 xxxxxxfoo

Reluctant字面翻译是勉强,也就是抓最小可能,像这个例子,第一次抓一个x之后发现后面和foo相符,就得第一个结果,然后一直到最后又得到第二个结果。

Possessive quantifiers

字串 "xfooxxxxxxfoo"

pattern ".*+foo"

结果 没有相符合资料,因为所有的资料都与"."比较相符,最后没有剩下的字串可以和foo做比较,所以没有符合资料。

3.Special constructs (non-capturing)

所谓的non-capturing就是说这个group会被比对,但是不会暂存在group裡面,就是最后得到的Group裡面不会有这组资料。

(?:X) :X会取得,但不会被保留,当之后有用\m的时候,这个Group会不算在内,这样的处理效能会比较好。

(?i d m s u x) : 特别设定的flag设为on。

(?-i -d -m -s -u -x) : 特别设定的flag设为off。

i d m s u x的说明如下:

i CASE_INSENSITIVE : 就是不分大小写。(?i)

例如

字串 "ABC"

pattern 用"abc"会找不到,用"(?i)abc"就会找到"ABC"。

d UNIX_LINES : \n当作换行,当文件是UNIX的换行格式时,要处理换行就可以打开这个模式。(?d)

m MULTILINE :多行模式下,^和$是以指每一行,不然是用整个字串的头尾当^和$。(?m)

例如

字串 "ABC\nABC\nABC";

pattern "^ABC$"会找不到, "(?m)^ABC$"才会找到三个"ABC";

s DOTALL : 预设java的.不含\n \r,这个模式可以让.等于所有字元包含\r \n。(?s)

例如

字串 "htm\nhtm\nhtm"

pattern 用".htm"会找不到,用"(?s).htm"就会找到后面两个"\nhtm"

u UNICODE_CASE : unicode模式。(?u)

x COMMENTS :可以在pattern裡面使用注解,会忽略pattern裡面的whitespace,以及"#"一直到结尾。(?x)

例如

字串 "ABC"

pattern 用"A B C #找字串ABC" 会找不到,用"(?x)A B C #找字串ABC",就会找到"ABC"。

(?idmsux-idmsux:X) :X是non-capturing group并且设定flags on -off。

X(?=X) : lookahead在要取得的字串右边,接著X但X不被算在内。例如Jack(?=Sprat) 则只有JackSprat的Jack会被取得,Jack(?=Sprat|Frost),则只有JackSprat和JackFrost的Jack都符合。

X(?!X) : lookahead在要取得的字串右边,和上面相反,例如Jack(?!Sprat) 则后面是Sprat的Jack不会被取得。

(?<=X)X : lookbehind在要取得的字串左边,例如"(?<=foo)bar",找接在foo之后的"bar"。还有裡面的文字必须已知长度,也就是不能用"(?<=foo+)" "(?<=foo*)" "(?<=foo{1,})",但是可以用"(?<=foo?)" "(?<=foo{1,2})" "(?<=foo{1})"。

(?

(?>X) : X, as an independent, non-capturing group。

因为这几个符号都是non-capturing的,所以会有一个现象,直接看下面范例会比较容易瞭解

字串 "abc"

pattern "a(?:b)c"

结果 "abc" 但是b没有变成group 1,这也就是non-capturing。

如果用 "a(b)c" 会得到 group 0是"abc",group 1是b。

字串 "abc"

pattern "a(?=b)c"

结果 抓不到,因为b并没有被取出,要"a(?=b).c"才抓的到,而且"a(?=b).c"只会与"abc"相符,不会与"acc"等等相符。

字串 "abc"

pattern "a(?<=b)c"

结果 抓不到,因为b并没有被取出,要"a.(?<=b)c"才抓的到,而且"a(?<=b).c"只会与"abc"相符,不会与"acc"等等相符。

由上面例子可知,这几个符号都不会把符合的字串取出,也就是会比对但是不会算到结果裡面(non-capturing)。

所以lookahead和lookbehind那四个符号,不适合放在字串中间,另外,因为这几个符号都是non-capturing,所以在后面加上大于0的次数都和一次是一样的,例如字串"XB",pattern"(?<=X){9}B"一样可以取得B,而pattern""(?<=A){0}B"也可以取得"B"。

整个java的REs大概就这些了,只是看完这些解释其实离可以运用还有一小段距离,因为REs需要的是分析pattern的能力,而这种能力要多练习才会。

在自己能写出pattern之前,可以拿别人写好的pattern来测试体会一下,下面是一个简单的测试程式。

这个程式是我小修改网路上找到的范例,会回传符合的group,方便测试结果。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

import java.util.regex.Matcher;

import java.util.regex.Pattern;

public class TestRegular {

public static void main(String[] args) {

String inputStr = "ABC\nABC\nABC";

String patternStr = "(?d)ABC";

Pattern pattern = Pattern.compile(patternStr);

Matcher matcher = pattern.matcher(inputStr);

boolean matchFound = matcher.find();

while(matchFound) {

System.out.println(matcher.start() + "-" + matcher.end());

for(int i = 0; i <= matcher.groupCount(); i++) {

String groupStr = matcher.group(i);

System.out.println(i + ":" + groupStr);

}

if(matcher.end() + 1 <= inputStr.length()) {

matchFound = matcher.find(matcher.end());

}else{

break;

}

}

}

}

测试REs也可以使用一外部工具,例如eclipse的plugin Regex tester,我很多范例跟观念都是用这个工具去测试的。

最后"反组译"几个例子来练习,就是把别人写好的pattern试著解释出来。

HTML TAG

?[a-z][a-z0-9]*[^<>]*>

开始是"的字元不限次数个,最后以">"结尾。

相符的是"" "" ""等。

不相符字串 "<123>"

HTML TAG 之二

]*>(.*?)\1>

开始是"字元,然后是一个">",不限定个数的字元(用*?才不会一直取到之后的tag去),然后是tag结束的"",要和第一个Group的值match,最后以">"结束。

相符的有 "Test" "

Test
" ""

不相符字串 "" "<123>123>"

IP

\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b

前后都是"\b"表示在独立的一个字的单元,用"?:"是不算group而已,250-255或200-249或0-199,接著一个".",这样的group有3次,最后再一次0-255的group。

这样就是一个0.0.0.0 - 255.255.255.255的IP的pattern,而[0-9][0-9]?也可以改成[0-9]{1,2}。

相符的字串 "140.115.83.240" "255.255.0.0"

不相符字串 "256.1.1.2" "-1.300.1.2"

IP之二

\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b

IP之三

[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}

这是0.0.0.0 - 999.999.999.999的格式

其他例子

"[hc]+at" 相符的有 "hat", "cat", "hhat", "chat", "hcat", "ccchat" etc.

"[hc]?at" 相符的有 "hat", "cat" and "at"

"([cC]at)|([dD]og)" 相符的有 "cat", "Cat", "dog" and "Dog"

"/\*.*\*/" 相符的有 /* Second comment */

以下几个(都是抄的)适合检查输入的值,因为都从^到$,从字串开始到结束。

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*)?$”

-------

学习REs最重要是学会找到字串出现的特徵,特徵有时候是出现的位置,例如位置是行首(^),是行尾($),单字开始(\b),单字裡面(\B),有时候是前后出现的字串,例如要找html tag的属性,都会由""结束。而要抓""的url就可以从"href"开始。但有时候要找的字并没有特殊的位置,也没关系,就把要找的字描述出来也就可以了,例如要抓日期就做一个"(0[1-9]|[12][0-9]|3[01])[- /.](0[1-9]|1[012])[- /.](19|20)[0-9]{2}"的pattern就可以了。

而其实真正困难的地方在于很多pattern都要看到结果才会瞭解哪裡错了。

看这个例子

字串 "ABCDC"

pattern "([A-Z]*).\1"

预期要抓到CDC的,结果却是"A" "B" "CDC",因为*是0~n所以没抓也算,而后面的\1如果前面group没抓到,他也跟著什麽都没有,最后只有"."抓到第一个结果的"A",同理抓到第二个结果的"B",然后才抓到预期的"CDC",而改成"([A-Z]+).\1"就只会抓到"CDC"了。

最后谁能帮忙解释一下"(?>X)",因为我实在分不出来他和(?:X)有什麽差别....

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java Expression Evaluator 是一个用 Java 编写的库,可以解析和计算数学表达式,并支持自定义函数和变量。它可以用于在 Java 应用程序中动态计算数学表达式,例如计算器或科学计算应用程序。使用 Java Expression Evaluator,您可以轻松地解析和计算包含各种数学函数和运算符的表达式,并在运行时添加自定义函数和变量。 Java Expression Evaluator 支持各种数学函数和运算符,例如加法、减法、乘法、除法、幂运算、三角函数、对数函数等。它还支持自定义函数和变量,您可以在运行时添加自己的函数和变量,并将它们包含在表达式中计算。 Java Expression Evaluator 的使用非常简单。您只需要创建一个 ExpressionEvaluator 对象,然后使用 evaluate() 方法计算表达式。例如: ``` ExpressionEvaluator ee = new ExpressionEvaluator(); double result = ee.evaluate("2 + 3 * 4"); ``` 以上代码将计算表达式 2 + 3 * 4,结果为 14。您也可以添加自定义函数和变量,例如: ``` ExpressionEvaluator ee = new ExpressionEvaluator(); ee.putFunction("square", new Function() { public double apply(double... args) { return args[0] * args[0]; } }); ee.putVariable("x", 5); double result = ee.evaluate("square(x) + 3"); ``` 以上代码将添加一个名为 square 的自定义函数,该函数计算参数的平方。然后,它将添加一个名为 x 的变量,并将其设置为 5。最后,它将计算表达式 square(x) + 3,结果为 28。 Java Expression Evaluator 是一个非常有用的库,可以帮助您轻松地解析和计算数学表达式,并支持自定义函数和变量。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值