??xml version="1.0" encoding="utf-8" standalone="yes"?>
图一Q匹配所?23-12-1234形式的社会安全号?/p>
图二Q匹配所?23-12-1234?23121234形式的社会安全号?/p>
图三Q匹配典型的国汽R牌照LQ如8836KV
囑֛Q匹配所有单词,?#8220;X”开头的除外
图五Q匹配所有Moth DD,YYYY格式的日?/p>
囑օQ匹配所有Month DD,YYYY格式的日期,定义月䆾gؓW一个组 图七Q匹配所?23-12-1234格式的社会安全号?/p>
囑օQ匹配IP地址
图九Q匹配至一个字W,直至扑ֈ“]”
囑֍Q匹配IP地址和时间标?/p>
囑֍一Q匹配FONT标记的所有属?/p>
囑֍二:匚w单个属性,q把它分割成名字-值对
囑֍三:匚w修改前的链接
如果你曾l用qPerl或Q何其他内建正则表辑ּ支持的语aQ你一定知道用正则表达式处理文本和匚w模式是多么简单。如果你不熟悉这个术语,那么“正则表达?#8221;QRegular ExpressionQ就是一个字W构成的Ԍ它定义了一个用来搜索匹配字W串的模式?
许多语言Q包括Perl、PHP、Python、JavaScript和JScriptQ都支持用正则表辑ּ处理文本Q一些文本编辑器用正则表辑ּ实现?
U?#8220;搜烦-替换”功能。那么Java又怎样呢?本文写作Ӟ一个包含了用正则表辑ּq行文本处理的Java规范需求(Specification
RequestQ已l得到认可,你可以期待在JDK的下一版本中看到它?
然而,如果现在需要用正则表辑ּQ又该怎么办呢Q你可以从Apache.org下蝲源代码开攄Jakarta-ORO库。本文接下来的内容先要地介绍正则表达式的入门知识Q然后以Jakarta-ORO APIZ介绍如何使用正则表达式?
一、正则表辑ּ基础知识
我们先从单的开始。假设你要搜索一个包含字W?#8220;cat”的字W串Q搜索用的正则表辑ּ是“cat”。如果搜索对大小写不敏感Q单?#8220;catalog”?#8220;Catherine”?#8220;sophisticated”都可以匹配。也是_
1.1 句点W号
假设你在玩英文拼字游戏,惌扑և三个字母的单词,而且q些单词必须?#8220;t”字母开_?#8220;n”字母l束。另外,假设有一本英文字典,你可以用正则表达?
搜烦它的全部内容。要构造出q个正则表达式,你可以用一个通配W——句点符?#8220;.”。这P完整的表辑ּ是“t.n”Q它匚w“tan”?
“ten”?#8220;tin”?#8220;ton”Q还匚w“t#n”?#8220;tpn”甚至“t
n”Q还有其他许多无意义的组合。这是因为句点符号匹配所有字W,包括I格、Tab字符甚至换行W:
1.2 ҎL?/strong>
Z解决句点W号匚w范围q于q泛q一问题Q你可以在方括号Q?#8220;[]”Q里面指定看来有意义的字W。此Ӟ只有Ҏ号里面指定的字符才参与匹配。也是
_正则表达?#8220;t[aeio]n”只匹?#8220;tan”?#8220;Ten”?#8220;tin”?#8220;ton”。但“Toon”不匹配,因ؓ在方括号之内你只能匹配单个字
W:
1.3 “?#8221;W号
如果除了上面匚w的所有单词之外,你还惌匚w“toon”Q那么,你可以?#8220;|”操作W?#8220;|”操作W的基本意义是“?#8221;q算。要匚w
“toon”Q?#8220;t(a|e|i|o|oo)n”正则表达式。这里不能用方扩号Q因为方括号只允许匹配单个字W;q里必须使用圆括?#8220;()”。圆?
可可以用来分组Q具体请参见后面介绍?
1.4 表示匚wơ数的符?/strong>
表一昄了表C匹配次数的W号Q这些符L来确定紧靠该W号左边的符号出现的ơ数Q?
假设我们要在文本文g中搜索美国的C会安全L。这个号码的格式?99-99-9999。用来匹配它的正则表辑ּ如图一所C。在正则表达式中Q连字符
Q?#8220;-”Q有着Ҏ的意义,它表CZ个范_比如??。因此,匚wC会安全L中的q字W号Ӟ它的前面要加上一个{义字W?#8220;\”?
假设q行搜烦的时候,你希望连字符号可以出玎ͼ也可以不出现——即Q?99-99-9999?99999999都属于正的格式。这Ӟ你可以在q字W号后面加上“Q?#8221;数量限定W号Q如图二所C:
下面我们再来看另外一个例子。美国汽车牌照的一U格式是四个数字加上二个字母。它的正则表辑ּ前面是数字部?#8220;[0-9]{4}”Q再加上字母部分“[A-Z]{2}”。图三显CZ完整的正则表辑ּ?
1.5 “?#8221;W号
“^”W号UCؓ“?#8221;W号。如果用在方括号内,“^”表示不想要匹配的字符。例如,囑֛的正则表辑ּ匚w所有单词,但以“X”字母开头的单词除外?
1.6 圆括号和I白W号
假设要从格式?#8220;June 26, 1951”的生日日期中提取出月份部分,用来匚w该日期的正则表达式可以如图五所C:
新出现的“\s”W号是空白符P匚w所有的I白字符Q包括Tab字符。如果字W串正确匚wQ接下来如何提取出月份部分呢Q只需在月份周围加上一个圆括号创徏一个组Q然后用ORO APIQ本文后面详l讨论)提取出它的倹{修改后的正则表辑ּ如图六所C:
1.7 其它W号
为简便v见,你可以用一些ؓ常见正则表达式创建的快捷W号。如表二所C:
表二Q常用符?
例如Q在前面C会安全L的例子中Q所有出?#8220;[0-9]”的地Ҏ们都可以使用“\d”。修改后的正则表辑ּ如图七所C:
二、Jakarta-ORO?/font>
有许多源代码开攄正则表达式库可供JavaE序员用,而且它们中的许多支持Perl
5兼容的正则表辑ּ语法。我在这里选用的是Jakarta-ORO正则表达式库Q它是最全面的正则表辑ּAPI之一Q而且它与Perl
5正则表达式完全兼宏V另外,它也是优化得最好的API之一?
Jakarta-ORO库以前叫做OROMatcherQDaniel Savarese大方地把它赠送给了Jakarta Project。你可以按照本文最后参考资源的说明下蝲它?
我首先将要介l用Jakarta-ORO库时你必d建和讉K的对象,然后介绍如何使用Jakarta-ORO API?
?PatternCompiler对象
首先Q创Z个Perl5Compilercȝ实例Qƈ把它赋值给PatternCompiler接口对象。Perl5Compiler是PatternCompiler接口的一个实玎ͼ允许你把正则表达式编译成用来匚w的Pattern对象?
?Pattern对象
要把正则表达式编译成Pattern对象Q调用compiler对象的compile()ҎQƈ在调用参C指定正则表达式。例如,你可以按照下面这U方式编译正则表辑ּ“t[aeio]n”Q?
默认情况下,~译器创Z个大写敏感的模式(patternQ。因此,上面代码~译得到的模式只匚w“tin”?#8220;tan”?“ten”?#8220;ton”Q但不匹?#8220;Tin”?#8220;taN”。要创徏一个大写不敏感的模式Q你应该在调用编译器的时候指定一个额外的参数Q?
创徏好Pattern对象之后Q你可以通过PatternMatchercȝ该Pattern对象q行模式匚w?
?PatternMatcher对象
PatternMatcher对象ҎPattern对象和字W串q行匚w查。你要实例化一个Perl5Matchercdƈ把结果赋值给
PatternMatcher接口。Perl5MatchercLPatternMatcher接口的一个实玎ͼ它根据Perl
5正则表达式语法进行模式匹配:
使用PatternMatcher对象Q你可以用多个方法进行匹配操作,q些Ҏ的第一个参数都是需要根据正则表辑ּq行匚w的字W串Q?
· boolean matches(String input, Pattern pattern)Q当输入字符串和正则表达式要_匚w时用。换句话_正则表达式必d整地描述输入字符丌Ӏ?
· boolean matchesPrefix(String input, Pattern pattern)Q当正则表达式匹配输入字W串起始部分时用?
· boolean contains(String input, Pattern pattern)Q当正则表达式要匚w输入字符串的一部分时用(卻I它必L一个子Ԍ?
另外Q在上面三个Ҏ调用中,你还可以用PatternMatcherInput对象作ؓ参数替代String对象Q这Ӟ你可以从字符串中最后一ơ匹?
的位|开始l进行匹配。当字符串可能有多个子串匚wl定的正则表辑ּӞ用PatternMatcherInput对象作ؓ参数很有用了。用
PatternMatcherInput对象作ؓ参数替代StringӞ上述三个Ҏ的语法如下:
· boolean matches(PatternMatcherInput input, Pattern pattern)
· boolean matchesPrefix(PatternMatcherInput input, Pattern pattern)
· boolean contains(PatternMatcherInput input, Pattern pattern)
三、应用实?/font>
下面我们来看看Jakarta-ORO库的一些应用实例?
3.1 日志文g处理
dQ分析一个Web服务器日志文Ӟ定每一个用戯在网站上的时间。在典型的BEA WebLogic日志文g中,日志记录的格式如下:
分析q个日志记录Q可以发玎ͼ要从q个日志文g提取的内Ҏ两项QIP地址和页面访问时间。你可以用分l符P圆括P从日志记录提取出IP地址和时间标记?
首先我们来看看IP地址。IP地址?个字节构成,每一个字节的值在0?55之间Q各个字节通过一个句点分隔。因此,IP地址中的每一个字节有臛_一个、最多三个数字。图八显CZ为IP地址~写的正则表辑ּQ?
IP地址中的句点字符必须q行转义处理Q前面加?#8220;\”Q,因ؓIP地址中的句点h它本来的含义Q而不是采用正则表辑ּ语法中的Ҏ含义。句点在正则表达式中的特D含义本文前面已l介l?
日志记录的时间部分由一Ҏ括号包围。你可以按照如下思\提取出方括号里面的所有内容:首先搜烦起始Ҏ号字W(“[”Q,提取出所有不过l束Ҏ号字W(“]”Q的内容Q向前寻扄x到结束方括号字符。图九显CZq部分的正则表达式?
现在Q把上述两个正则表达式加上分l符P圆括P后合q成单个表达式,q样可以从日志记录提取出IP地址和时间。注意,Z匚w“- -”Q但不提取它Q,正则表达式中间加入了“\s-\s-\s”。完整的正则表达式如囑֍所C?
现在正则表达式已l编写完毕,接下来可以编写用正则表辑ּ库的Java代码了?
Z用Jakarta-ORO库,首先创徏正则表达式字W串和待分析的日志记录字W串Q?
q里使用的正则表辑ּ与图十的正则表达式差不多完全相同Q但有一点例外:在Java中,你必d每一个向前的斜杠Q?#8220;\”Q进行{义处理。图十不?
Java的表CŞ式,所以我们要在每?#8220;\”前面加上一?#8220;\”以免出现~译错误。遗憄是,转义处理q程很容易出现错误,所以应该小心}慎。你可以首先
输入未经转义处理的正则表辑ּQ然后从左到右依ơ把每一?#8220;\”替换?#8220;\\”。如果要复检Q你可以试着把它输出到屏q上?
初始化字W串之后Q实例化PatternCompiler对象Q用PatternCompiler~译正则表达式创Z个Pattern对象Q?
现在Q创建PatternMatcher对象Q调用PatternMatcher接口的contain()Ҏ查匹配情况:
接下来,利用PatternMatcher接口q回的MatchResult对象Q输出匹配的l。由于logEntry字符串包含匹配的内容Q你可以看到cd下面的输出:
3.2 HTML处理实例一
下面一个Q务是分析HTML面内FONT标记的所有属性。HTML面内典型的FONT标记如下所C:
E序按照如下Ş式,输出每一个FONT标记的属性:
在这U情况下Q我你用两个正则表辑ּ。第一个如囑֍一所C,它从字体标记提取?#8220;"face="Arial, Serif" size="+2" color="red"”?
W二个正则表辑ּ如图十二所C,它把各个属性分割成名字-值对?
分割l果为:
现在我们来看看完成这个Q务的Java代码。首先创Z个正则表辑ּ字符Ԍ用Perl5Compiler把它们编译成Pattern对象。编译正则表?
式的时候,指定Perl5Compiler.CASE_INSENSITIVE_MASK选项Q得匹配操作不区分大小写?
接下来,创徏一个执行匹配操作的Perl5Matcher对象?
假设有一个Stringcd的变量htmlQ它代表了HTML文g中的一行内宏V如果html字符串包含FONT标记Q匹配器返回true。此Ӟ你可以用匚w器对象返回的MatchResult对象获得W一个组Q它包含了FONT的所有属性:
接下来创Z个PatternMatcherInput对象。这个对象允怽从最后一ơ匹配的位置开始l进行匹配操作,因此Q它很适合于提取FONT?
记内属性的名字-值对。创建PatternMatcherInput对象Q以参数形式传入待匹配的字符丌Ӏ然后,用匹配器实例提取出每一个FONT的属
性。这通过指定PatternMatcherInput对象Q而不是字W串对象Qؓ参数Q反复地调用PatternMatcher对象的contains
()Ҏ完成。PatternMatcherInput对象之中的每一ơP代将把它内部的指针向前移动,下一ơ检将从前一ơ匹配位|的后面开始?
本例的输出结果如下:
3.3 HTML处理实例?/strong>
下面我们来看看另一个处理HTML的例子。这一ơ,我们假定Web服务器从widgets.acme.comUd了newserver.acme.com。现在你要修改一些页面中的链接:
执行q个搜烦的正则表辑ּ如图十三所C:
如果能够匚wq个正则表达式,你可以用下面的内Ҏ换图十三的链接:
注意#字符的后面加上了$1。Perl正则表达式语法用$1?2{表C已l匹配且提取出来的组。图十三的表辑ּ把所有作Z个组匚w和提取出来的内容附加到链接的后面?
现在Q返回Java。就象前面我们所做的那样Q你必须创徏试字符Ԍ创徏把正则表辑ּ~译到Pattern对象所必需的对象,以及创徏一个PatternMatcher对象Q?img src="http://www.ccw.com.cn/htm/app/aprog/01_7_31_4_v.jpg" border="0">
接下来,用com.oroinc.text.regex包Utilcȝsubstitute()静态方法进行替换,输出l果字符Ԍ
Util.substitute()Ҏ的语法如下:
【结束语?/strong>?
q篇文章中,我ؓ你介l了正则表达式的强大功能。只要正运用,正则表达式能够在字符串提取和文本修改中v到很大的作用。另外,我还介绍了如何在Java
E序中通过Jakarta-ORO库利用正则表辑ּ。至于最l采用老式的字W串处理方式Q用StringTokenizerQcharAtQ和
substringQ,q是采用正则表达式,q就有待你自己决定了?img src ="http://www.tkk7.com/kuxiaoku/aggbug/131668.html" width = "1" height = "1" />
q个调用的前两个参数是以前创建的PatternMatcher和Pattern对象。第三个参数是一个Substiution对象Q它军_了替换操作如
何进行。本例用的是Perl5Substitution对象Q它能够q行Perl5风格的替换。第四个参数是想要进行替换操作的字符Ԍ最后一个参数允
许指定是否替换模式的所有匹配子ԌUtil.SUBSTITUTE_ALLQ,或只替换指定的次数?
]]>
包括Q?br> “>> 右移”Q?#8220;<< 左移”Q?#8220;>>> 无符号右U?#8221;
例子Q?br>-5>>3=-1
1111 1111 1111 1111 1111 1111 1111 1011
1111 1111 1111 1111 1111 1111 1111 1111
其结果与 Math.floor((double)-5/(2*2*2)) 完全相同?/p>
-5<<3=-40
1111 1111 1111 1111 1111 1111 1111 1011
1111 1111 1111 1111 1111 1111 1101 1000
其结果与 -5*2*2*2 完全相同?/p>
5>>3=0
0000 0000 0000 0000 0000 0000 0000 0101
0000 0000 0000 0000 0000 0000 0000 0000
其结果与 5/(2*2*2) 完全相同?/p>
5<<3=40
0000 0000 0000 0000 0000 0000 0000 0101
0000 0000 0000 0000 0000 0000 0010 1000
其结果与 5*2*2*2 完全相同?/p>
-5>>>3=536870911
1111 1111 1111 1111 1111 1111 1111 1011
0001 1111 1111 1111 1111 1111 1111 1111
无论正数、负敎ͼ它们的右UR左UR无W号右移 32 位都是其本nQ比?-5<<32=-5?5>>32=-5?5>>>32=-5?br>一个有的现象是,?1 左移 31 位再右移 31 位,其结果ؓ -1?br>0000 0000 0000 0000 0000 0000 0000 0001
1000 0000 0000 0000 0000 0000 0000 0000
1111 1111 1111 1111 1111 1111 1111 1111
位逻辑q算W?/strong>
包括Q?br> & 与;| 或;~ 非(也叫做求反)Q^ 异或
“& ?#8221;?#8220;| ?#8221;?#8220;~ ?#8221;是基本逻辑q算Q由此可以演变出“与非”?#8220;或非”?#8220;与或?#8221;复合逻辑q算?#8220;^ 异或”是一U特D的逻辑q算Q对它求反可以得?#8220;同或”Q所?#8220;同或”逻辑也叫“异或?#8221;逻辑?/p>
例子Q?br>5&3=1
0000 0000 0000 0000 0000 0000 0000 0101
0000 0000 0000 0000 0000 0000 0000 0011
0000 0000 0000 0000 0000 0000 0000 0001
-5&3=1
1111 1111 1111 1111 1111 1111 1111 1011
0000 0000 0000 0000 0000 0000 0000 0011
0000 0000 0000 0000 0000 0000 0000 0011
5|3=7
0000 0000 0000 0000 0000 0000 0000 0101
0000 0000 0000 0000 0000 0000 0000 0011
0000 0000 0000 0000 0000 0000 0000 0111
-5|3=-5
1111 1111 1111 1111 1111 1111 1111 1011
0000 0000 0000 0000 0000 0000 0000 0011
1111 1111 1111 1111 1111 1111 1111 1011
~5=-6
0000 0000 0000 0000 0000 0000 0000 0101
1111 1111 1111 1111 1111 1111 1111 1010
~-5=4
1111 1111 1111 1111 1111 1111 1111 1011
0000 0000 0000 0000 0000 0000 0000 0100
5^3=6
0000 0000 0000 0000 0000 0000 0000 0101
0000 0000 0000 0000 0000 0000 0000 0011
0000 0000 0000 0000 0000 0000 0000 0110
![]() |
Q?xml version="1.0"?Q?br>Qproject name="Hello world" default="doc"Q?/font> Q?-- properies --Q?br>Qproperty name="src.dir" value="src" /Q?br>Qproperty name="report.dir" value="report" /Q?br>Qproperty name="classes.dir" value="classes" /Q?br>Qproperty name="lib.dir" value="lib" /Q?br>Qproperty name="dist.dir" value="dist" /Q?br>Qproperty name="doc.dir" value="doc"/Q?/font> Q?-- 定义classpath --Q?br>Qpath id="master-classpath"Q?br>Qfileset file="${lib.dir}/*.jar" /Q?br>Qpathelement path="${classes.dir}"/Q?br>Q?pathQ?/font> Q?-- 初始化Q?--Q?br>Qtarget name="init"Q?br>Q?targetQ?/font> Q?-- ~译 --Q?br>Qtarget name="compile" depends="init" description="compile the source files"Q?br>Qmkdir dir="${classes.dir}"/Q?br>Qjavac srcdir="${src.dir}" destdir="${classes.dir}" target="1.4"Q?br>Qclasspath refid="master-classpath"/Q?br>Q?javacQ?br>Q?targetQ?/font> Q?-- 试 --Q?br>Qtarget name="test" depends="compile" description="run junit test"Q?br>Qmkdir dir="${report.dir}"/Q?br>Qjunit printsummary="on" haltonfailure="false" failureproperty="tests.failed" showoutput="true"Q?br>Qclasspath refid="master-classpath" /Q?br>Qformatter type="plain"/Q?br>Qbatchtest todir="${report.dir}"Q?br>Qfileset dir="${classes.dir}"Q?br>Qinclude name="**/*Test.*"/Q?br>Q?filesetQ?br>Q?batchtestQ?br>Q?junitQ?br>Qfail if="tests.failed"Q?br>*********************************************************** **** One or more tests failed! Check the output ... **** *********************************************************** Q?failQ?br>Q?targetQ?/font> Q?-- 打包成jar --Q?br>Qtarget name="pack" depends="test" description="make .jar file"Q?br>Qmkdir dir="${dist.dir}" /Q?br>Qjar destfile="${dist.dir}/hello.jar" basedir="${classes.dir}"Q?br>Qexclude name="**/*Test.*" /Q?br>Qexclude name="**/Test*.*" /Q?br>Q?jarQ?br>Q?targetQ?/font> Q?-- 输出api文档 --Q?br>Qtarget name="doc" depends="pack" description="create api doc"Q?br>Qmkdir dir="${doc.dir}" /Q?br>Qjavadoc destdir="${doc.dir}" author="true" version="true" use="true" windowtitle="Test API"Q?br>Qpackageset dir="${src.dir}" defaultexcludes="yes"Q?br>Qinclude name="example/**" /Q?br>Q?packagesetQ?br>QdoctitleQ<![CDATA[Qh1QHello, testQ?h1Q]]Q</doctitleQ?br>QbottomQ<![CDATA[QiQAll Rights Reserved.Q?iQ]]Q</bottomQ?br>Qtag name="todo" scope="all" description="To do:" /Q?br>Q?javadocQ?br>Q?targetQ?br>Q?projectQ?/font> |
![]() |
![]() |
![]() |
Buildfile: F:\eclipse-projects\Hello\build.xml init: compile: [mkdir] Created dir: F:\eclipse-projects\Hello\classes [javac] Compiling 2 source files to F:\eclipse-projects\Hello\classes test: [mkdir] Created dir: F:\eclipse-projects\Hello\report [junit] Running example.HelloTest [junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0.02 sec pack: [mkdir] Created dir: F:\eclipse-projects\Hello\dist [jar] Building jar: F:\eclipse-projects\Hello\dist\hello.jar doc: [mkdir] Created dir: F:\eclipse-projects\Hello\doc [javadoc] Generating Javadoc [javadoc] Javadoc execution [javadoc] Loading source files for package example... [javadoc] Constructing Javadoc information... [javadoc] Standard Doclet version 1.4.2_04 [javadoc] Building tree for all the packages and classes... [javadoc] Building index for all the packages and classes... [javadoc] Building index for all classes... [javadoc] Generating F:\eclipse-projects\Hello\doc\stylesheet.css... [javadoc] Note: Custom tags that could override future standard tags: @todo. To avoid potential overrides, use at least one period character (.) in custom tag names. [javadoc] Note: Custom tags that were not seen: @todo BUILD SUCCESSFUL Total time: 11 seconds |
以前对于ant的用L避而不触,M为有炚w度,最q一直看我们目中的build.xml忽而觉得非常简单,也模仿着在我?a >www.javaedu.com开发上使用了一下,非常不错Q以下是我的打包文gbuild.xmlQ?br>
<?xml version="1.0"?>
<project name="payManager" default="all" basedir=".">
<property name="src.dir" value="JavaSource" />
<property name="classes.dir" value="WebContent/WEB-INF/classes" />
<property name="lib.dir" value="WebContent/WEB-INF/lib" />
<property name="WebContent.dir" value="WebContent" />
<!--用于输出的时文件夹与打包的文g?->
<property name="temp.dir" value="D:\temp" />
<property name="war.dir" value="D:\war" />
<!--对于不同环境的war包,使用不同的配|文?->
<property name="生.dir" value="bak\生" />
<property name="开?dir" value="bak\开? />
<!-- 定义classpath -->
<path id="master-classpath">
<fileset file="${lib.dir}/*.jar" />
<pathelement path="${classes.dir}" />
</path>
<target name="clean">
<delete dir="${temp.dir}" />
<delete dir="${classes.dir}" />
<delete file="${war.dir}/开?ROOT.war"/>
<delete file="${war.dir}/生/ROOT.war"/>
</target>
<!-- 初始化Q?-->
<target name="init" depends="clean">
</target>
<!-- ~译 -->
<target name="compile" depends="init" description="compile the source files">
<mkdir dir="${temp.dir}/生" />
<mkdir dir="${temp.dir}/开? />
<mkdir dir="${classes.dir}" />
<javac srcdir="${src.dir}" destdir="${classes.dir}" debug="true" encoding="GB2312">
<classpath refid="master-classpath" />
</javac>
<copy todir="${temp.dir}/生" overwrite="true">
<fileset dir="${WebContent.dir}">
<include name="**/**" />
</fileset>
</copy>
<copy todir="${temp.dir}/生/WEB-INF/classes" overwrite="true">
<fileset dir="${生.dir}">
<include name="jlo_logging.xml" />
<include name="log4j.properties" />
<include name="mvncore.xml" />
<include name="mvnForum_i18n_zh_CN.properties" />
<include name="mvnforum.xml" />
<include name="whirlycache.xml" />
</fileset>
</copy>
<copy todir="${temp.dir}/开? overwrite="true">
<fileset dir="${WebContent.dir}">
<include name="**/**" />
</fileset>
</copy>
<copy todir="${temp.dir}/开?WEB-INF/classes" overwrite="true">
<fileset dir="${开?dir}">
<include name="jlo_logging.xml" />
<include name="log4j.properties" />
<include name="mvncore.xml" />
<include name="mvnForum_i18n_zh_CN.properties" />
<include name="mvnforum.xml" />
<include name="whirlycache.xml" />
</fileset>
</copy>
</target>
<!--打包-->
<target name="war" depends="compile">
<mkdir dir="${war.dir}/生" />
<mkdir dir="${war.dir}/开? />
<jar jarfile="${war.dir}/生/ROOT.war">
<fileset dir="${temp.dir}/生">
<include name="**/*" />
</fileset>
</jar>
<jar jarfile="${war.dir}/开?ROOT.war">
<fileset dir="${temp.dir}/开?>
<include name="**/*" />
</fileset>
</jar>
<delete dir="${temp.dir}"/>
</target>
<target name="all" depends="war">
<delete dir="${classes.dir}" />
<delete dir="${temp.dir}" />
</target>
</project>
值得注意的是Q在书写build.xml的时候一定要心Q别写错了,我就是因为粗心,在写下面代码的时候:
<fileset dir="${生.dir}">
.<include name="jlo_logging.xml" />
<include name="log4j.properties" />
<include name="mvncore.xml" />
<include name="mvnForum_i18n_zh_CN.properties" />
<include name="mvnforum.xml" />
<include name="whirlycache.xml" />
</fileset>
不小心在U色代码前加了一?#8220;.”Q结果报了一?font color="#ff0033">type doesn't support nested text data. 的错Q我找了半天没找出原因,到网上搜了一下竟然没有找到答案,后来才发现自己写错了Q大汗,Ҏ后,搞定Q爽啊。是?/p>
内容摘要Q?br> ant是一个基于JAVA的自动化脚本引擎Q脚本格式ؓXML。除了做JAVA~译相关d外,ANTq可以通过插g实现很多应用的调用?br>
当一个代码项目大了以后,每次重新~译Q打包,试{都会变得非常复杂而且重复Q因此c语言中有make脚本来帮助这些工作的扚w完成。在Java 中应用是q_无关性的Q当然不会用q_相关的make脚本来完成这些批处理d了,ANT本n是q样一个流E脚本引擎,用于自动化调用程序完成项目的~? 译,打包Q测试等。除了基于JAVA是^台无关的外,脚本的格式是ZXML的,比make脚本来说q要好维护一些?br>
每个ant脚本Q缺省叫build.xmlQ中讄了一pdd(target)Q比如对于一个一般的目可能需要有以下d?/p>
出处QJDON.COM
Java的事件模式是动态响应系l重要的基础Q在囑Ş界面领域的事件模式已l有很多文章介绍Q但是在服务器端我们会碰到更多的事g模式Q这里本图ȝ一下:
事g直接驱动模式
事g模式的第一个要求就是性能的要求,需要直接而且快,Command模式是必ȝ怋用的Q主要适合于迅速处?前台的命令,Command模式往往是系l架构的重要部分Q也是流E控制的主要模式?
Command模式l常Java的Reflect一起用,因ؓpȝ的事件处理系l是处于动态变化的Q随着功能要求扩展Q就可能有动态变化事? 处理响应pȝQ以Struts中actionZQ我们知道,Structs的一个主要配|文件是struts-config.xml 如下Q?
Qstruts-configQ?br> Qaction-mappingsQ?br> Qaction path="/login" type="com.javapro.struts.LoginAction"/Q?br> Qaction path="/logout" type="com.javapro.struts.LogoutAction"/Q?br> Q?action-mappingsQ?br>Q?struts-configQ? |
它实际是个command和event的映关p,通过q个配置文gQ运行时动态装载相应的ActionQ完成Command模式Q?我们查LoginAction代码Q就可以看出Command模式的基本特征:
public final class LoginAction extends Action { public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { ................. } } |
很明显,典型的Command模式需要有一个接?接口中有一个统一的方法,q里l一的方法就是execute;
比如我们有个实时pȝQ客h向服务器发出不同~码代号Q意味着不同的请求,不同的请求有不同的Handlerq行 处理QHandler接口?
public class Handler{ public byte[] handleRequest(); } |
不同性质的处理过E承这个Handler接口Q如负责q入pȝ的处理过E?
public class EnterHandler implements Handler{ public byte[] handleRequest(){ } |
调用Handler时是:
//从cache中获取这个requestId对应的Handler Handler handler = (Handler)cache.get(new Integer(reqId)); //调用handler的统一ҎhandleRequest() byte[] outInf = handler.handleRequest(); |
以上是常用的一个事仉动模式。它的特Ҏ靠一个事件直接启动对应的事g处理器?
Chain of
Responsibility职责链模式也应该属于q类Q当事g到达后,让这个事件在我们提供的一批处理器中逐个挑选适合的处理器q行处理Q这个模式缺?
是显然的Q性能丧失在逐个挑?上,一般不推荐使用Q这个模式适合在我们无法预知发生的事g内容时用,因ؓ不知道发生事件的具体情况Q?
我们无法在E序q行前事先ؓ其指z应的处理器,只能靠运行时Q事件自己去摸烦“撞运?#8221;?
监控式事件模?/strong> 应用客户端有三种与观察者交互的方式Q?.直接融合 2.推方?3.拉方式? 直接融合是说应用客L自己是观察者,两者融合,q样无疑应用客户端获得的触发旉是最快的? 推方式就是观察者一旦侦到事g发生Q立卛_事gPush推到应用客户端;拉方式类似收取邮Ӟ应用客户端在需要时才从观察者拉取事件? JDK 1.4的None Blocking I/O是监控式事g模式的典型实玎ͼSelector昄是一个监控I/O的第三者,当有外部事gq来Q通过 调用Slector.selectҎ可以获得外部事gQ从而进行处理,可参考我的本栏文章? 监控式事件模式适合使用在触发性质的场合,比如数据库后端触发器 界面触发 I/O触发 状态改变触发等? 我们以一个信件触发ؓ例,q其实是个Observer应用例子: 比如用户提请服务器计一个数据,如果用户同时要求计结果向自己信箱发一,那么我们看如何设计?按照通常思维Q这是一个次序问题,先在?
存中计算数据Q然后将l果发送到他的信箱Q最后返回结果到用户端,我们知道信g的发送是耗时的,因此Q有可能|络的原因造成信g发送很慢,q是用户׃?
{不C的计结果,很显Ӟ我们使用监控式事件模式来解决Q让发信的事件由监控者去完成Q只要需要时触发可以了Q? public class Computer extends Observable{ public Computer(){ ....... //计算操作 if (needEmail){ //讄变化? } ?/p>
我们再来看监控观察者代码是如何写的Q? public class sendMailObserver implements Observer{ public void update(Observable obj,Object email){ if (email instanceof String){ sendMail(email); } } } q样服务器在执行computeҎӞ没有发送邮件的{待Q一直接l箋执行? 既然事g处理模式是众多应用系l的基本模式Q那么应该可以Ş成一个框架标准,JMX的notification Model是q样一个架构设计? JMX Notification Model JMX Notification
Model允许MBean通过调用notificationsq播事gQ接受者只要注册ؓ一个listernerQ?JMX?MBean
notification model 会Ȁz这个listerner注册一ơ,然后一直接受到 来自q播者发出的各种事g? 事g模式有三个角ԌW一个是事g发出者producer 然后是事件接受者Consumer,W三?是要传输的事件。JMX notification model也是q样分别依靠下列lg来实现这三个角色Q? A. NotificationBroadcaster接口, 事gq播发出者, q个接口允许监听者在需要发出的notification中注册他们感兴趣的事件? 只要是MBeanQ就既可以成为notification的发布广播者,也可以成为notification的监听者接受者,或者同时两者都可以? Attribute Change Notifications 在JMX架构中,MBean能够在属性attribute变化发生Ӟ发出通知Q关于诊断属性变化的机制以及触发 通知事gq不属于JMX规定部分Q每个MBean可以有自q立的实现方式? Timer Service Monitoring 当derived gauge值满一pd条gӞ每个monitor server会发出一个特定类型的通知?q些条g都是在monitor被初始化时设定的Q也可以通过monitor MBean的管理接口动态设定? ҎMBean内部属性值类型有三种monitor: A.CounterMonitor - 使用Java的整数类型来观察属性,有一个行为特征: B.GaugeMonitor - 使用java的整数或点cd观察属性。象gauge(量仪器) 要么上升 要么下降减少? C StringMonitor - 使用Stringcd观察属? 事g处理架构 JMS可实现同步或异步的事件触发机Ӟ分别是通过Poin to
Point(拉方?和Pubilsh/Subscibe(推方?具体完成Q在分布式计环境中Q异步机制是非常重要Q可以v到解耦作用,因ؓ分布环境
中单炚w误或通讯问题是经常发生的Q整个分布式pȝ不能L依靠同步机制来可靠地传递事件或notificationQ? 由此可见Q事件处理模式从Java诸多架构到我们具体应用程序,随处可见Q根据不同的应用需求选择不同的事件处理模式,才能真正挖掘Java的潜在性能?
监控式事件模式就不同于事件直接驱动模式,它是借助W三者来监控和触发事Ӟq类事g的特ҎQ?有一个观察者置w事外在定期独立q行着Q我们将我们要监听的事g向这个观察者注册,q样观察者就 代替我们来监听这个事Ӟ应用客户端通过观察者来获得事g状况?
//sendMailObserver讑֮为本cȝ观察者?
addObserver(new sendMailObserver());
}
public void compute(String input,boolean needEmail,String email){
.........
setChanged();
//如果需要发送emailQ我们把email地址作ؓ参数传送过?
notifyObservers(email);
}
监控式事件模式和事g直接驱动模式可以在一个系l一起用,外界信号通过事g直接驱动模式启动pȝ处理模块Q?pȝ处理模块处理q程中,可以通过监控式事件模式来触发其它后台d。这样一个架构非帔R合实时处理pȝ?
我们知道QJMX是提供了一U对MBean资源执行控制和配
|的理机制Q但q只是复杂的Q分布式的系l中的一部分Q?q有需要资源能够感应状态改变或者特定事件变化的机制Q这是JMX
Notification Model?在JMX Notification
Model中均可以实现"事g直接驱动模式"?监控式事件模?Q这取决于你的应用需求?
B. 通用事g(Notification)Q这是我们要传输的事件?Notification事g能被直接使用Q也能成为子c,q些都依赖于随同事g传输的信息?通过使用通用事gcdQ监听者将能接受来自广播者所有类型的事g?
C. NotificationListener接口, 事g监听者或者接受者, 用于接受来自q播者的Mnotification信号?
D. NotificationFilter接口, q个接口为notification的监听者提供一个对发出事g的过滤器?
E. NotificationEmitter 接口, 扩展了NotificationBroadcaster接口Q当删除监听者时允许更多的控制功能?
Attribute Change Notifications是一U特D的notification, M时候MBean属性attribute 被修改,外界能够被通知到?
Timer Service触发器是在规定的日期和事件发出通知Q它能够一一个恒定的间隙不断重复发出通知Q?通知可以发往所有注册ؓ接受timer通知的对象,Timer Service也是一个可理的MBeanQ允许应用系l设|?一个可配置的调度程度?
通过使用monitoring
serviceQ一个或多个MBean属性将按规定间隔时间被监视Q?
对于被观察的Mbean,监视器monitor从它上面获得一个|叫derived gaugeQ这个derived gauge可以?
被观察属性的原|也可以是一个数字性属性连l被观察g差?
a. L大于或等于零.
b. 能自?
c. 能回?
JMS是基于Socket的一U消息处理框Ӟ原理cM于监控式事g模式Q但是JMS已经把这U模式上升到架构的高度。不同JVM间也依靠JMS消息可以实现事gpȝQ注意是pȝQ不单是一个小事g了)的触发和ȀzR?
从上面JMS的架构图可以看出事g三个角色Producer和Consumer以及事g信息本nMessageQJMS是在Producer和Consumer之间建立一个连接ConnectionQ?
服务器的异常处理包括的内定w常广泛,本文仅就在网l封包方面出现的异常作一讨论Q希望能Ҏ从事相关工作的朋友有所帮助?/p>
关于|络包斚w的异常,M来说Q可以分Z大类Q一是封包格式出现异常;二是包内容Q即包数据Q出现异常。在包格式的异常处理方面,我们在最 底端的网l数据包接收模块便可以加以处理。而对于封包数据内容出现的异常Q只有依靠游戏本w的逻辑d以判定和验。游戏逻辑斚w的异常处理,是随每个? 戏的不同而不同的Q所以,本文随后的内容将重点阐述在网l数据包接收模块中的异常处理?/p>
为方便以下的讨论Q先明确两个概念Q这两个概念是ؓ了叙q方面,W者自行取的,q无标准可言Q:
1、逻辑包:指的是在应用层提交的数据包,一个完整的逻辑包可以表CZ个确切的逻辑意义。比如登录包Q它里面可以含有用户名字段和密码字Dc尽它看上M是一D늼冲区数据Q但q个~冲区里的各个区间是代表一定的逻辑意义的?br> 2、物理包Q指的是使用recv(recvfrom)或wsarecv(wsarecvfrom)从网l底层接收到的数据包Q这h到的一个数据包Q能不能表示一个完整的逻辑意义Q要取决于它是通过UDPcȝ“数据报协?#8221;发的包还是通过TCPcȝ“协?#8221;发的包?/p>
我们知道QTCP是流协议Q?#8220;协?#8221;?#8220;数据报协?#8221;的不同点在于Q?#8220;数据报协?#8221;中的一个网l包本n是一个完整的逻辑包,也就是说Q在应用层? sendto发送了一个逻辑包之后,在接收端通过recvfrom接收到的是刚才使用sendto发送的那个逻辑包,q个包不会被分开发送,也不会与? 它的包放在一起发送。但对于TCP而言QTCP会根据网l状况和neagle法Q或者将一个逻辑包单独发送,或者将一个逻辑包分成若q次发送,或者会? 若干个逻辑包合在一起发送出厅R正因ؓTCP在逻辑包处理方面的q种_合性,要求我们在作ZTCP的应用时Q一般都要编写相应的拼包、解包代码?/p>
因此Q基于TCP的上层应用,一般都要定义自q包格式。TCP的封包定义中Q除了具体的数据内容所代表的逻辑意义之外Q第一步就是要定以何U方式表C当前包的开始和l束。通常情况下,表示一个TCP逻辑包的开始和l束有两U方式:
1、以Ҏ的开始和l束标志表示Q比如FF00表示开始,00FF表示l束?br> 2、直接以包长度来表示。比如可以用W一个字节表C包总长度,如果觉得q样的话包比较小Q也可以用两个字节表C包长度?/p>
下面要l出的代码是以第2U方式定义的数据包,包长度以每个包的前两个字节表示。我结合着代码l出相关的解释和说明?/p>
函数中用到的变量说明Q?/p>
CLIENT_BUFFER_SIZEQ缓冲区的长度,定义为:Const int CLIENT_BUFFER_SIZE=4096?br> m_ClientDataBufQ数据整理缓冲区Q每ơ收到的数据Q都会先被复制到q个~冲区的末尾Q然后由下面的整理函数对q个~冲行整理。它的定义是Qchar m_ClientDataBuf[2* CLIENT_BUFFER_SIZE]?br> m_DataBufByteCountQ数据整理缓冲区中当前剩余的未整理字节数?br> GetPacketLen(const char*)Q函敎ͼ可以Ҏ传入的缓冲区首址按照应用层协议取出当前逻辑包的长度?br> GetGamePacket(const char*, int)Q函敎ͼ可以Ҏ传入的缓冲区生成相应的游戏逻辑数据包?br> AddToExeList(PBaseGamePacket)Q函敎ͼ指定的游戏逻辑数据包加入待处理的游戏逻辑数据包队列中Q等待逻辑处理U程对其q行处理?br> DATA_POSQ指的是除了包长度、包cd{这些标志型字段之外Q真正的数据包内容的起始位置?/p>
Bool SplitFun(const char* pData,const int &len)
{
PBaseGamePacket pGamePacket=NULL;
__int64 startPos=0, prePos=0, i=0;
int packetLen=0;
//先将本次收到的数据复制到整理~冲区尾?br> startPos = m_DataBufByteCount;
memcpy( m_ClientDataBuf+startPos, pData, len );
m_DataBufByteCount += len;
//当整理缓冲区内的字节数少于DATA_POS字节Ӟ取不到长度信息则退?br> //注意Q退出时q不|m_DataBufByteCount?
if (m_DataBufByteCount < DATA_POS+1)
return false;
//Ҏ正常逻辑Q下面的情况不可能出玎ͼ为稳妥v见,q是加上
if (m_DataBufByteCount > 2*CLIENT_BUFFER_SIZE)
{
//讄m_DataBufByteCount?Q意味着丢弃~冲Z的现有数?br> m_DataBufByteCount = 0;
//可以考虑开N误格式数据包的处理接口,处理逻辑交给上层
//OnPacketError()
return false;
}
//q原起始指针
startPos = 0;
//只有当m_ClientDataBuf中的字节个数大于最包长度时才能执行此语句
packetLen = GetPacketLen( pIOCPClient->m_ClientDataBuf );
//当逻辑层的包长度不合法Ӟ则直接丢弃该?br> if ((packetLen < DATA_POS+1) || (packetLen > 2*CLIENT_BUFFER_SIZE))
{
m_DataBufByteCount = 0;
//OnPacketError()
return false;
}
//保留整理~冲区的末尾指针
__int64 oldlen = m_DataBufByteCount;
while ((packetLen <= m_DataBufByteCount) && (m_DataBufByteCount>0))
{
//调用拼包逻辑Q获取该~冲区数据对应的数据?br> pGamePacket = GetGamePacket(m_ClientDataBuf+startPos, packetLen);
if (pGamePacket!=NULL)
{
//数据包加入执行队列
AddToExeList(pGamePacket);
}
pGamePacket = NULL;
//整理~冲区的剩余字节数和新逻辑包的起始位置q行调整
m_DataBufByteCount -= packetLen;
startPos += packetLen;
//D留~冲区的字节数少于一个正常包大小Ӟ只向前复制该包随后退?br> if (m_DataBufByteCount < DATA_POS+1)
{
for(i=startPos; i<startPos+m_DataBufByteCount; ++i)
m_ClientDataBuf[i-startPos] = m_ClientDataBuf[i];
return true;
}
packetLen = GetPacketLen(m_ClientDataBuf + startPos );
//当逻辑层的包长度不合法Ӟ丢弃该包及缓冲区以后的包
if ((packetLen<DATA_POS+1) || (packetLen>2*CLIENT_BUFFER_SIZE))
{
m_DataBufByteCount = 0;
//OnPacketError()
return false;
}
if (startPos+packetLen>=oldlen)
{
for(i=startPos; i<startPos+m_DataBufByteCount; ++i)
m_ClientDataBuf[i-startPos] = m_ClientDataBuf[i];
return true;
}
}//取所有完整的?/p>
return true;
}
以上便是数据接收模块的处理函敎ͼ下面是几点简要说明:
1、用于拼包整理的~冲?m_ClientDataBuf)应该比recv中指定的接收~冲?pData)长度(CLIENT_BUFFER_SIZE)要大Q通常前者是后者的2?2*CLIENT_BUFFER_SIZE)或更大?br>
2、ؓ避免因ؓ剩余数据前移而导致的额外开销Q徏议m_ClientDataBuf使用环Ş~冲区实现?br>
3、ؓ了避免出现无法拼装的包,我们U定每次发送的逻辑包,其单个逻辑包最大长度不可以过CLIENT_BUFFER_SIZE?倍。因为我们的?
理缓冲区只有2*CLIENT_BUFFER_SIZEq么长,更长的数据,我们无法整理。这p求在协议的设计上以及最l的发送函数的处理上要加上q?
L异常处理机制?/p>
4、对于数据包q短或过长的包,我们通常的情冉||m_DataBufByteCount?Q即舍弃当前
包的处理。如果此处不讄m_DataBufByteCount?也可Q但该客L只要发了一ơ格式错误的包,则其后发过来的包则也将q带着产生格式
错误Q如果设|m_DataBufByteCount?Q则可以比较好的避免后的包受此包的格式错误影响。更好的作法是,在此处开放一个封包格式异?
的处理接?OnPacketError)Q由上层逻辑军_对这U异常如何处|。比如上层逻辑可以对封包格式方面出现的异常q行计数Q如果错误的ơ数过
一定的|则可以断开该客L的连接?/p>
5、徏议不要在recv或wsarecv的函数后Q就紧接着作以上的处理。当recv收到一D|
据后Q生成一个结构体或对?它主要含有data和len两个内容Q前者是数据~冲区,后者是数据长度)Q将q样的一个结构体或对象放C个队列中由后?
的线E对其用SplitFun函数q行整理。这P可以最大限度地提高|络数据的接攉度Q不臛_为数据整理的原因而在此处费旉?img src ="http://www.tkk7.com/kuxiaoku/aggbug/109154.html" width = "1" height = "1" />