這篇文章主要介紹了如何構(gòu)建用于正則表達(dá)式的抽象Java API,具有一定借鑒價(jià)值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
網(wǎng)站建設(shè)哪家好,找創(chuàng)新互聯(lián)!專注于網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)站建設(shè)、微信開(kāi)發(fā)、重慶小程序開(kāi)發(fā)公司、集團(tuán)企業(yè)網(wǎng)站建設(shè)等服務(wù)項(xiàng)目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了三穗免費(fèi)建站歡迎大家使用!
簡(jiǎn)介
盡管您可能認(rèn)為編寫需要分析文本的 Java
應(yīng)用程序是一項(xiàng)簡(jiǎn)單任務(wù),但象許多事情一樣,它會(huì)很快變得復(fù)雜起來(lái)。那的確是我在編寫代碼以解析 HTML 頁(yè)面時(shí)的經(jīng)驗(yàn)。開(kāi)始的時(shí)候,我偶爾會(huì)使用 Perl5
正則表達(dá)式(regexp)。但是,由于某些原因(稍后說(shuō)明),我后來(lái)常常使用它們。
背景知識(shí)
在我的經(jīng)驗(yàn)中,大多數(shù) Java
開(kāi)發(fā)人員都需要解析某種文本。通常,這意味著他們最初要花一些時(shí)間使用象 indexOf 或 substring 那樣的與 Java
字符串相關(guān)的函數(shù)或方法,并且希望輸入格式永遠(yuǎn)不變。但是,如果輸入格式改變,那么用于讀取新格式的代碼維護(hù)起來(lái)就會(huì)變得更復(fù)雜、更困難。最后,代碼可能需要支持自動(dòng)換行(word
wrapping)、區(qū)分大小寫等。
由于邏輯變得更加復(fù)雜,所以維護(hù)也變得很困難。因?yàn)槿魏胃亩伎赡墚a(chǎn)生副作用并使文本解析器的其它部分停止工作,所以開(kāi)發(fā)人員需要時(shí)間修正這些小錯(cuò)誤。
有一定
Perl
經(jīng)驗(yàn)的開(kāi)發(fā)人員可能也有過(guò)使用正則表達(dá)式的經(jīng)驗(yàn)。如果夠幸運(yùn)(或優(yōu)秀)的話,這位開(kāi)發(fā)人員能夠說(shuō)服團(tuán)隊(duì)其余的人(或至少是團(tuán)隊(duì)領(lǐng)導(dǎo))使用這項(xiàng)技術(shù)。新的方法將取消編寫用來(lái)調(diào)用
String 方法的多行代碼,它意味著將解析器邏輯的核心委托出去,并替換為 regexp 庫(kù)。
接受了有 Perl5
經(jīng)驗(yàn)的開(kāi)發(fā)人員的建議后,團(tuán)隊(duì)必須選擇哪個(gè) regex
實(shí)現(xiàn)最適合他們的項(xiàng)目。然后他們需要學(xué)習(xí)如何使用它。
在簡(jiǎn)要地研究了從因特網(wǎng)上找到的眾多可選方案后,假設(shè)團(tuán)隊(duì)決定從人們更熟悉的庫(kù)中選擇一個(gè)使用,如屬于
Jakarta 項(xiàng)目的 Oro。接下來(lái),對(duì)解析器進(jìn)行較大程度地重構(gòu)或幾乎重新編寫,并且解析器最終使用了 Oro 的類,如
Perl5Compiler、Perl5Matcher 等。
這一決定的后果很明顯:
代碼與 Jakarta Oro
的類緊密地耦合在一起。
團(tuán)隊(duì)承擔(dān)了風(fēng)險(xiǎn),因?yàn)椴恢婪枪δ苄孕枨螅ㄈ缧阅芑蚓€程模型)是否將得到滿足。
團(tuán)隊(duì)已花費(fèi)時(shí)間和財(cái)力來(lái)學(xué)習(xí)并重新編寫代碼,以使它使用
regexp
庫(kù)。如果他們的決定是錯(cuò)誤的并且選擇了新的庫(kù),則這一工作在成本上將不會(huì)有很大區(qū)別,因?yàn)閷⑿枰俅沃匦戮帉懘a。
即使庫(kù)工作正常,如果他們決定應(yīng)該遷移到全新的庫(kù)(例如,包括在
JDK 1.4 中的庫(kù)),怎么辦?
去耦的好處
有沒(méi)有辦法使團(tuán)隊(duì)知道哪個(gè)實(shí)現(xiàn)最適合他們的需要呢(不僅現(xiàn)在能將來(lái)也能)?讓我們?cè)囍鴮ふ掖鸢浮?br/>
避免依賴任何特定的實(shí)現(xiàn)
前面的情形在軟件工程中十分常見(jiàn)。在有些情況中,這樣的情形會(huì)導(dǎo)致較大的投資和較長(zhǎng)的延期。當(dāng)不了解所有后果就作出決定而且決策制定人不太走運(yùn)或缺乏必需的經(jīng)驗(yàn)時(shí),就常常會(huì)發(fā)生這種情況。
可將該情形概括如下:
您需要某種提供者
您沒(méi)有選擇最佳提供者的客觀標(biāo)準(zhǔn)
您希望能用最低的成本來(lái)評(píng)估所有的待選項(xiàng)
所作的決定不應(yīng)將您束縛在所選的提供者上
這一問(wèn)題的解決方法是使代碼更加獨(dú)立于提供者。這引入了新的層 ?
同時(shí)去除客戶機(jī)和提供者的耦合的層。
在服務(wù)器端開(kāi)發(fā)中,很容易找到使用該方法的模式或體系結(jié)構(gòu)。下面引用一些示例:
對(duì)于
J2EE,您主要關(guān)注如何構(gòu)建應(yīng)用程序而不是應(yīng)用程序服務(wù)器的細(xì)節(jié)。
數(shù)據(jù)訪問(wèn)對(duì)象(Data Access
Object,DAO)模式隱藏了如何訪問(wèn)數(shù)據(jù)庫(kù)(或 LDAP 服務(wù)器、XML
文件等)的細(xì)節(jié)和復(fù)雜性,因?yàn)樗峁┝嗽L問(wèn)抽象持久存儲(chǔ)層的方法,而您則不需要在客戶機(jī)代碼中處理數(shù)據(jù)庫(kù)問(wèn)題(數(shù)據(jù)實(shí)際存儲(chǔ)在哪里)。這不是四人組(Gang of
Four,GoF)模式,而是 Sun 的 J2EE 最佳實(shí)踐的一部分。
在假想的開(kāi)發(fā)團(tuán)隊(duì)示例中,他們正在尋找這樣的層:
抽象所有正則表達(dá)式實(shí)現(xiàn)背后的概念。團(tuán)隊(duì)就可以著重學(xué)習(xí)和理解這些概念。他們所學(xué)的可以應(yīng)用到任何實(shí)現(xiàn)或版本。
支持新的庫(kù)且沒(méi)有副作用?;诓寮w系結(jié)構(gòu),動(dòng)態(tài)選擇執(zhí)行
regexp
模式的實(shí)際庫(kù),并且適配器不會(huì)被耦合。新庫(kù)僅會(huì)引入對(duì)新適配器的需要。
提供比較不同可選方案的方法。一個(gè)簡(jiǎn)單的基準(zhǔn)實(shí)用程序就可以顯示有趣的性能測(cè)量結(jié)果。如果對(duì)每個(gè)實(shí)現(xiàn)都執(zhí)行這樣的實(shí)用程序,團(tuán)隊(duì)就會(huì)獲得有價(jià)值的信息并能選擇最好的可選方案。
聽(tīng)起來(lái)不錯(cuò),但……
任何去耦方法都至少有一個(gè)缺點(diǎn):如果客戶機(jī)代碼僅需要一個(gè)實(shí)現(xiàn)所提供的特定功能,怎么辦?您不能使用任何其它實(shí)現(xiàn),因此您最終將代碼與該實(shí)現(xiàn)耦合。也許將來(lái)會(huì)在這方面有所改善,但您現(xiàn)在卻束手無(wú)策。
這樣的示例并不象您想的那樣少。在
regexp 領(lǐng)域中,一些編譯器選項(xiàng)僅被某些實(shí)現(xiàn)支持。如果您的客戶機(jī)代碼需要這種特定的功能,那么這個(gè)一般層是不夠的 ?
至少?gòu)钠駥?duì)它描述來(lái)看是不夠的。
附加層是否應(yīng)支持每個(gè)實(shí)現(xiàn)的所有非公共功能,并且如果選擇了不支持該實(shí)現(xiàn)的附加層則拋出異常?那可以是一種解決方案,但它并不支持僅定義公共抽象概念這一最初目標(biāo)。
有一個(gè)
GoF 模式非常適合這種情形:職責(zé)鏈(Chain of
Responsibility)。它在設(shè)計(jì)中引入了另一種間接方法。用這種方法,客戶機(jī)代碼向能處理其所發(fā)消息的實(shí)體列表發(fā)送消息或命令。列表項(xiàng)被組織成鏈,因此消息可按順序被處理并且在到達(dá)鏈尾之前被用掉。
在這種情況中,可以通過(guò)特殊類型的消息對(duì)僅被某些實(shí)現(xiàn)支持的特定功能建模。由鏈中的每一項(xiàng)根據(jù)其是否了解這些功能來(lái)決定是否將該消息傳給下一項(xiàng)。
定義一個(gè)公共
API
這里講述的 API 名為 RegexpPlugin。已將它設(shè)計(jì)成遵循剛剛討論的方法,并且它在 regexp
庫(kù)和使用該庫(kù)的代碼之間支持去耦。
RegexpPlugin
在以下示例中,我將總結(jié)一下使用具體實(shí)現(xiàn)(Jakarta Oro)和使用
RegexpPlugin API 之間的差別。
我從一個(gè)非常簡(jiǎn)單的 regexp 開(kāi)始:假定您必須要解析的文本只是人名。您接收的格式是象 John
A. Smith 這樣的內(nèi)容,而您只想獲取名字(John)。但您不知道單詞由什么分隔,是空格、換行符、制表符還是這些字符的組合。能處理這樣的輸入格式的
regexp 只是 .*s*(.*?)s+.*。我將一步一步地說(shuō)明如何使用該 regexp 來(lái)抽取信息。
第一部分是點(diǎn)號(hào)和星號(hào)字符
.*,它們?cè)谶@里表示任意數(shù)量的空格和 (.*?)
組之前的任何字符。第二部分比較引人注意(因?yàn)樗粓A括號(hào)括起來(lái))。問(wèn)號(hào)表示取第一個(gè)符合條件的項(xiàng)。
接下來(lái)的符號(hào)表示任意數(shù)量的空格、換行或制表符(s),但至少要有一個(gè)(+)。最后的點(diǎn)號(hào)和星號(hào)
.* 僅代表文本的余下部分(對(duì)它沒(méi)有興趣)。
因此,該 regexp 相當(dāng)于:取空格前的第一段文本。讓我們來(lái)編寫 Java
代碼。
上機(jī)實(shí)踐
要在 Java 代碼中使用正則表達(dá)式,通常需要完成以下七個(gè)步驟:
第 1 步:創(chuàng)建編譯器實(shí)例。如果使用
Jakarta Oro,則必須實(shí)例化 Perl5Compiler:
org.apache.oro.text.regex.Perl5Compiler
compiler =
new
org.apache.oro.text.regex.Perl5Compiler();
使用 RegexpPlugin
時(shí)的等同代碼是相似的:
org.acmsl.regexpplugin.Compiler compiler
=
org.acmsl.regexpplugin.RegexpManager.createCompiler();
但存在差異。正如前面提到的,該
API 對(duì)實(shí)際使用哪個(gè)具體實(shí)現(xiàn)加以隱藏。您可以選擇一個(gè)具體實(shí)現(xiàn)或保留缺省的 Jakarta Oro。如果所選的庫(kù)在運(yùn)行時(shí)不可用,則 RegexpPlugin
API 會(huì)嘗試用它的類名創(chuàng)建一個(gè)編譯器。如果該操作失敗,它會(huì)將異常發(fā)回 API 的客戶機(jī)。
假定您一直在使用 JDK 1.4 的內(nèi)置 regexp
類。那樣的話,包含始終不會(huì)使用的額外 jar 文件毫無(wú)意義。那就是為什么僅僅調(diào)用 createCompiler()
方法還不夠的原因。您需要管理這樣的異常:每當(dāng)所選的庫(kù)不存在時(shí)就會(huì)拋出該異常。因而必須更新示例:
try
{
org.acmsl.regexpplugin.Compiler
compiler
=
org.acmsl.regexpplugin.RegexpManager.createCompiler();
}
catch
(org.acmsl.regexpplugin.RegexpEngineNorFoundException
exception)
{
[..]
}
第 2 步:編譯 regexp 模式。將正則表達(dá)式本身編譯到
Pattern 對(duì)象中。
org.apache.oro.text.regex.Pattern pattern
=
compiler.compile(".*s*(.*?)s+.*",
Perl5Compiler.MULTILINE_MASK);
注:您必須轉(zhuǎn)義反斜杠()字符。
該模式對(duì)象代表以文本格式定義的正則表達(dá)式。請(qǐng)盡可能多地重用模式實(shí)例。然后,如果
regexp 是固定的(缺少任何可變部分,如“(.*?)Tom.*”),則模式應(yīng)是類中的靜態(tài)成員。
compile 方法適合用標(biāo)志(如
EXTENDED_MASK)來(lái)配置(請(qǐng)參閱參考資料以獲得更詳細(xì)的 regexp 教程)。但是,RegexpPlugin 并不允許隨意的標(biāo)志。受支持的標(biāo)志只有
case sensitivity 和
multiline,因?yàn)樗惺苤С值膸?kù)都可以處理它們。
編譯器實(shí)例有特定的特性來(lái)定義這些標(biāo)志:
compiler.setMultiline(true);
org.acmsl.regexpplugin.Pattern
pattern =
compiler.compile(".*s*(.*?)s+.*");
第 3 步:創(chuàng)建
Matcher 對(duì)象。在 Jakarta Oro
中,這一步非常簡(jiǎn)單:
org.apache.oro.text.regex.Perl5Matcher matcher =
new
org.apache.oro.text.regex.Perl5Matcher();
它之所以如此簡(jiǎn)單是因?yàn)樗恍枰獦?gòu)造任何信息。在后來(lái)的
regexp 中,它將變得具體?;旧?,RegexpPlugin 中的步驟差不多相似。您不必親自創(chuàng)建 matcher,而是可以將其代理給
RegexpManager 類:
org.acmsl.regexpplugin.Matcher matcher
=
org.acmsl.regexpplugin.RegexpManager.createMatcher();
區(qū)別和前面一樣,您需要處理
RegexpEngineNotFoundException。實(shí)際上,RegexpManager 需要為您所選的庫(kù)或缺省庫(kù)創(chuàng)建 matcher
適配器。如果這樣的類在運(yùn)行時(shí)不可用,它會(huì)拋出該異常。
第 4 步:評(píng)估正則表達(dá)式。matcher
對(duì)象需要解釋正則表達(dá)式并抽取所需的信息。這在一行代碼中完成:
if (matcher.contains("John A. Smith",
pattern))
{
如果輸入文本與正則表達(dá)式匹配,則該方法返回
true。隱含的副作用是,執(zhí)行該行代碼之后,matcher
對(duì)象包含在輸入文本中找到的第一個(gè)匹配項(xiàng)。接下來(lái)的一步演示如何實(shí)際獲取感興趣的信息。
通過(guò)使用 RegexpPlugin
API,在此時(shí)根本沒(méi)有任何不同。
第 5
步:檢索找到的第一個(gè)匹配項(xiàng)。這一簡(jiǎn)單的步驟僅用一行完成:
org.apache.oro.text.regex.MatchResult
matchResult = matcher.getMatch();
您可以聲明一個(gè)局部變量來(lái)存儲(chǔ)這樣的對(duì)象,該對(duì)象含有與
regexp
匹配的一段文本。在這兩種情況下,該步驟是相同的,除了變量聲明(因?yàn)橐粋€(gè)是另一個(gè)的適配器):
org.acmsl.regexpplugin.MatchResult
matchResult =
matcher.getMatch();
第 6 步:獲取感興趣的
group。您可以使用兩種方法:
具體庫(kù)
RegexpPlugin API
因?yàn)槟?regexp 是
.*s*(.*?)s+.*,所以您只有一個(gè)組:(.*?)
MatchResult
對(duì)象包含已排序列表中的所有組。您只需要知道要獲取的組的位置。因?yàn)樵撌纠挥幸粋€(gè)組,所以毫無(wú)疑問(wèn):
String name =
matchResult.group(1);
[..]
}
變量 name 現(xiàn)在包含文本
John,那正是您需要的。
第 7
步:如果需要,則重復(fù)該過(guò)程。如果您需要的信息可多次出現(xiàn),而您想分析所有出現(xiàn)的信息而不只是第一個(gè),那么您只需循環(huán)執(zhí)行第 5 步到第 7 步,直到不滿足第 4
步中描述的條件為止:
while (matcher.contains("John A. Smith",
pattern))
{
映射
除了編寫公共抽象 API,主要的工作實(shí)際上是實(shí)現(xiàn) Java 環(huán)境中某些已存在的
regexp
引擎的適配器。
以下各表提供了對(duì)如何從一個(gè)庫(kù)遷移至另一個(gè)庫(kù)的詳細(xì)描述。有些情況中,概念明顯不同。也有些情況中,卻不是那么明顯。
Regexp
概念 GNU Regexp 1.2
編譯器 gnu.regexp.RE
模式 gnu.regexp.RE
匹配程序
gnu.regexp.REMatchEnumeration
gnu.regexp.RE
匹配結(jié)果 gnu.regexp.REMatch
畸形模式異常 gnu.regexp.REException
Regexp 概念 Jakarta Oro
2.0.6
編譯器 org.apache.oro.text.regex.Perl5Compiler
模式
org.apache.oro.text.regex.Pattern
匹配程序
org.apache.oro.text.regex.Perl5Matcher
匹配結(jié)果
org.apache.oro.text.regex.MatchResult
畸形模式異常
org.[..].regex.MalformedPatternException
Regexp 概念 Jakarta
Regexp 1.3
編譯器 org.apache.regexp.RE
org.apache.regexp.RECompiler
org.apache.regexp.REProgram
模式 org.apache.regexp.REProgram
org.apache.regexp.RE
匹配程序 org.apache.regexp.RE
org.apache.regexp.REProgram
匹配結(jié)果 org.apache.regexp.RE
畸形模式異常
org.apache.regexp.RESyntaxException
Regexp 概念 JDK 1.4 regex
包
編譯器 java.util.regex.Pattern
模式 java.util.regex.Pattern
匹配程序
java.util.regex.Matcher
匹配結(jié)果 java.util.regex.Matcher
畸形模式異常
java.util.regex.PatternSyntaxException
基準(zhǔn)
該 API
較顯著的用法之一是用來(lái)比較實(shí)現(xiàn)、測(cè)量性能、對(duì) Perl5 語(yǔ)法的兼容性或其它標(biāo)準(zhǔn)之間的差異。
為這些測(cè)試開(kāi)發(fā)的基準(zhǔn)實(shí)用程序使用 HTML
解析器來(lái)處理 Web 內(nèi)容,更新有關(guān)鏈接、表單和表等元素的信息。但是,重要的是解析邏輯用正則表達(dá)式來(lái)表示,因此會(huì)通過(guò) RegexpPlugin API
實(shí)現(xiàn)。
基準(zhǔn)測(cè)試包括對(duì)非常簡(jiǎn)單的 HTML 頁(yè)面解析 10000 次。結(jié)果在下表中顯示。
Regexp 庫(kù)
Benchmark 結(jié)果(秒)
Jakarta Oro 2.0.6 130,71
Jakarta Regexp 1.2 23,261
GNU Regexp 1.1.4 1,966.939
JDK1.4 33,222
您可以用多種方法在實(shí)際應(yīng)用程序中改進(jìn)性能。最重要的是,當(dāng)您使用 regexp
庫(kù)時(shí),不需要每次都編譯模式,而是編譯它們并重用各自的實(shí)例。但是,如果 regexp
本身不固定,則不能忽略編譯過(guò)程。
因?yàn)榛鶞?zhǔn)需要在實(shí)現(xiàn)之間切換以比較性能,所以必須始終廢棄已編譯模式以避免庫(kù)之間的交互。但是,正如您所見(jiàn),大多數(shù)已評(píng)估的庫(kù)有相似的響應(yīng)時(shí)間,盡管更詳細(xì)的基準(zhǔn)能讓我們更好的理解每個(gè)庫(kù)在不同環(huán)境下的行為。
感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“如何構(gòu)建用于正則表達(dá)式的抽象Java API”這篇文章對(duì)大家有幫助,同時(shí)也希望大家多多支持創(chuàng)新互聯(lián),關(guān)注創(chuàng)新互聯(lián)-成都網(wǎng)站建設(shè)公司行業(yè)資訊頻道,更多相關(guān)知識(shí)等著你來(lái)學(xué)習(xí)!
分享文章:如何構(gòu)建用于正則表達(dá)式的抽象JavaAPI-創(chuàng)新互聯(lián)
網(wǎng)頁(yè)路徑:http://jinyejixie.com/article20/depijo.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站策劃、網(wǎng)站收錄、小程序開(kāi)發(fā)、Google、品牌網(wǎng)站設(shè)計(jì)、搜索引擎優(yōu)化
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容