??xml version="1.0" encoding="utf-8" standalone="yes"?>
?亚玲 (xuyal@cn.ibm.com), 软g工程? IBM 中国开发试验室
2007 q?10 ?18 ?/p>
在全球经一体化的今天,|络让大家可以共享同{的信息。世界是q的Q在q个变^的世界里Q我们仍焉要克服语a和文化的差异Q?如果软g如果能够做到全球化,以不同的语言和文化提供信息,那么无疑q个全球化的软g也是让这个世界变q的力量之一?
Eclipse已经成ؓ大家耳熟能详的开发环境和架构q_。现在IBM来多的客L产品UL到Eclipse RCPq_Q本文将介绍ZEclipse RCP的品的全球化实现?
Eclipse的文章很多,那么我们来ȝ一下关键词Q开攄可扩展的IDE Q?gt; 可插入插件的开发环?Q?gt; 带标准插仉(Java Development Tool, JDT)的JAVA开发环?Q?gt;可开发插件的开发环境(Plug-in Development Environment, PDEQ-> 开攄可扩展的应用q_?
Eclipse的开攑֒可扩展究其根源是q行时组件模式的架构Qruntime component moduleQ,我们可以从C++的纯虚基cdCOM原理来解释这些动态可加蝲原理Q从他们的似曄识来认知多年来我们不变的q求。Eclipse 使用 OSGi 作ؓ插gpȝ的基QOSGiQOpen Services Gateway Initiative是一个基于Java语言的服务(业务Q规范,该规范和核心部分是一个框ӞEclipse框架采用Lazy loadingQ提供了q行时可扩展的扩展点模式?
Eclipse Rich Client Platform, RCPQ?在Eclipse platform的基上增加了GUI的特性,它当然也具备Java的跨q_Ҏ,是由可构建桌面应的最的一l插件组成的q行q_。这里面的关键词/ SWTQ?JFaceQWorkbench。首先,Standard Widget ToolkitQSWT最显著的特Ҏ体现了系l^台即操作pȝ的UI的特性,是^台相关的套g集合。基于SWT开发的UI体现了操作系l^台的UI特征?JFace在SWT上提供了更高层的装Q提供了功能强大的界面组建,菜单Q工hQ状态条{等?Workbench: 提供了更高层的可扩展可管理的UI, 包括editors, views ?perspectives?
Eclipse RCP l构囄CZ您谈到Y件的国际化支持,我们通常会从对字W集Q文化习惯和对翻译的支持也就是对本地化的支持三个斚w来考虑?
对于前两个方面,我们知道现在通过UNICODE字符集来支持全球字符Q而文化习惯包括日期,旉Q货币,地址{等Q尽不同的国家地区千差万别Q但它的实现通常q没有我们想象的那么复杂Q可以通过调用支持全球化的lg来实现对字符集和文化Ҏ的支持。例如IBM 中国开发中心的Globalization团队推ZSWT的支持文化特性的套gQ这些套件可在基于RCPq_的应用中被重用。所以,在以下的D落中我们会重点来谈如何实现Eclipse RCP 软gҎ地化的支持,也就是Y件的可翻译性?
我们从以下几方面探讨Y件的可翻译性:
通常Q在传统开发中Q界面布局问题一直是困扰本地化开发过E中比较大的问题Q由于文本翻译后引v长度的扩展,D了界面布局的截断,寚w和遮盖问题。如下图Q在译为Greek语言后,出现了很多截断问题?/p>
囑Ş界面的布局调整是一工作量非常大的工作Q我们知道传l开发是以矩形来定位控g的布局Q即通过坐标值和长度及宽度来描述控g的位|和大小Q通常在英文品开发过E中Q会通过预留一定的扩展I间来解册个问题,但是译的过E中存在着很多偶然性,l常会有一些翻译过长的字串q会出现截断问题Q同时所有语a׃nl一的留有扩展的界面布局降低了界面的友好性,在英文和CJKTq些扩展较少的语a上,我们会觉得界面上有太多的I白I间?
SWT中的Layout manager很好的解决了q个问题。layout manager会管理界面的布局QSWT提供了四Ulayout managerQ在q四Ulayout manager中,功能最强大Q最复杂Q也最有实用性的是GridLayout。GridLayout用网格来控制布局。关于layout manager 有最l典的介l文章,大家可以通过q些文章来了解layout manager的工作原理。本文则是要一定要用layout manager来解x地化q程中界面的布局问题Q在布局设计时要惛_本地化后引v的扩展问题,在这U前提下开发,会将布局问题减少到最,我们在测试Sametime7.5 {RCP的品过E中发现Q布局q一困扰本地化开发的问题得到了根本的解决?
在SWT的示例程序中Q我们可以看到layout manager在本地化q程中是如何自动调整布局的, 在AddressBookq个例子中采用了Fill Layout 布局理器,当我们扩展这些界面上要翻译的字符串时Q我们看到界面的布局也随之自动的扩展变化?
SWT套g允许讄Z叛_左RTLQ但BiDi的支持与q_有关QBiDi支持正扩展到所有Windowsq_和GTK的linuxq_。如果品考虑支持BiDiQ需要调研你使用的SWT版本对BiDi的支持?
大家都知道在JavaE序中用Resource bundle来处理locale相关的资源文Ӟ 所有要译的资源保存到按照locale命名的properties文g中,通过resource bundle来调用。Localeq个词一直没有很好的译Q我们可以理解ؓ语言Q地区,我们命名properties文g也会以locale作ؓ扩展Q例如中国的体中文文Ӟ我们会命名ؓaddress_zh_CN.properties, 而台湄J体中文文g会命名ؓaddress_zh_TW.properties, 当在中文中国locale下时Q程序设计会依顺序查找address_zh_CN.propertiesQ然后是 address_zh.propertiesQ最后fall back到address.propertiesQ而在中文台湾locale下则会查找address_zh_TW.propertiesQ找不到会是地域不同但语a相近的address_zh.propertiesQ最后是英文的address.properties?
如果你在~程时没有这Lq虑Q可以用Eclipse提供的wizard来完成这工作,q部分也有很多技术资料可参考。同时我们更应该从程序设计开始就做好资源文g的规划,下面我们来介l一下资源文件的理Q注释,译和检查工作?
在一个项目中如何有效的定义,创徏和管理资源文Ӟ首先我们会对资源q行分类Q按照资源的cd我们会分为User Interface,UI界面资源和User Assisant, UA帮助资源Q从最l用者的角度Q资源可分ؓ普通用戯源和IT用户的资源?
我们把用户分为普通用户和IT用户Q普通用h指用L领域qIT领域Q例如银行柜员,管他熟悉他所使用的业务系l,但他qIT专职人员Q而IT用户则特指如E序员,|络理员等IT从业者。从最l用h区分资源Q我们可以根据资源的使用者来定是否要翻译这部分资源。通常普通用L资源译U别较高Q而IT用户的资源翻译别较低,例如一些专业的调试出错信息QIT用户可能更們于用英文原文信息?
在JAVA目中,通常UA帮助文g会以html的Ş式存在,所以我们通常以不同语a命名的文件夹下面。而对于UI文gQ正如我们前面介l的Q通常存ؓfilename_{locale}.properties 的文件?
在这里,我们在创源文件时要同时创建filenames.properties和filenames_en.properties文gQ在filenames_en.properties中存放要译的字串而在filenames.properties中存攄权信息和不需要翻译的资源。通过q个单的拆分法,我们可以得到很多开发的方便之处Q?
伪翻译测试是试软g可翻译性的最有效的手D,通常我们试软g的可译性可通过伪翻译或pilot译来测试。伪译的过E是利用工具品中需要翻译的部分随机或按照一定规则进行{换,以这U{换来模仿译的过E,而pilot译则是在英文品开发的同时Q选择某种语言例如hq行同步译。通过产品开发过E中的伪译或pilot译Q测试下面几c问题:
1Q是否有需要翻译的字符串被编码。当所有的资源文g中的资源被伪译为某U字W,例如全部被{换ؓ圆点W号Q那么界面上仍旧昄文ASCII原文的字W串有可能是硬~码在代码中Q没有被伪翻译的字符丌Ӏ这些字W串需要被resource outQ?才会被正的译和显C?
2Q对字符集的支持在伪译q程中,我们会把原ASCII字符伪翻译ؓ风险字符来测试品对q些Ҏ字符的支持。关于风险字W,如果大家感兴,我会单独写一介l文章,每个语言的风险字W不同,另外比如Ƨ元QGB18030字符都是Ҏ出现问题的风险字W。如下给Z一D将ASCII码{化ؓ重音字符的脚本代码,Zq种转换Q不但可以测试对西欧中欧重音字符的支持,同时保证了程序的可读性?
3Q界面布局的可译性我们可以选择按一定比例,例如30%Q扩展原文的伪翻译方式,如果伪翻译后界面出现截断Q不寚w{问题,说明界面的设计不具备可翻译性。由于语a的翻译有很多偶然性,可以随机的把一些字W串加长Q来试界面对可译性的支持?
4Q串联字W串在伪译的过E中Q我们会通过l每句要译的文本在首尾加上分割W例如[]来发现这c问题。当一个完整的句子是由几个片断拼凑h的话会ؓ译带来很大的困扎ͼ例如Q?
Strings_OutOfOffice = I will be out of the office
Strings_StartTime= starting {0}.
在实际运行环境中的显CZؓQI will be out of the office starting 2007-8-17.
我们可以通过定义在句首加“[”Q句末加“]”的伪译规则Q这样在伪翻译后Q?
Strings_OutOfOffice=[I will be out of the office]
Strings_StartTime=[starting{0}]
在实际运行环境中的显CZؓQ[I will be out of the office][ starting 2007-8-17.] 如果在一个整句中发现“][”Q则是有串联情况出现?
q句话整句翻译成中文Q我?007-8-17日v不在办公室?
Strings_OutOfOffice=我不在办公室
Strings_StartTime=从{0}.
当在资源文g中被分ؓ两个断句分别译在进行组合时Q翻译变?#8220;我不在办公室?007-8-17?#8221;Q显Ӟq不W合中文的翻译习惯。所以,在翻译的q程中,׃不同语言间的文化Ҏ有很大的不同,语序l常会出现变化,只有保持句子的完整性,才能保证译的准性?
以上从开发和全球化测试的角度介绍?Eclipse RCP 上的国际化,软g的本地化是一个比较复杂的工程Q在开发中充分考虑Ҏ地化的支持,使用自动化工hq行理会ɘq个工程更加更加规范有序
30分钟内让你明白正则表辑ּ是什么,q对它有一些基本的了解Q让你可以在自己的程序或|页里用它?/p>
最重要的是——请l我30分钟Q如果你没有使用正则表达式的l验Q请不要试图?0U?/em>内入门——除非你是超?:)
别被下面那些复杂的表辑ּ吓倒,只要跟着我一步一步来Q你会发现正则表辑ּ其实q?span lang="zh-cn">没有你想像中的那么困难。当Ӟ如果你看完了q篇教程之后Q发现自己明白了很多Q却又几乎什么都C得,那也是很正常的——我认ؓQ没接触q正则表辑ּ的h在看完这教E后Q能把提到过的语法记?0%以上的可能性ؓ零。这里只是让你明白基本的原理Q以后你q需要多l习Q多使用Q才能熟l掌握正则表辑ּ?/p>
除了作ؓ入门教程之外Q本文还试图成ؓ可以在日常工作中使用的正则表辑ּ语法参考手册。就作者本人的l历来说Q这个目标还是完成得不错的——你看,我自׃没能把所有的东西C来,不是吗?
清除格式 文本格式U定Q?span class="name">专业术语 元字W?语法格式 正则表达?/span> 正则表达式中的一部分(用于分析) 对其q行匚w的源字符?/span> Ҏ则表辑ּ或其中一部分的说?/span>
隐藏Ҏ 本文双有一些注释,主要是用来提供一些相关信息,或者给没有E序员背景的读者解释一些基本概念,通常可以忽略?/p>
字符是计机软g处理文字时最基本的单位,可能是字母,数字Q标点符PI格Q换行符Q汉字等{?span class="name">字符?/span>?个或更多个字W的序列?span class="name">文本也就是文字,字符丌Ӏ说某个字符?span class="name">匚w某个正则表达式,通常是指q个字符串里有一部分Q或几部分分别)能满辑ּl出的条件?/p>
在编写处理字W串的程序或|页Ӟl常会有查找W合某些复杂规则的字W串的需要?span class="name">正则表达?/span>是用于描述q些规则的工兗换句话_正则表达式就是记录文本规则的代码?/p>
很可能你使用qWindows/Dos下用于文件查扄通配W?wildcard)Q也是*?span class="code">?。如果你x找某个目录下的所有的Word文档的话Q你会搜?span style="color: red">*.doc。在q里Q?span class="code">*会被解释成Q意的字符丌Ӏ和通配W类|正则表达式也是用来进行文本匹配的工具Q只不过比v通配W,它能更精地描述你的需求——当Ӟ代h是更复杂——比如你可以~写一个正则表辑ּQ用来查?span class="desc">所有以0开_后面跟着2-3个数字,然后是一个连字号“-”Q最后是7?位数字的字符?/span>(?span class="string">010-12345678?span class="string">0376-7654321)?/p>
学习正则表达式的最好方法是从例子开始,理解例子之后再自己对例子q行修改Q实验。下面给Z不少单的例子Qƈ对它们作了详l的说明?/p>
假设你在一英文小说里查找hiQ你可以使用正则表达?span class="regex">hi?/p>
q几乎是最单的正则表达式了Q它可以_匚wq样的字W串Q?span class="desc">׃个字W组成,前一个字W是h,后一个是i。通常Q处理正则表辑ּ的工具会提供一个忽略大写的选项Q如果选中了这个选项Q它可以匚whi,HI,Hi,hIq四U情况中的Q意一U?/p>
不幸的是Q很多单词里包含hiq两个连l的字符Q比?span class="string">him,history,high{等。用hi来查扄话,q里边的hi也会被找出来。如果要_地查找hiq个单词的话Q我们应该?span class="regex">\bhi\b?/p>
\b是正则表辑ּ规定的一个特D代码(好吧Q某些h叫它元字W,metacharacterQ,代表着单词的开头或l尾Q也是单词的分界处。虽焉常英文的单词是q|标点W号或者换行来分隔的,但是\bq不匚wq些单词分隔字符中的M一个,?strong>只匹配一个位|?/strong>?/p>
如果需要更_的说法,\b匚wq样的位|:它的前一个字W和后一个字W不全是(一个是,一个不是或不存?\w?/p>
假如你要扄?span class="desc">hi后面不远处跟着一个LucyQ你应该?span class="regex">\bhi\b.*\bLucy\b?/p>
q里Q?span class="part">.是另一个元字符Q匹?span class="desc">除了换行W以外的L字符?span class="part">*同样是元字符Q不q它代表的不是字W,也不是位|,而是数量——它指定*前边的内容可以连l重复出CQ意次以整个表达式得到匹?/span>。因此,.*q在一起就意味着L数量的不包含换行的字W?/span>。现?span class="regex">\bhi\b.*\bLucy\b的意思就很明显了Q?span class="desc">先是一个单词hi,然后是Q意个L字符(但不能是换行)Q最后是Lucyq个单词?/p>
换行W就?\n',ASCII~码?0(十六q制0x0A)的字W?/p>
如果同时使用其它元字W,我们p构造出功能更强大的正则表达式。比如下面这个例子:
0\d\d-\d\d\d\d\d\d\d\d匚wq样的字W串Q?span class="desc">?开_然后是两个数字,然后是一个连字号“-”Q最后是8个数?/span>(也就是中国的电话L。当Ӟq个例子只能匚w区号?位的情Ş)?/p>
q里?span class="part">\d是个新的元字W,匚w一位数?0Q或1Q或2Q或……)?span class="part">-不是元字W,只匹配它本n——连字符或者减受?/p>
Z避免那么多烦人的重复Q我们也可以q样写这个表辑ּQ?span class="regex">0\d{2}-\d{8}?q里\d后面?span class="part">{2}({8})的意思是前面\d必须q箋重复匚w2?8??/p>
其它可用的测试工?
如果你不觉得正则表达式很难读写的话,要么你是一个天才,要么Q你不是地球人。正则表辑ּ的语法很令h头疼Q即使对l常使用它的人来说也是如此。由于难于读写,Ҏ出错Q所以找一U工具对正则表达式进行测试是很有必要的?/p>
׃在不同的环境下正则表辑ּ的一些细节是不相同的Q本教程介绍的是微Y .Net Framework 2.0下正则表辑ּ的行为,所以,我向你介l一?Net下的工具.Net Framework 2.0Q然?a title="从www.unibetter.com下蝲Regex Tester, 75KB" >下蝲Regex Tester。这是个l色软gQ下载完后打开压羃?直接q行RegexTester.exe可以了?/p>
下面是Regex Testerq行时的截图Q?/p>
现在你已l知道几个很有用的元字符了,?span class="code">\b,.,*Q还?span class="code">\d.正则表达式里q有更多的元字符Q比?span class="code">\s匚wL的空白符Q包括空|制表W?Tab)Q换行符Q中文全角空格等?span class="code">\w匚w字母或数字或下划U或汉字{?/span>?/p>
对中?汉字的特D处理是?Net提供的正则表辑ּ引擎支持的,其它环境下的具体情况h看相x档?/p>
下面来看看更多的例子Q?/p>
\ba\w*\b匚w以字?span class="part">a开头的单词——先是某个单词开始处(\b)Q然后是字母a,然后是Q意数量的字母或数?\w*)Q最后是单词l束?\b)
好吧Q现在我们说说正则表辑ּ里的单词是什么意思吧Q就是多于一个的q箋?span class="code">\w。不错,q与学习英文时要背的成千上万个同名的东西的确关系不大 :)
\d+匚w1个或更多q箋的数?/span>。这里的+是和*cM的元字符Q不同的?span class="code">*匚w重复L?可能??Q?span class="code">+则匹?span class="desc">重复1ơ或更多?/span>?/p>
\b\w{6}\b 匚w刚好6个字?数字的单?/span>?/p>
元字W?span class="code">^
代码
说明
.
匚w除换行符以外的Q意字W?/span>
\w
匚w字母或数字或下划U或汉字
\s
匚wL的空白符
\d
匚w数字
\b
匚w单词的开始或l束
^
匚w字符串的开?/span>
$
匚w字符串的l束
q里?span class="part">{5,12}和前面介l过?span class="part">{2}是类似的Q只不过{2}匚w只能不多不少重复2?/span>Q?span class="part">{5,12}则是重复的次C能少?ơ,不能多于12?/span>Q否则都不匹配?/p>
因ؓ使用?span class="part">^ 和忽略大写的选项cMQ有些正则表辑ּ处理工具q有一个处理多行的选项。如果选中了这个选项Q?span class="code">^
如果你想查找元字W本w的话,比如你查?span class="desc">.,或?span class="desc">*,出C问题Q你没办法指定它们,因ؓ它们会被解释成别的意思。这时你得使用\来取消这些字W的Ҏ意义。因此,你应该?span class="regex">\.?span class="regex">\*。当Ӟ要查?span class="desc">\本nQ你也得?span class="regex">\\.
例如Q?span class="regex">unibetter\.com匚wunibetter.comQ?span class="regex">C:\\Windows匚wC:\Windows?/p>
你已l看q了前面?span class="code">*,+,{2},{5,12}q几个匹配重复的方式了。下面是正则表达式中所有的限定W?指定数量的代码,例如*,{5,12}{?Q?/p>
代码/语法 | 说明 |
---|---|
* | 重复零次或更多次 |
+ | 重复一ơ或更多?/span> |
? | 重复零次或一?/span> |
{n} | 重复n?/span> |
{n,} | 重复nơ或更多?/span> |
{n,m} | 重复n到m?/span> |
下面是一些用重复的例子Q?/p>
Windows\d+匚wWindows后面?个或更多数字
^\w+匚w一行的W一个单?或整个字W串的第一个单词,具体匚w哪个意思得看选项讄)
要想查找数字Q字母或数字Q空白是很简单的Q因为已l有了对应这些字W集合的元字W,但是如果你想匚w没有预定义元字符的字W集?比如元音字母a,e,i,o,u),应该怎么办?
很简单,你只需要在Ҏ号里列出它们p了,?span class="regex">[aeiou]匹?span class="desc">M一个英文元韛_?/span>Q?span class="regex">[.?!]匚w标点W号(.??)?/p>
我们也可以轻村֜指定一个字W?span class="name">范围Q像[0-9]代表的含意与\d是完全一致的Q?span class="desc">一位数?/span>Q同?span class="regex">[a-z0-9A-Z_]也完全等同于\wQ如果只考虑英文的话Q?/p>
下面是一个更复杂的表辑ּQ?span class="regex">\(?0\d{2}[) -]?\d{8}?/p>
“(”?#8220;)”也是元字W,后面?a >分组?/a>里会提到Q所以在q里需要?a >转义?/p>
q个表达式可以匹?span class="desc">几种格式的电话号?/span>Q像(010)88886666Q或022-22334455Q或02912345678{。我们对它进行一些分析吧Q首先是一个{义字W?span class="part">\(,它能出现0ơ或1??),然后是一?span class="part">0Q后面跟着2个数?\d{2})Q然后是)?span class="part">-?span class="part">I格中的一个,它出?ơ或不出??)Q最后是8个数?\d{8})?/p>
不幸的是Q刚才那个表辑ּ也能匚w010)12345678?span class="string">(022-87654321q样?#8220;不正?#8221;的格式。要解决q个问题Q我们需要用?span class="name">分枝条g。正则表辑ּ里的分枝条g指的是有几种规则Q如果满_中Q意一U规则都应该当成匚wQ具体方法是?span class="code">|把不同的规则分隔开。听不明白?没关p,看例子:
0\d{2}-\d{8}|0\d{3}-\d{7}q个表达式能匚w两种以连字号分隔的电话号码:一U是三位区号Q?位本地号(?10-12345678)Q一U是4位区P7位本地号(0376-2233445)?/p>
\(0\d{2}\)[- ]?\d{8}|0\d{2}[- ]?\d{8}q个表达?span class="desc">匚w3位区L电话LQ其中区号可以用括hhQ也可以不用Q区号与本地号间可以用连字号或空格间隔,也可以没有间?/span>。你可以试试用分枝条件把q个表达式扩展成也支?位区L?/p>
\d{5}-\d{4}|\d{5}q个表达式用于匹配美国的邮政~码。美国邮~的规则?位数字,或者用q字号间隔的9位数字。之所以要l出q个例子是因为它能说明一个问题:使用分枝条gӞ要注意各个条件的序。如果你把它Ҏ\d{5}|\d{5}-\d{4}的话Q那么就只会匚w5位的邮编(以及9位邮~的??。原因是匚w分枝条gӞ会从左到右地测试每个条Ӟ如果满了某个分枝的话,׃会去再管其它的条件了?/p>
我们已经提到了怎么重复单个字符Q直接在字符后面加上限定W就行了Q;但如果想要重复多个字W又该怎么办?你可以用括h指定子表辑ּ(也叫?span class="name">分组)Q然后你可以指定这个子表达式的重复ơ数了,你也可以对子表达式进行其它一些操?后面会有介绍)?/p>
(\d{1,3}\.){3}\d{1,3}是一?span class="desc">单的IP地址匚w表达式。要理解q个表达式,h下列序分析它:\d{1,3}匚w1?位的数字Q?span class="part">(\d{1,3}\.){3}匚w三位数字加上一个英文句?q个整体也就是这?span class="name">分组)重复3?/span>Q最后再加上一个一C位的数字(\d{1,3})?/p>
IP地址中每个数字都不能大于255Q大家千万不要被?4》第三季的编剧给忽悠?..
不幸的是Q它也将匚w256.300.888.999q种不可能存在的IP地址。如果能使用术比较的话Q或许能单地解决q个问题Q但是正则表辑ּ中ƈ不提供关于数学的M功能Q所以只能用冗长的分组Q选择Q字W类来描qC个正的IP地址Q?span class="regex">((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)?/p>
理解q个表达式的关键是理?span class="part">2[0-4]\d|25[0-5]|[01]?\d\d?Q这里我׃l说了,你自己应该能分析得出来它的意义?/p>
有时需要查找不属于某个能简单定义的字符cȝ字符。比如想查找除了数字以外Q其它Q意字W都行的情况Q这旉要用?span class="name">反义Q?/p>
代码/语法 | 说明 |
---|---|
\W | 匚wL不是字母Q数字,下划U,汉字的字W?/span> |
\S | 匚wL不是I白W的字符 |
\D | 匚wL非数字的字符 |
\B | 匚w不是单词开头或l束的位|?/span> |
[^x] | 匚w除了x以外的Q意字W?/span> |
[^aeiou] | 匚w除了aeiouq几个字母以外的L字符 |
例子Q?span class="regex">\S+匚w不包含空白符的字W串?/p>
<a[^>]+>匚w用尖括号括v来的以a开头的字符?/span>?/p>
使用括h定一个子表达式后Q?strong>匚wq个子表辑ּ的文?/strong>(也就是此分组捕获的内?可以在表辑ּ或其它程序中作进一步的处理。默认情况下Q每个分l会自动拥有一?span class="name">l号后向引用
后向引用用于重复搜烦前面某个分组匚w的文本。例如,\1代表分组1匚w的文?/span>。难以理解?LCZQ?/p>
\b(\w+)\b\s+\1\b可以用来匚w重复的单?/span>Q像go go, 或?span class="string">kitty kitty。这个表辑ּ首先?span class="desc">一个单?/span>Q也是单词开始处和结束处之间的多于一个的字母或数?/span>(\b(\w+)\b)Q这个单词会被捕获到~号?的分l中Q然后是1个或几个I白W?/span>(\s+)Q最后是分组1中捕L内容Q也是前面匚w的那个单词)(\1)?/p>
你也可以自己指定子表辑ּ?span class="name">l名
使用括L时候,q有很多特定用途的语法。下面列Z最常用的一些:
分类 | 代码/语法 | 说明 |
---|---|---|
捕获 | (exp) | 匚wexp,q捕h本到自动命名的组?/span> |
(?<name>exp) | 匚wexp,q捕h本到名称为name的组里,也可以写??'name'exp) | |
(?:exp) | 匚wexp,不捕获匹配的文本Q也不给此分l分配组?/span> | |
零宽断言 | (?=exp) | 匚wexp前面的位|?/span> |
(?<=exp) | 匚wexp后面的位|?/span> | |
(?!exp) | 匚w后面跟的不是exp的位|?/span> | |
(?<!exp) | 匚w前面不是exp的位|?/span> | |
注释 | (?#comment) | q种cd的分l不Ҏ则表辑ּ的处理生Q何媄响,用于提供注释让h阅读 |
我们已经讨论了前两种语法。第三个(?:exp)不会改变正则表达式的处理方式Q只是这Ll匹配的内容不会像前两种那样被捕获到某个l里面,也不会拥有组?/span>?/p>
地球人,是不是觉得这些术语名U太复杂Q太难记了?我也和你一栗知道有q么一U东西就行了Q它叫什么,随它dQ?#8220;无名Q万物之?..” 接下来的四个用于查找在某些内?但ƈ不包括这些内?之前或之后的东西Q也是说它们像\b,^,$那样用于指定一个位|,q个位置应该满一定的条g(xa)Q因此它们也被称?span class="name">零宽断言零宽断言
断言用来声明一个应该ؓ真的事实。正则表辑ּ中只有当断言为真时才会l进行匹配?/p>
(?=exp)也叫零宽度正预测先行断言Q它断言自n出现的位|的后面能匹配表辑ּexp。比?span class="regex">\b\w+(?=ing\b)Q匹?span class="desc">以ingl尾的单词的前面部分(除了ing以外的部?Q如查找I'm singing while you're dancing.Ӟ它会匚wsing?span class="desc">danc?/p>
(?<=exp)也叫零宽度正回顾后发断言Q它断言自n出现的位|的前面能匹配表辑ּexp。比?span class="regex">(?<=\bre)\w+\b会匹?span class="desc">以re开头的单词的后半部?除了re以外的部?Q例如在查找reading a bookӞ它匹?span class="desc">ading?/p>
假如你想要给一个很长的数字中每三位间加一个逗号(当然是从双加v?Q你可以q样查找需要在前面和里面添加逗号的部分:((?<=\d)\d{3})*\bQ用它对1234567890q行查找时结果是234567890?/p>
下面q个例子同时使用了这两种断言Q?span class="regex">(?<=\s)\d+(?=\s)匚w以空白符间隔的数?再次Q不包括q些I白W??/p>
前面我们提到q怎么查找不是某个字符或不在某个字W类?/strong>的字W的Ҏ(反义)。但是如果我们只是想?strong>保某个字符没有出现Q但q不惛_匚w?/strong>时怎么办?例如Q如果我们想查找q样的单?-它里面出C字母q,但是q后面跟的不是字母u,我们可以试q样Q?/p>
\b\w*q[^u]\w*\b匚w包含后面不是字母u的字母q的单?/span>。但是如果多做测?或者你思维_敏锐Q直接就观察出来?Q你会发玎ͼ如果q出现在单词的l尾的话Q像Iraq,BenqQ这个表辑ּ׃出错。这是因?span class="part">[^u]总要匚w一个字W,所以如果q是单词的最后一个字W的话,后面?span class="part">[^u]会匚wq后面的单词分隔符(可能是空|或者是句号或其它的什?Q后面的\w*\b会匚w下一个单词,于是\b\w*q[^u]\w*\bp匚w整个Iraq fighting?span class="name">负向零宽断言能解册L问题Q因为它只匹配一个位|,q不消费M字符。现在,我们可以q样来解册个问题:\b\w*q(?!u)\w*\b?/p>
零宽度负预测先行断言(?!exp)Q?span class="desc">断言此位|的后面不能匚w表达式exp。例如:\d{3}(?!\d)匚w三位数字Q而且q三位数字的后面不能是数?/span>Q?span class="regex">\b((?!abc)\w)+\b匚w不包含连l字W串abc的单?/span>?/p>
同理Q我们可以用(?<!exp),零宽度正回顾后发断言?span class="desc">断言此位|的前面不能匚w表达式exp 误l分析表辑ּ(?<=<(\w+)>).*(?=<\/\1>)Q这个表辑ּ最能表现零宽断a的真正用途?/p>
一个更复杂的例子:(?<=<(\w+)>).*(?=<\/\1>)匚w不包含属性的单HTML标签内里的内?/span>?span class="code">(<?(\w+)>)指定了这L前缀Q?span class="desc">被尖括号括v来的单词 括L另一U用途是通过语法(?#comment)来包含注释。例如:2[0-4]\d(?#200-249)|25[0-5](?#250-255)|[01]?\d\d?(?#0-199)?/p>
要包含注释的话,最好是启用“忽略模式里的I白W?#8221;选项Q这样在~写表达式时能Q意的dI格QTabQ换行,而实际用时q些都将被忽略。启用这个选项后,?后面到这一行结束的所有文本都被当成注释忽略掉。例如,我们可以前面的一个表辑ּ写成q样Q?/p>
当正则表辑ּ中包含能接受重复的限定符Ӟ通常的行为是Q在使整个表辑ּ能得到匹配的前提下)匚w可能多的字W。考虑q个表达式:a.*bQ它会匚w最长的以a开始,以bl束的字W串。如果用它来搜烦aabab的话Q它会匹配整个字W串aabab。这被称?span class="name">贪婪匚w?/p>
有时Q我们更需?span class="name">懒惰匚wQ也是匚w可能少的字W。前面给出的限定W都可以被{化ؓ懒惰匚w模式Q只要在它后面加上一个问?span class="code">?。这?span class="regex">.*?意味着匚wL数量的重复,但是在能使整个匹配成功的前提下用最的重复。现在看看懒惰版的例子吧Q?/p>
a.*?b匚w最短的Q以a开始,以bl束的字W串。如果把它应用于aabab的话Q它会匹?span class="desc">aabQ第一到第三个字符Q?/span>?span class="desc">abQ第四到W五个字W)?/p>
Z么第一个匹配是aabQ第一到第三个字符Q而不是abQ第二到W三个字W)Q简单地_因ؓ正则表达式有另一条规则,比懒惎ͼ贪婪规则的优先更高Q最先开始的匚w拥有最高的优先权——The match that begins earliest wins?/p>
在C#中,你可以?a title="MSDN 相关文档" >Regex(String, RegexOptions)构造函?/a>来设|正则表辑ּ的处理选项。如QRegex regex = new Regex("\ba\w{6}\b", RegexOptions.IgnoreCase); 上面介绍了几个选项如忽略大写Q处理多行等Q这些选项能用来改变处理正则表辑ּ的方式。下面是.Net中常用的正则表达式选项Q?/p>
一个经常被问到的问题是Q是不是只能同时使用多行模式和单行模式中的一U?{案是:不是。这两个选项之间没有M关系Q除了它们的名字比较怼Q以至于让h感到疑惑Q以外?/p>
q里介绍的^衡组语法是由.Net Framework支持的;其它语言Q库不一定支持这U功能,或者支持此功能但需要用不同的语法?/p>
有时我们需要匹配像( 100 * ( 50 + 15 ) )q样的可嵌套的层ơ性结?/span>Q这时简单地使用\(.+\)则只会匹配到最左边的左括号和最双的右括号之间的内?q里我们讨论的是贪婪模式Q懒惰模式也有下面的问题)。假如原来的字符串里的左括号和右括号出现的次C相等Q比?span class="string">( 5 / ( 3 + 2 ) ) )Q那我们的匹配结果里两者的个数也不会相{。有没有办法在这L字符串里匚w到最长的Q配对的括号之间的内容呢Q?/p>
Z避免(?span class="code">\(把你的大脑彻底搞p涂Q我们还是用括号代替圆括号吧。现在我们的问题变成了如何把xx <aa <bbb> <bbb> aa> yyq样的字W串里,最长的配对的尖括号内的内容捕获出来Q?/p>
q里需要用C下的语法构造: 如果你不是一个程序员Q或者你自称E序员但是不知道堆栈是什么东西)Q你pL解上面的三种语法吧:W一个就是在黑板上写一?group"Q第二个是从黑板上擦掉一?group"Q第三个是看黑板上写的q有没有"group"Q如果有ql匹配yes部分Q否则就匚wno部分?/p>
我们需要做的是每碰C左括P在压入一?Open",每碰C个右括号Q就弹出一个,C最后就看看堆栈是否为空Q-如果不ؓI那p明左括号比右括号多,那匹配就应该p|。正则表辑ּ引擎会进行回?攑ּ最前面或最后面的一些字W?Q尽量整个表达式得到匹配?/p>
ql的一个最常见的应用就是匹配HTML,下面q个例子可以匚w嵌套?lt;div>标签Q?span class="regex"><div[^>]*>[^<>]*(((?'Open'<div[^>]*>)[^<>]*)+((?'-Open'</div>)[^<>]*)+)*(?(Open)(?!))</div>. 我已l描qC构造正则表辑ּ的大量元素,q有一些我没有提到的东ѝ下面是未提到的元素的列表,包含语法和简单的说明。你可以在网上找到更详细的参考资料来学习它们--当你需要用到它们的时候。如果你安装了MSDN Library,你也可以在里面找到关?net下正则表辑ּ详细的文档?/p>
注释
(?<= # 断言要匹配的文本的前~
<(\w+)> # 查找括hh的字母或数字(即HTML/XML标签)
) # 前缀l束
.* # 匚wL文本
(?= # 断言要匹配的文本的后~
<\/\1> # 查找括hh的内容:前面是一?/"Q后面是先前捕获的标{?
) # 后缀l束
贪婪与懒?/h2>
代码/语法
说明
*?
重复Lơ,但尽可能重?/span>
+?
重复1ơ或更多ơ,但尽可能重?/span>
??
重复0ơ或1ơ,但尽可能重?/span>
{n,m}?
重复n到mơ,但尽可能重?/span>
{n,}?
重复nơ以上,但尽可能重?/span>
处理选项
名称
说明
IgnoreCase(忽略大小?
匚w时不区分大小写?/td>
Multiline(多行模式)
更改^?span class="code">$的含义,使它们分别在L一行的行首和行配,而不仅仅在整个字W串的开头和l尾匚w?在此模式?$的精含意是:匚w\n之前的位|以及字W串l束前的位置.)
Singleline(单行模式)
更改.的含义,使它与每一个字W匹配(包括换行W\nQ?
IgnorePatternWhitespace(忽略I白)
忽略表达式中的非转义I白q启用由#标记的注释?/td>
RightToLeft(从右向左查找)
匚w从右向左而不是从左向双行?/td>
ExplicitCapture(昑ּ捕获)
仅捕获已被显式命名的l?/td>
ECMAScript(JavaScript兼容模式)
使表辑ּ的行Z它在JavaScript里的行ؓ一致?/td>
ql?递归匚w
< #最外层的左括号
[^<>]* #最外层的左括号后面的不是括L内容
(
(
(?'Open'<) #到了左括号Q在黑板上写一?Open"
[^<>]* #匚w左括号后面的不是括号的内?
)+
(
(?'-Open'>) #到了右括号Q擦掉一?Open"
[^<>]* #匚wx号后面不是括L内容
)+
)*
(?(Open)(?!)) #在遇到最外层的右括号前面Q判断黑板上q有没有没擦掉的"Open"Q如果还有,则匹配失?
> #最外层的右括号
q有些什么东西没提到
代码/语法
说明
\a
报警字符(打印它的效果是电脑嘀一?
\b
通常是单词分界位|,但如果在字符c里使用代表退?/span>
\t
制表W,Tab
\r
回R
\v
竖向制表W?/span>
\f
换页W?/span>
\n
换行W?/span>
\e
Escape
\0nn
ASCII代码中八q制代码为nn的字W?/span>
\xnn
ASCII代码中十六进制代码ؓnn的字W?/span>
\unnnn
Unicode代码中十六进制代码ؓnnnn的字W?/span>
\cN
ASCII控制字符。比如\cC代表Ctrl+C
\A
字符串开?cM^Q但不受处理多行选项的媄?
\Z
字符串结或行尾(不受处理多行选项的媄?
\z
字符串结?cM$Q但不受处理多行选项的媄?
\G
当前搜烦的开?/span>
\p{name}
Unicode中命名ؓname的字W类Q例如\p{IsGreek}
(?>exp)
贪婪子表辑ּ
(?<x>-<y>exp)
ql?/span>
(?im-nsx:exp)
在子表达式exp中改变处理选项
(?im-nsx)
辑ּ后面的部分改变处理选项
(?(exp)yes|no)
把exp当作零宽正向先行断言Q如果在q个位置能匹配,使用yes作ؓ此组的表辑ּQ否则用no
(?(exp)yes)
同上Q只是用空表达式作为no
(?(name)yes|no)
如果命名为name的组捕获C内容Q用yes作ؓ表达式;否则使用no
(?(name)yes)
同上Q只是用空表达式作为no
]]>
(当然q有其他Ҏ,在这里主要介lpropertiescd的配|文?
2.寚w|信息的操作无非׃U?从配|文件中d信息,q将信息赋值给E序中的相应变量,q有是当Y件运行到某一个特定的时?手动或自
动将软g中的某些信息写入到配|文件中,以便于以后的调用.Z方便的对propertiescd的配|文件进行操?在这里介l一个Java提供?/p>
一个类:Properties大家可以查看API以了解此cȝ详细信息.
对于最单的应用,q里只用到Properties中的三个Ҏ:
1.void load(InputStream inStream)从输入流中读取属性列表(键和元素对)。这个inStream可以有多U取得的Ҏ,q里只介l一U通过
ClassName.class.getClassLoader().getResourceAsStream()来取得properties文g.通常:ClassName.class.getClassLoader ().getResourceAsStream(““)取得的是
WEB-INF的下U目录,比如ClassName.class.getClassLoader().getResourceAsStream(“db.properties“).在Tomcat中,可以通过增加”../”来取得上层目?br />
Q即WEB-INF目录Q这样就可以把properties攑֜WEB-INF中统一理。但是WLS不识?#8221;../”。另外一U土办法Q就是不q回classLoader,
直接ClassName.class.getResourceAsStream()。然后通过多个”../”Q小?个)来返回相应的上目录。当Ӟ如果cL展了HttpServletQ可以通过
getServletContext().getRealPath("/")来取得Web部v目录的绝对\径?nbsp;
2. String getProperty(String key)用指定的键在此属性列表中搜烦属性?br />
3. Object setProperty(String key, String value)调用 Hashtable 的方?put?br />
4. void store(OutputStream out, String comments) 以适合使用 load(InputStream) Ҏ加蝲到Properties 表中的格式,此Properties 表中的属性列表(键和元素对)
写入输出?/p>
在读之前必须要用1Ҏ得到输入?在写之前必须先徏一个到配置文g的输出流作ؓ4的参?写完后用4Ҏ
处于Singleton设计模式的考虑,把整个对配置文g的操作写成一个类,q且所有变量方法全部设|ؓ静?/p>
JVM启动Q会形成3个类加蝲器组成的初始化加载器层次l构Q?br />
bootstap classloader Q加载核心类Q?
||
extension classloaderQ加载ext(目录)Q即java.ext.dirs()Q?br />
||
system classloader Q加?classpath或者java.class.path或者CLASSPATHQ?/p>
ClassLoader机制Q?br />
aQ全盘负责:一个classloader加蝲一个class后,q个class所引用或者依赖的cMp个classloader载入Q除非显C的用另一个classloader载入
bQ委托机Ӟ先由父加载器加蝲Q除非父加蝲器找不到时才从自qc\径中d?br />
cQCache机制Qclassloader采用~存机制Q即先查cacheQ若cache中保存了q个classq接返回;若无Q才从文件读取和转化为classq放入cache
ClassLoader加蝲c顺序:
1Q检查cache是否有该c:
11Q若有直接返?br />
12Q若无,h父类加蝲
121) 若无?则从bootstap classloader加蝲
2Q加载:
21Q寻找class文gQ丛与此classloader相关的类路径中寻找)
22Q从文g载入class
23Q找不到则抛出ClassNotFoundeException
3Q扩展:
记蝲时即2Q,覆写findClass可以实现自己的蝲入策?br />
记蝲时即2Q,覆写loadClass来实现自q载入q程
如何实现q行时动态蝲入与更新
本质Q只要动态改cL索\径和清除classloader的cache已蝲入的classok
做法Q?br />
1Q承ClassLoaderQ覆写loadClassҎQ动态寻找class文g
2Q只要重C用一个新的类搜烦路径来new一个classloader可以,q样既更Ccȝ搜烦路径以便来蝲入新的classQ也更新生成了一个空白的cache
classloader载入的方?br />
1QPre-loading 预先载入Q蝲入基c?br />
2Qload-on-demand 按需求蝲?/p>
JDK为啥有两个JREQ?br />
JDK中jre是运行java本n的程序,如javac
ProgramFileQ默认安装)中jre是运行用L写的javaE序
classloader有啥妙用Q?Q?
q个问题得从自定义的classloaderw上_那自定义classloader~由是什么呢Q?br />
告诉你:大多是因为编译时无法预知q行旉要哪些类Q特别是app serverQ因此自定义classloaderQ运行时指定路径Q来加蝲q个路径下的class
Ҏ说明
Ҏ说明1Q如果没有特D指定,用户自定义的classloader都把system classloader作ؓ它的父加载器
Ҏ说明2Qjvm认ؓ不同的classloade载入相同名字的class是不同的Q即使从同一个class文g载入
classloader有啥妙用Q?Q?
看到Ҏ说明2Q你或许׃感觉疑惑或者不爽;啥概念?
以servlet、ejb{容器来剖析q个问题Q?/p>
接口或者基cL入classpath <---------system classloader
执行Ӟ动态蝲入实现或者承这些接口或者基cȝ子类Q?lt;---------customized classloader
||
||
用customized classloader载入cLQ发现它有一个父cclassQextendsQ;
但是在蝲入它Ӟjvm先加载父cclassQ?q个父类是system classloader能识别的Q?Ҏ“委托机制”它将由system classloader来加载;
然后customized classloaderQ实际是system classloader来加载)再蝲入这个class,创徏一个实例,转型为父c;
jvm׃用system classloader再次载入父类classQ然后将此实例{型ؓq个父类classQ?/p>
q个q程加蝲了两个父cclassQ都是由system classloader载入Q即同一个classloader载入同一个文Ӟ造型不会由异?/p>
web app server大概是这样工作的Q这栯入了Ml承了servlet的classq正运行它们,不管class是什么,都它们实例化Z个servlet class