一個需求,在一段字符串中識別是否有郵箱. 因而寫了一個正則表達式html
private final static Pattern REGEX_EMAIL_PATTEN = Pattern.compile("([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+([a-zA-Z]*)?"); public static String parseEmail(String str){ if (StringUtil.isBlank(str)) { return null; } Matcher m = REGEX_EMAIL_PATTEN.matcher(str); if (m.find()) { int start = m.start(); int end = m.end(); return str.substring(start,end); } return null; }
而後作了幾個簡單的測試,嗯,都經過了.最終上線。 沒多久運維通知服務cpu load很高,堆棧信息拉出來一看... 堆棧信息見本文末尾. 定位到了 parseEmail(String str) 這個方法.根據時間定位,拉取線上請求日誌,發現方法入參是這樣的:java
http://mail.1etrip.com/OK/controller/journey/apv/toApprove.html?param=20855793060AD450E779B0CAC0CEB570B571740F86C17F660B12EDC2ED0FFDCA 2018-10-19
這樣的正則表達式
www.benke168.com/Admin/Wechatlogin/index/U/cbmeVnwkYeW1l6y8aWQ9NTMyL3Bhc3N3b3JkPWUxMGFkYzM5NDliYTU5YWJiZTU2ZTA1N2YyMGY4ODNlL3VzZ XJuYW1lPTEzOTUyNDI1NTk1.html
本地運行一下,果真跑不出來結果. 搜索引擎查查看.算法
Java regex正則表達式相似死循環問題,詳見:http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6988218 這個問題實際上是因爲正則表達式很複雜時,java regex複雜度太高(複雜度成指數級),致使相似死循環的問題。less
那麼這個問題怎麼解決...修改正則表達式,簡單粗暴, 匹配 x@y.z
這樣的字符串就行了。x
長度 1,y
的長度1~30 ,z
的長度1~10.運維
private final static Pattern REGEX_EMAIL_PATTEN = Pattern.compile("([a-z0-9A-Z])@([a-z0-9A-Z]){1,30}\\.([a-z0-9A-Z]){1,10}");
用9月20日當天1000w條歷史數據作單元測試. 最終結果:耗時大於10毫秒的有26條數據.最大的有36毫秒。 耗時最大的數據是這樣的:工具
1 172.25.50.202-DB: F:\\backup\\236\\DB\\2018/9/19 19:39:29本次備份完成文件: CheckOperationLastTime_backup_2018_09_03_092647_0992470.bak CheckOperationLastTime_backup_2018_09_03_130001_6685014.bak CheckOperationLastTime_backup_2018_09_19_130001_5866855.bak office_wx_backup_2018_09_03_092647_1178543.bak office_wx_backup_2018_09_03_130001_6841585.bak office_wx_backup_2018_09_19_130001_6023100.bak UMDataBase_backup_2018_09_03_092647_1178543.bak UMDataBase_backup_2018_09_03_130001_6997541.bak UMDataBase_backup_2018_09_19_130001_6023100.bak WXGZ_backup_2018_09_03_092647_1647407.bak WXGZ_backup_2018_09_03_130001_7466590.bak WXGZ_backup_2018_09_19_130001_6491867.bak WX_CAM_backup_2018_09_03_092647_1334902.bak WX_CAM_backup_2018_09_03_130001_6997541.bak WX_CAM_backup_2018_09_19_130001_6179345.bak WX_GDZC_backup_2018_09_03_092647_1334902.bak WX_GDZC_backup_2018_09_03_130001_7153797.bak WX_GDZC_backup_2018_09_19_130001_6179345.bak WX_GWGL_backup_2018_09_03_092647_1491175.bak WX_GWGL_backup_2018_09_03_130001_7153797.bak WX_GWGL_backup_2018_09_19_130001_6335584.bak WX_SWTJ_backup_2018_09_03_092647_1491175.bak WX_SWTJ_backup_2018_09_03_130001_7310320.bak WX_SWTJ_backup_2018_09_19_130001_6335584.bak WX_UMDataBase_backup_2018_09_03_092647_1647407.bak WX_UMDataBase_backup_2018_09_03_130001_7310320.bak WX_UMDataBase_backup_2018_09_19_130001_6491867.bak
emmmm...基本符合預期了.oop
參考:正則表達式匹配原理單元測試
引擎 | 區別點 |
---|---|
DFA<br> Deterministic finite automaton 肯定型有窮自動機 | DFA引擎它們不要求回溯(並所以它們永遠不測試相同的字符兩次),因此匹配速度快!DFA引擎還能夠匹配最長的可能的字符串。不過DFA引擎只包含有限的狀態,因此它不能匹配具備反向引用的模式,還不能夠捕獲子表達式。表明性有:awk,egrep,flex,lex,MySQL,Procmail |
NFA<br>Non-deterministic finite automaton 非肯定型有窮自動機,又分爲傳統NFA,Posix NFA | 傳統的NFA引擎運行所謂的「貪婪的」匹配回溯算法(longest-leftmost),以指定順序測試正則表達式的全部可能的擴展並接受第一個匹配項。傳統的NFA回溯能夠訪問徹底相同的狀態屢次,在最壞狀況下,它的執行速度可能很是慢,但它支持子匹配。表明性有:GNU Emacs,Java,ergp,less,more,.NET語言,PCRE library,Perl,PHP,Python,Ruby,sed,vi等,通常高級語言都採用該模式。 |
DFA以字符串字符,逐個在正則表達式匹配查找,而NFA以正則表達式爲主,在字符串中逐一查找。儘管速度慢,可是對操做者來講更簡單,所以應用更普遍。測試
對於字符串「DEF」而言,包括D、E、F三個字符和 0、一、二、3 四個數字位置:0D1E2F3,對於正則表達式而言全部源字符串,都有字符和位置。正則表達式會從0號位置,逐個去匹配的。
正則表達式匹配過程當中,若是子表達式匹配到的是字符內容,而非位置,並被保存到最終的匹配結果中,那麼就認爲這個子表達式是佔有字符的;若是子表達式匹配的僅僅是位置,或者匹配的內容並不保存到最終的匹配結果中,那麼就認爲這個子表達式是零寬度的。佔有字符是互斥的,零寬度是非互斥的。也就是一個字符,同一時間只能由一個子表達式匹配,而一個位置,卻能夠同時由多個零寬度的子表達式匹配。常見零寬字符有:^,(?=)等
regex101.zip 下載
慎用 "+,*",用 {1,30}
這樣的替代. +
匹配1到無窮次. *
匹配0到無窮次. {1,30}
匹配1到30次 集合本次案例分析:
([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+([a-zA-Z]*)?
致使匹配長字符串是匹配複雜度指數級增長
[7] Busy(11.4%) thread(23901/0x5d5d) stack of java process(23087) under user(admin): "catalina-exec-1" #381 daemon prio=5 os_prio=0 tid=0x00007facd4088800 nid=0x5d5d runnable [0x00007fad1079b000] java.lang.Thread.State: RUNNABLE at java.util.regex.Pattern$Curly.match(Pattern.java:4227) at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658) at java.util.regex.Pattern$Loop.match(Pattern.java:4785) at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717) at java.util.regex.Pattern$Ques.match(Pattern.java:4182) at java.util.regex.Pattern$Curly.match0(Pattern.java:4272) at java.util.regex.Pattern$Curly.match(Pattern.java:4234) at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658) at java.util.regex.Pattern$Loop.match(Pattern.java:4785) at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717) at java.util.regex.Pattern$Ques.match(Pattern.java:4182) at java.util.regex.Pattern$Curly.match0(Pattern.java:4272) at java.util.regex.Pattern$Curly.match(Pattern.java:4234) at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658) at java.util.regex.Pattern$Loop.match(Pattern.java:4785) at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717) at java.util.regex.Pattern$Ques.match(Pattern.java:4182) at java.util.regex.Pattern$Curly.match0(Pattern.java:4272) at java.util.regex.Pattern$Curly.match(Pattern.java:4234) at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658) .... at java.util.regex.Pattern$Curly.match0(Pattern.java:4272) at java.util.regex.Pattern$Curly.match(Pattern.java:4234) at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658) at java.util.regex.Pattern$Loop.matchInit(Pattern.java:4801) at java.util.regex.Pattern$Prolog.match(Pattern.java:4741) at java.util.regex.Pattern$Start.match(Pattern.java:3461) at java.util.regex.Matcher.search(Matcher.java:1248) at java.util.regex.Matcher.find(Matcher.java:637) at com.weike.commons.util.RegexUtil.parseEmail(RegexUtil.java:260) at com.taovip.v2.action.SmsAction.lambda$needSmsRecheck$5(SmsAction.java:4670) at com.taovip.v2.action.SmsAction$$Lambda$10/40486007.test(Unknown Source) at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:174) at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:175) at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:175) at java.util.HashMap$ValueSpliterator.tryAdvance(HashMap.java:1633) at java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:126) at java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:498) at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:485) at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471) at java.util.stream.MatchOps$MatchOp.evaluateSequential(MatchOps.java:230) at java.util.stream.MatchOps$MatchOp.evaluateSequential(MatchOps.java:196) at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) at java.util.stream.ReferencePipeline.anyMatch(ReferencePipeline.java:449)