正則表達式,又稱正規表示法、常規表示法。(英語:Regular Expression,在代碼中常簡寫爲regex、regexp或RE),計算機科學的一個概念。正則表達式使用單個字符串來描述、匹配一系列符合某個句法規則。在不少文本編輯器裏,正則表達式一般被用來檢索、替換那些符合某個模式的文本。git
typedef NS_OPTIONS(NSUInteger, NSRegularExpressionOptions) {
NSRegularExpressionCaseInsensitive = 1 << 0, // 不區分大小寫的
NSRegularExpressionAllowCommentsAndWhitespace = 1 << 1, // 忽略空格和# (註釋符)
NSRegularExpressionIgnoreMetacharacters = 1 << 2, // 總體化
NSRegularExpressionDotMatchesLineSeparators = 1 << 3, // 匹配任何字符,包括行分隔符
NSRegularExpressionAnchorsMatchLines = 1 << 4, // 容許^和$在匹配的開始和結束行
NSRegularExpressionUseUnixLineSeparators = 1 << 5, // (查找範圍爲整個的話無效)
NSRegularExpressionUseUnicodeWordBoundaries = 1 << 6 // (查找範圍爲整個的話無效)
};
複製代碼
typedef NS_OPTIONS(NSUInteger, NSMatchingOptions) {
NSMatchingReportProgress = 1 << 0, //找到最長的匹配字符串後調用block回調
NSMatchingReportCompletion = 1 << 1, //找到任何一個匹配串後都回調一次block
NSMatchingAnchored = 1 << 2, //從匹配範圍的開始處進行匹配
NSMatchingWithTransparentBounds = 1 << 3, //容許匹配的範圍超出設置的範圍
NSMatchingWithoutAnchoringBounds = 1 << 4 //禁止^和$自動匹配行仍是和結束
};
複製代碼
此枚舉值只在block方法中用到正則表達式
typedef NS_OPTIONS(NSUInteger, NSMatchingFlags) {
NSMatchingProgress = 1 << 0, //匹配到最長串是被設置
NSMatchingCompleted = 1 << 1, //所有分配完成後被設置
NSMatchingHitEnd = 1 << 2, //匹配到設置範圍的末尾時被設置
NSMatchingRequiredEnd = 1 << 3, //當前匹配到的字符串在匹配範圍的末尾時被設置
NSMatchingInternalError = 1 << 4 //因爲錯誤致使的匹配失敗時被設置
};
複製代碼
1. 返回全部匹配結果的集合(適合,從一段字符串中提取咱們想要匹配的全部數據)
* - (NSArray *)matchesInString:(NSString *)string options:(NSMatchingOptions)options range:(NSRange)range;
2. 返回正確匹配的個數(經過等於0,來驗證郵箱,電話什麼的,代替NSPredicate)
* - (NSUInteger)numberOfMatchesInString:(NSString *)string options:(NSMatchingOptions)options range:(NSRange)range;
3. 返回第一個匹配的結果。注意,匹配的結果保存在 NSTextCheckingResult 類型中
* - (NSTextCheckingResult *)firstMatchInString:(NSString *)string options:(NSMatchingOptions)options range:(NSRange)range;
4. 返回第一個正確匹配結果字符串的NSRange
* - (NSRange)rangeOfFirstMatchInString:(NSString *)string options:(NSMatchingOptions)options range:(NSRange)range;
5. block方法
* - (void)enumerateMatchesInString:(NSString *)string options:(NSMatchingOptions)options range:(NSRange)range usingBlock:(void (^)(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop))block;
複製代碼
替換方法算法
- (NSString *)stringByReplacingMatchesInString:(NSString *)string options:(NSMatchingOptions)options range:(NSRange)range withTemplate:(NSString *)templ;
- (NSUInteger)replaceMatchesInString:(NSMutableString *)string options:(NSMatchingOptions)options range:(NSRange)range withTemplate:(NSString *)templ;
- (NSString *)replacementStringForResult:(NSTextCheckingResult *)result inString:(NSString *)string offset:(NSInteger)offset template:(NSString *)templ;
複製代碼
字符串的替換swift
let test = "sdgreihen一個安靜的晚上jlosd一個"
let regex = "一個"
let RE = try NSRegularExpression(pattern: regex, options: .caseInsensitive)
let modified = RE.stringByReplacingMatches(in: test, options: .reportProgress, range: NSRange(location: 0, length: test.count), withTemplate: "是的")
複製代碼
打印數組
sdgreihen是的安靜的晚上jlosd是的
複製代碼
字符串的匹配bash
let test = "sdgreihendfjbhiidfjdbjb"
let regex = "jb"
let RE = try NSRegularExpression(pattern: regex, options: .caseInsensitive)
let matchs = RE.matches(in: test, options: .reportProgress, range: NSRange(location: 0, length: test.count))
print(matchs.count)
複製代碼
可是有的時候,咱們須要匹配的不是準確的字符串,是模糊匹配,像檢測手機號,郵箱等等app
let test = "1832321108"
let regex = "^1[0-9]{10}$"
let RE = try NSRegularExpression(pattern: regex, options: .caseInsensitive)
let matchs = RE.matches(in: test, options: .reportProgress, range: NSRange(location: 0, length: test.count))
print(matchs.count)
複製代碼
咱們接下來學習一下正則表達式的規則編輯器
咱們先來寫一個方便測試的工具ide
/// 正則匹配
///
/// - Parameters:
/// - regex: 匹配規則
/// - validateString: 匹配對test象
/// - Returns: 返回結果
func RegularExpression (regex:String,validateString:String) -> [String]{
do {
let regex: NSRegularExpression = try NSRegularExpression(pattern: regex, options: [])
let matches = regex.matches(in: validateString, options: [], range: NSMakeRange(0, validateString.count))
var data:[String] = Array()
for item in matches {
let string = (validateString as NSString).substring(with: item.range)
data.append(string)
}
return data
}
catch {
return []
}
}
/// 字符串的替換
///
/// - Parameters:
/// - validateString: 匹配對象
/// - regex: 匹配規則
/// - content: 替換內容
/// - Returns: 結果
func replace(validateString:String,regex:String,content:String) -> String {
do {
let RE = try NSRegularExpression(pattern: regex, options: .caseInsensitive)
let modified = RE.stringByReplacingMatches(in: validateString, options: .reportProgress, range: NSRange(location: 0, length: validateString.count), withTemplate: content)
return modified
}
catch {
return validateString
}
}
複製代碼
本章節按照下面順序研究函數
正則表達式是匹配模式,要麼匹配字符,要麼匹配位置
若是正則只有精確匹配是沒多大意義的,好比hello
,也只能匹配字符串中的hello
這個子串
正則表達式之因此強大,是由於其能實現模糊匹配。
而模糊匹配,有兩個方向上的「模糊」:橫向模糊和縱向模糊。
1.一、橫向模糊匹配
橫向模糊指的是,一個正則可匹配的字符串的長度不是固定的,能夠是多種狀況的。
其實現的方式是使用量詞。譬如{m,n}
,表示連續出現最少m
次,最多n
次。
好比ab{2,5}c
表示匹配這樣一個字符串:第一個字符是a
,接下來是2到5個字符b
,最後是字符c
。測試以下:
let regex = "ab{2,5}c"
let validate = "abc abbc abbbc abbbbc abbbbbc abbbbbbc"
let result = RegularExpression(regex: regex, validateString: validate)
//打印結果
["abbc", "abbbc", "abbbbc", "abbbbbc"]
複製代碼
1.二、縱向模糊匹配
縱向模糊指的是,一個正則匹配的字符串,具體到某一位字符時,它能夠不是某個肯定的字符,能夠有多種可能。
其實現的方式是使用字符組。譬如[abc]
,表示該字符是能夠字符a
、b
、c
中的任何一個。
好比a[123]b
能夠匹配以下三種字符串:a1b
、a2b
、a3b
。測試以下
let regex = "a[123]b"
let validate = "a0b a1b a2b a3b a4b"
let result = RegularExpression(regex: regex, validateString: validate)
print(result)
//打印結果
["a1b", "a2b", "a3b"]
複製代碼
須要強調的是,雖叫字符組(字符類),但只是其中一個字符。例如[abc]
,表示匹配一個字符,它能夠是a
、b
、c
之一。
一、範圍表示法:若是字符組裏的字符特別多的話,可使用範圍表示法。好比[123456abcdefGHIJKLM]
,能夠寫成[1-6a-fG-M]
。用連字符-
來省略和簡寫
二、 排除字符組:縱向模糊匹配,還有一種情形就是,某位字符能夠是任何東西,但就不能是"a"、"b"、"c"
。此時就是排除字符組(反義字符組)的概念。例如[^abc]
,表示是一個除"a"、"b"、"c"
以外的任意一個字符。字符組的第一位放^(脫字符)
,表示求反的概念。
2.一、常見的簡寫形式
有了字符組的概念後,一些常見的符號咱們也就理解了。由於它們都是系統自帶的簡寫形式
正則表達式 | 匹配區間 | 記憶方式 |
---|---|---|
\d |
[0-9]表示是一位數字 | 其英文是digit(數字) |
\D |
[^0-9]表示除數字外的任意字符 | |
\w |
[0-9a-zA-Z_]表示數字、大小寫字母和下劃線 | w是word的簡寫,也稱單詞字符 |
\W |
[^0-9a-zA-Z_] | 非單詞字符 |
\s |
[ \t\v\n\r\f]表示空白符,包括空格、水平製表符、垂直製表符、換行符、回車符、換頁符 | s是space character的首字母 |
\S |
[^ \t\v\n\r\f] | 非空白符 |
. |
[^\n\r\u2028\u2029]通配符,表示幾乎任意字符。換行符、回車符、行分隔符和段分隔符除外 |
2.二、量詞
量詞也稱重複。掌握{m,n}
的準確含義後,只須要記住一些簡寫形式。
{m,}
表示至少出現m次{m}
等價於{m,m}
,表示出現m次?
等價於{0,1}
,表示出現或者不出現。記憶方式:問號的意思表示,有嗎?+
等價於{1,}
,表示出現至少一次。記憶方式:加號是追加的意思,得先有一個,而後才考慮追加。*
等價於{0,}
,表示出現任意次,有可能不出現。記憶方式:看看天上的星星,可能一顆沒有,可能零散有幾顆,可能數也數不過來。貪婪匹配:它會盡量多的匹配。你能給我6個,我就要5個。你能給我3個,我就3要個。反正只要在能力範圍內,越多越好。
惰性匹配:就是儘量少的匹配:
let regex = "\\d{2,5}"
let validate = "123 1234 12345 123456"
let result = RegularExpression(regex: regex, validateString: validate)
print(result)
//打印結果
["123", "1234", "12345", "12345"]
---------------------------------
let regex = "\\d{2,5}?"
let validate = "123 1234 12345 123456"
let result = RegularExpression(regex: regex, validateString: validate)
print(result)
//打印結果
["12", "12", "34", "12", "34", "12", "34", "56"]
複製代碼
經過在量詞後面加個問號就能實現惰性匹配,所以全部惰性匹配情形以下:
{m,n}?
{m,}?
??
+?
*?
2.三、多選分支
一個模式能夠實現橫向和縱向模糊匹配。而多選分支能夠支持多個子模式任選其一。
具體形式以下:(p1|p2|p3)
,其中p一、p2和p3是子模式,用|
(管道符)分隔,表示其中任何之一
例如要匹配good
和nice
可使用good|nice
。測試以下:
let regex = "good|nice"
let validate = "good idea, nice try."
let result = RegularExpression(regex: regex, validateString: validate)
print(result)
//打印結果
["good", "nice"]
複製代碼
但有個事實咱們應該注意,好比我用 good|goodbye
,去匹配goodbye
字符串時,結果是good
:
let regex = "good|goodbye"
let validate = "goodbye"
let result = RegularExpression(regex: regex, validateString: validate)
print(result)
//打印結果
["good"]
複製代碼
而把正則改爲goodbye|good
,結果是
let regex = "goodbye|good"
let validate = "goodbye"
let result = RegularExpression(regex: regex, validateString: validate)
print(result)
//打印結果
["goodbye"]
複製代碼
也就是說,分支結構也是惰性的,即當前面的匹配上了,後面的就再也不嘗試了。
匹配攻略主要是從如下幾個方面介紹
位置是相鄰字符之間的位置。好比,下圖中箭頭所指的地方
2.一、^
和$
^
(脫字符)匹配開頭,在多行匹配中匹配行開頭$
(美圓符號)匹配結尾,在多行匹配中匹配行結尾。好比咱們把字符串的開頭和結尾用"#"替換
let regex = "^|$"
let validate = "hello"
let result = replace(validateString: validate, regex: regex, content: "#")
print(result)
//打印結果
#hello#
複製代碼
2.二、 \b
和\B
\b
是單詞邊界,具體就是\w
和\W
之間的位置,也包括\w
和^
之間的位置,也包括\w
和$
之間的位置。
let regex = "\\b"
let validate = "[JS] Lesson_01.mp4"
let result = replace(validateString: validate, regex: regex, content: "#")
print(result)
//[#JS#] #Lesson_01#.#mp4#
複製代碼
首先,咱們知道,\w
是字符組[0-9a-zA-Z_]
的簡寫形式,即\w
是字母數字或者下劃線的中任何一個字符。而\W
是排除字符組[^0-9a-zA-Z_]
的簡寫形式,即\W
是\w
之外的任何一個字符。
此時咱們能夠看看"[#JS#] #Lesson_01#.#mp4#"中的每個"#",是怎麼來的。
\B
就是\b
的反面的意思,非單詞邊界。例如在字符串中全部位置中,扣掉\b
,剩下的都是\B
的。
let regex = "\\B"
let validate = "[JS] Lesson_01.mp4"
let result = replace(validateString: validate, regex: regex, content: "#")
print(result)
//#[J#S]# L#e#s#s#o#n#_#0#1.m#p#4
複製代碼
2.三、(?=p)
和(?!p)
(?=p)
,其中p
是一個子模式,即p
前面的位置
好比(?=l)
,表示l
字符前面的位置,例如:
let regex = "(?=l)"
let validate = "hello"
let result = replace(validateString: validate, regex: regex, content: "#")
print(result)
//he#l#lo
複製代碼
而(?!p)
就是(?=p)
的反面意思
let regex = "(?!l)"
let validate = "hello"
let result = replace(validateString: validate, regex: regex, content: "#")
print(result)
複製代碼
數字的千位分隔符表示法
好比把"12345678",變成"12 345 678"。
let regex = "(?=(\\d{3})+$)"
let validate = "12345678"
let result = replace(validateString: validate, regex: regex, content: " ")
print(result)
//12 345 678
複製代碼
思路:
空格
,使用(?=\d{3}$)
空格
,全部可使用量詞+
,最終就是(?=(\\d{3})+$)
可是當咱們在對123456789
切分時,發現最前面多一個空格,此時咱們須要不設置開頭,可使用(?!^)
。爲了看出來效果,咱們使用#
來代替空格
let regex = "(?=(\\d{3})+$)"
let validate = "123456789"
let result = replace(validateString: validate, regex: regex, content: "#")
print(result)
//#123#456#789
let regex = "(?!^)(?=(\\d{3})+$)"
let validate = "123456789"
let result = replace(validateString: validate, regex: regex, content: "#")
print(result)
//123#456#789
複製代碼
驗證密碼問題
密碼長度6-12位,由數字、小寫字符和大寫字母組成,但必須至少包括2種字符。
針對這個問題咱們能夠分步實現
一、密碼長度6-12位,由數字、小寫字符和大寫字母組成。正則表達式爲^[0-9A-Za-z]{6,12}$
二、判斷是否包含有某一種字符。要求的必須包含數字,正則表達式爲(?=.*[0-9])
。(?=.*[0-9])
表示該位置後面的字符匹配.*[0-9]
,有任何多個任意字符,後面再跟個數字。翻譯成大白話,就是接下來的字符,必須包含個數字。
三、同時包含具體兩種字符,好比同時包含數字和小寫字母,正則表達式爲(?=.*[0-9])(?=.*[a-z])
四、完整的正則表達式爲(?=.*[0-9])(?=.*[a-z])^[0-9A-Za-z]{6,12}$
無論哪門語言中都有括號。正則表達式也是一門語言,而括號的存在使這門語言更爲強大。
內容包括:
分組
咱們知道a+
匹配連續出現的「a」,而要匹配連續出現的「ab」時,須要使用(ab)+
。
其中括號是提供分組功能,使量詞+
做用於ab
這個總體,測試以下
let regex = "(ab)+"
let validate = "ababa abbb ababab"
let result = RegularExpression(regex: regex, validateString: validate)
print(result)
//["abab", "ab", "ababab"]
複製代碼
分支結構 而在多選分支結構(p1|p2)
中,此處括號的做用也是不言而喻的,提供了子表達式的全部可能。
要匹配以下的字符串
I love Swift I love Regular Expression
測試以下
let regex = "^I love (Swift|Regular Expression)$"
let validate = "I love Swift"
let result = RegularExpression(regex: regex, validateString: validate)
print(result)
//["I love Swift"]
複製代碼
這個功能好像swift不支持,有可能我沒找到相應方法,有找到相關支持方法的歡迎提出來。
這是括號一個重要的做用,有了它,咱們就能夠進行數據提取,以及更強大的替換操做。
而要使用它帶來的好處,必須配合使用實現環境的API。
以日期爲例。假設格式是yyyy-mm-dd的,咱們能夠先寫一個簡單的正則
var regex = /\d{4}-\d{2}-\d{2}/;
複製代碼
而後再修改爲括號版的
var regex = /(\d{4})-(\d{2})-(\d{2})/;
複製代碼
好比提取出年、月、日,能夠這麼作:
var regex = /(\d{4})-(\d{2})-(\d{2})/;
var string = "2017-06-12";
console.log( string.match(regex) );
// => ["2017-06-12", "2017", "06", "12", index: 0, input: "2017-06-12"]
複製代碼
match
返回的一個數組,第一個元素是總體匹配結果,而後是各個分組(括號裏)匹配的內容,而後是匹配下標,最後是輸入的文本。(注意:若是正則是否有修飾符g,match返回的數組格式是不同的)。
另外也可使用正則對象的exec
方法
var regex = /(\d{4})-(\d{2})-(\d{2})/;
var string = "2017-06-12";
console.log( regex.exec(string) );
// => ["2017-06-12", "2017", "06", "12", index: 0, input: "2017-06-12"]
複製代碼
同時,也可使用構造函數的全局屬性$1
至$9
來獲取:
var regex = /(\d{4})-(\d{2})-(\d{2})/;
var string = "2017-06-12";
regex.test(string); // 正則操做便可,例如
//regex.exec(string);
//string.match(regex);
console.log(RegExp.$1); // "2017"
console.log(RegExp.$2); // "06"
console.log(RegExp.$3); // "12"
複製代碼
好比,想把yyyy-mm-dd格式,替換成mm/dd/yyyy怎麼作?
var regex = /(\d{4})-(\d{2})-(\d{2})/;
var string = "2017-06-12";
var result = string.replace(regex, "$2/$3/$1");
console.log(result);
// => "06/12/2017"
複製代碼
除了使用相應API來引用分組,也能夠在正則自己裏引用分組。但只能引用以前出現的分組,即反向引用。
仍是以日期爲例。
好比要寫一個正則支持匹配以下三種格式
2016-06-12 2016/06/12 2016.06.12
最早可能想到的正則是:
var regex = /\d{4}(-|\/|\.)\d{2}(-|\/|\.)\d{2}/;
var string1 = "2017-06-12";
var string2 = "2017/06/12";
var string3 = "2017.06.12";
var string4 = "2016-06/12";
console.log( regex.test(string1) ); // true
console.log( regex.test(string2) ); // true
console.log( regex.test(string3) ); // true
console.log( regex.test(string4) ); // true
複製代碼
其中/和.須要轉義。雖然匹配了要求的狀況,但也匹配"2016-06/12"這樣的數據。
假設咱們想要求分割符先後一致怎麼辦?此時須要使用反向引用:
var regex = /\d{4}(-|\/|\.)\d{2}\1\d{2}/;
var string1 = "2017-06-12";
var string2 = "2017/06/12";
var string3 = "2017.06.12";
var string4 = "2016-06/12";
console.log( regex.test(string1) ); // true
console.log( regex.test(string2) ); // true
console.log( regex.test(string3) ); // true
console.log( regex.test(string4) ); // false
複製代碼
注意裏面的\1
,表示的引用以前的那個分組(-|\/|\.)
。無論它匹配到什麼(好比-),\1
都匹配那個一樣的具體某個字符。
咱們知道了\1
的含義後,那麼\2
和\3
的概念也就理解了,即分別指代第二個和第三個分組
括號嵌套怎麼辦
以左括號(開括號)爲準。好比:
var regex = /^((\d)(\d(\d)))\1\2\3\4$/;
var string = "1231231233";
console.log( regex.test(string) ); // true
console.log( RegExp.$1 ); // 123
console.log( RegExp.$2 ); // 1
console.log( RegExp.$3 ); // 23
console.log( RegExp.$4 ); // 3
複製代碼
咱們能夠看看這個正則匹配模式:
\1
,是第一個分組內容,那麼看第一個開括號對應的分組是什麼,是123,\2
,找到第2個開括號,對應的分組,匹配的內容是1,\3
,找到第3個開括號,對應的分組,匹配的內容是23,\4
,找到第3個開括號,對應的分組,匹配的內容是3。以前文中出現的分組,都會捕獲它們匹配到的數據,以便後續引用,所以也稱他們是捕獲型分組。
若是隻想要括號最原始的功能,但不會引用它,即,既不在API裏引用,也不在正則裏反向引用。此時可使用非捕獲分組(?:p),例如本文第一個例子能夠修改成:
var regex = /(?:ab)+/g;
var string = "ababa abbb ababab";
console.log( string.match(regex) );
// => ["abab", "ab", "ababab"]
複製代碼
學習正則表達式,是須要懂點兒匹配原理的。
而研究匹配原理時,有兩個字出現的頻率比較高:「回溯」。
聽起來挺高大上,確實還有不少人對此不明不白的。
所以,本章就簡單扼要地說清楚回溯究竟是什麼東西。
內容包括:
假設咱們的正則是ab{1,3}c
,其可視化形式是:
而當目標字符串是abbbc
時,就沒有所謂的「回溯」。其匹配過程是:
其中子表達式b{1,3}
表示「b」字符連續出現1到3次
若是目標字符串是"abbc",中間就有回溯。
圖中第5步有紅顏色,表示匹配不成功。此時b{1,3}
已經匹配到了2個字符「b」,準備嘗試第三個時,結果發現接下來的字符是「c」。那麼就認爲b{1,3}就已經匹配完畢。而後狀態又回到以前的狀態(即第6步,與第4步同樣),最後再用子表達式c,去匹配字符「c」。固然,此時整個表達式匹配成功了。圖中的第6步,就是「回溯」。
正則表達式匹配字符串的這種方式,有個學名,叫回溯法。回溯法也稱試探法,它的基本思想是:從問題的某一種狀態(初始狀態)出發,搜索從這種狀態出發所能達到的全部「狀態」,當一條路走到「盡頭」的時候(不能再前進),再後退一步或若干步,從另外一種可能「狀態」出發,繼續搜索,直到全部的「路徑」(狀態)都試探過。這種不斷「前進」、不斷「回溯」尋找解的方法,就稱做「回溯法」
本質上就是深度優先搜索算法。其中退到以前的某一步這一過程,咱們稱爲「回溯」。從上面的描述過程當中,能夠看出,路走不通時,就會發生「回溯」。即,嘗試匹配失敗時,接下來的一步一般就是回溯。
貪婪量詞
以前的例子都是貪婪量詞相關的。好比b{1,3}
,由於其是貪婪的,嘗試可能的順序是從多往少的方向去嘗試。首先會嘗試"bbb",而後再看整個正則是否能匹配。不能匹配時,吐出一個"b",即在"bb"的基礎上,再繼續嘗試。若是還不行,再吐出一個,再試。若是還不行呢?只能說明匹配失敗了
let regex = "\\d{1,3}"
let validate = "12345"
let result = RegularExpression(regex: regex, validateString: validate)
print(result)
//["123", "45"]
複製代碼
惰性量詞
惰性量詞就是在貪婪量詞後面加個問號。表示儘量少的匹配,好比:
let regex = "\\d{1,3}?"
let validate = "12345"
let result = RegularExpression(regex: regex, validateString: validate)
print(result)
//["1", "2", "3", "4", "5"]
複製代碼
分支結構
咱們知道分支也是惰性的,好比/can|candy/
,去匹配字符串"candy",獲得的結果是"can",由於分支會一個一個嘗試,若是前面的知足了,後面就不會再試驗了。分支結構,可能前面的子模式會造成了局部匹配,若是接下來表達式總體不匹配時,仍會繼續嘗試剩下的分支。這種嘗試也能夠當作一種回溯。好比正則
對於一門語言的掌握程度怎麼樣,能夠有兩個角度來衡量:讀和寫。
不只要求本身能解決問題,還要看懂別人的解決方案。代碼是這樣,正則表達式也是這樣。正則這門語言跟其餘語言有一點不一樣,它一般就是一大堆字符,而沒有所謂「語句」的概念。如何能正確地把一大串正則拆分紅一塊一塊的,成爲了破解「天書」的關鍵。
本章就解決這一問題,內容包括:
a
匹配字符"a"[0-9]
,表示匹配一個數字。也有\d
的簡寫形式。另外還有反義字符組,表示能夠是除了特定字符以外任何一個字符,好比[^0-9],表示一個非數字字符,也有\D的簡寫形式。a{1,3}
表示「a」字符連續出現3次。另外還有常見的簡寫形式,好比a+
表示「a」字符連續出現至少一次^
匹配字符串的開頭,又好比\b
匹配單詞邊界,又好比(?=\d)
表示數字前面的位置。(ab)+
,表示"ab"兩個字符連續出現屢次,也可使用非捕獲分組(?:ab)+
。abc|bcd
,表達式匹配"abc"或者"bcd"字符子串這裏,咱們來分析一個正則:
ab?(c|de*)+|fg
(c|de*)
是一個總體結構。(c|de*)
中,注意其中的量詞*
,所以e*
是一個總體結構|
優先級最低,所以c
是一個總體、而de*
是另外一個總體a、b?、(...)+、f、g
。而因爲分支的緣由,又能夠分紅ab?(c|de*)+
和fg
這兩部分。匹配字符串總體問題
由於是要匹配整個字符串,咱們常常會在正則先後中加上錨字符 ^
和$
好比要匹配目標字符串"abc"或者"bcd"時,若是一不當心,就會寫成^abc|bcd$
。
而位置字符和字符序列優先級要比豎槓高,這句正則的意思是開始匹配abc或者結尾匹配bcd
let regex = "^abc|bcd$"
let validate = "abc123456"
let result = RegularExpression(regex: regex, validateString: validate)
print(result)
//["abc"]
複製代碼
正確的寫法應該是^(abc|bcd)$
量詞連綴問題
假設,要匹配這樣的字符串:
- 每一個字符爲a、b、c任選其一
- 字符串的長度是3的倍數
此時正則不能想固然地寫成^[abc]{3}+$
let regex = "^[abc]{3}+$"
let validate = "abcaaa"
let result = RegularExpression(regex: regex, validateString: validate)
print(result)
//[]
複製代碼
正確的應該寫成^([abc]{3})+$
let regex = "^([abc]{3})+$"
let validate = "abcaaa"
let result = RegularExpression(regex: regex, validateString: validate)
print(result)
//["abcaaa"]
複製代碼
元字符轉義問題
^ $ . * + ? | \ / ( ) [ ] { } = ! : - ,
let regex = "\\^\\$\\.\\*\\+\\?\\|\\\\\\/\\[\\]\\{\\}\\=\\!\\:\\-\\,"
let validate = "^$.*+?|\\/[]{}=!:-,"
let result = RegularExpression(regex: regex, validateString: validate)
print(result)
//["^$.*+?|\\/[]{}=!:-,"]
複製代碼
須要用\\
轉義
匹配「[abc]」和「{3,5}」
let regex = "\\[abc]"
let validate = "[abc]"
let result = RegularExpression(regex: regex, validateString: validate)
print(result)
//["[abc]"]
複製代碼
只須要在第一個方括號轉義便可,由於後面的方括號構不成字符組,正則不會引起歧義,天然不須要轉義。
文章轉載:JS正則表達式完整教程(略長)