一、正则表达式的用途(搜索和替换)
- 1.1.正则表达式(
regular expression
,简称regex
)是一种工具,和其他的工具是一样的,它是人们为了解决某一类问题而发明的,要想理解正则表达式及其功用,最好的办法是了解它们可以解决什么样的问题。 - 1.2. 在编写处理字符串的程序时,经常会有查找符合某些复杂规则的字符串或者替换一些字符串。正则表达式就是用于描述这些规则的工具。换句话说,正则表达式就是记录文本规则的代码
- 1.3.正则表达式是对字符串操作的一种逻辑公式,用事先定义好的一些特定字符、及这些特定字符的组合,组成一个"规则字符串",这个"规则字符串"用来表达对字符串的一种过滤逻辑。
- 1.4.-几乎所有的程序设计语言都支持正则表达式,例如:
OC
,java
,c#
,python
,js
等 - 1.5.在很多文本编辑器里,可以使用正则表达式进行检索,正则表达式是文本处理方面功能最强大的工具之一,正则表达式语言来构造正则表达式(最终构造出来的字符串就称为正则表达式),正则表达式用来完成搜索和替换的操作,当然,Xcode同样支持正则表达式!
二、匹配单个字符(下面的都将调用textRegex()方法)
- 2.1.匹配纯文本(
"ben"
)
import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // 匹配单个字符 view.addSubview(label) let str = "Hello, my name is Ben. Please visit my ben website at https://www.forta.com/." label.attributedText = textRegex(pattern: ".a.",str: str, font: 22) } // 1.匹配纯文本 func textRegex(pattern: String,str: String,font: CGFloat) -> NSMutableAttributedString{ //富文本设置 let attributeString = NSMutableAttributedString(string:str) do { // 1.1.定义规则 //let pattern = "ben" // 1.2.创建正则表达式对象 let regex = try NSRegularExpression(pattern: pattern, options: NSRegularExpression.Options.caseInsensitive) // 1.3.开始匹配 let res = regex.matches(in: str, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: NSMakeRange(0, str.count)) for checkingRes in res { print("range\(checkingRes.range)") // substring 截取符合规定规则的字符串 print((str as NSString).substring(with: checkingRes.range)) //从文本checkingRes.range个字符字体HelveticaNeue-Bold attributeString.addAttribute(NSAttributedStringKey.font, value: UIFont(name: "HelveticaNeue-Bold", size: 22)!,range: checkingRes.range) //设置字体颜色 attributeString.addAttribute(NSAttributedStringKey.foregroundColor, value: UIColor.blue,range: checkingRes.range) //设置文字背景颜色 attributeString.addAttribute(NSAttributedStringKey.backgroundColor, value: UIColor.green,range: checkingRes.range) } return attributeString } catch { print(error) } return attributeString } lazy var label: UILabel = { let label1: UILabel = UILabel(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.height)) label1.font = UIFont.systemFont(ofSize: 22) label1.numberOfLines = 0 label1.backgroundColor = UIColor.white return label1 }() }
- 2.2.匹配任意字符(注意:下面的两句代码调了textRegex方法)
前面见到的正则表达式都是一些静态的纯文本,它们根本体现不出来正则表达式的威力,下面,我们一起来看看如何用正则表达式去匹配不可预知的字符。在正则表达式里,特殊字符(或字符集合)用来给出要搜索的东西。"."字符(英文句号)可以匹配任何一个单个的字符。
let str = "Hello, my name is Ben. Please visit my ben website at https://www.forta.com/." label.attributedText = textRegex(pattern: ".a.",str: str, font: 22)
- 2.3. 用正则表达式w.t进行的搜索将匹配到wht和wat(还能匹配到一些毫无意义的单词)
let str = "HellomynameisBen.whtareyouwatoulikeme" label.attributedText = textRegex(pattern: "w.t",str: str, font: 22)
- 2.4. “.”字符可以匹配任何单个的字符,字母,数字甚至只“.”字符本身
let str = "HellomynameisBen.whtareyouwatoulikeme" label.attributedText = textRegex(pattern: "n.",str: str, font: 22)
- 2.5.匹配特殊字符
- “.”字符在正则表达式里面有着特殊的含义,如果模式里需要一个“.”,就要想办法告诉正则表达式你需要的是“.”字符本身而不是它在正则表达式里的特殊含义。为此,你必须在“.”前面加一个(反斜杠)字符来对它进行转义。\是一个元字符(metacharacter,表示“这个字符有特殊的含义”)。
- 在正则表达式里面,\字符永远出现在一个有着特殊含义的字符串序列的开头,这个序列可以由一个或者多个字符构成。下面看到的是
\.
序列,在后面的章节里面还会看到更多使用了\字符的例子。
let str = "Hellomynamei.sBen.whtarbyouwatoulikea.se" label.attributedText = textRegex(pattern: ".e.\\.s",str: str, font: 22)
- 注意:如果需要搜索\本身,就必须对\字符进行转义;相应的转义序列是两个连续的反斜杠字符
\\
。
三、匹配一组字符(下面会涉及字符集合)
- 3.1.说明字符集合则能匹配特定的字符和字符区间。
- 3.2.匹配多个字符中的某一个
let str = "Hellomynamei.sBen.whtareb.syouwatoulikea.se" label.attributedText = textRegex(pattern: "[mk]e.\\.s",str: str, font: 22)
- 解释:
"[mk]e.\\.s"
是e
的前面只能是 字母m
或者k
- 3.2.利用字符集合区间
A-Z: 匹配从A到Zdenka所有大写字母
a-z: 匹配a-z的所有小写的字母
A-F: 匹配从A到F所有大写字母
A-z: 匹配ASCII字符A到ASCII字符z的所有字母。这个模式一般不常用,因为它还包含着[^等在ASCII字符表里面排列在z和a之间的字符。字符区间的首、尾字符可以是ASCII字符表里的任意字符。但在实际工作中,最长哟个的字符区间还是数字字符区间和字母字符区间。
提示:在定义一个字符区间一定要避免这个区间的尾字符小于它的首字符(例如:[3-1])。这种区间是没有意义的,二往往会让整个模式失效。-
(连字符):是一个特殊的元字符,作为元字符它只能用在[和]之间。在字符集合意外的地方,“-”只是一个普通的字符,只能与“-”本身相匹配。因此,在正则表达式里面,“-”字符不需要被转义。
let str = "<BODY BGCOLOR=#336633 TEXT=#FFFFFF MARGINWIDTH=0 MARGINHEIGHT=0 TOPMARGIN=0 LEFTMARGIN=0>" label.attributedText = textRegex(pattern: "#[0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f]",str: str, font: 22)
- 分析:这里使用的模式以普通字符
#
开头,随后是6个同样的[0-9A-Fa-f]
字符集合。这将匹配一个由字符#
开头,然后6个数字或字母A-F(大小写均可)的字符串
- 3.3.取非匹配
字符集合通常用来指定一组必须匹配其中之一的字符。但在某些场合,我们需要反过来做给出一组不需要得到的字符,换句话说,除了那个字符集合的字符,其他的字符都可以匹配。我们可以用元字符^
来表明你想对一个字符集合进行取非匹配。这与逻辑运算很相似,只是这里的操作数是字符集合而已。
let str = "Real Love is n2 just instin1t, but intent." label.attributedText = textRegex(pattern: "[no][^a-zA-Z]",str: str, font: 22)
- 解释:
[no]
意思是第一个取n
或者o
都可以,[^a-zA-Z]
意思是除了小写a~z
和大写A~Z
的都不可以取,^
是非的意思^
的效果将作用于给定字符集合里的所有字符或字符区间,而不是仅限于紧跟在^
字符后面的那一个字符或者字符区间。
- 3.4.总结
元字符[和]用来定义一个字符集合,其含义是必须匹配该集合里的字符之一。定义一个字符集合的具体的做法有两种:1、把所有的字符都列举出来;2、是利用元字符-
以字符区间的方式给出。字符集合可以用元字符^
来求非;这将把给定的字符集合强行排除在匹配操作以外,除了该字符集合里的字符,其他字符都可以进行匹配。
四、使用元字符(利用元字符去匹配特定的字符或字符类型)
- 4.1.对特定的字符进行转义
元字符是一些在正则表达式里有着特殊含义的字符。英文句号.
是一个元字符,他可以用来匹配任意一个单个字符,类似地,在方括号[
也是一个元字符,它标志着一个字符集合的开始。
元字符是无法用来替代它们本身,因为在正则表达式里面有着特殊的含义,比如,你不能用一个[
来匹配[
本身,也不能使用.
来匹配.
本身,看下面的例子
let str = "var myArray = new Array() if (myArray[0] = 0)" label.attributedText = textRegex(pattern: "myArray[0]",str: str, font: 22)
- 说明:为什么没有匹配到
myArray[0]
呢?因为:[和]在正则表达式里是用来定义一个字符集合(而不是[和]本身)的元字符,所以,myArray[0]
将匹配myArray
后面跟着一个该集合成员的情况,而那个集合只有一个成员0
.因此,myArray[0]
只能匹配到myArray0
。
- 4.2. 如何才能匹配到myArray[0]呢???
答案是对元字符进行转义 前面加\\
反斜杠,这样就可以匹配本身了,[
和]
就不再是元字符。
let str = "var myArray = new Array() if (myArray[0] = 0)" label.attributedText = textRegex(pattern: "myArray\\[0]",str: str, font: 22)
如果想匹配到0-9之间的数字都可以匹配的话,就需要对集合进行匹配了
let str = "var myArray[8] = new Array() if (myArray[0] = 0)" label.attributedText = textRegex(pattern: "myArray\\[[0-9]]",str: str, font: 22)
任何一个元字符都可以通过给它加上一个反斜杠字符 \
作为前缀的办法来转义,对元字符进行转义需要用到\
字符也是一个元字符,它的特殊含义是对其他的额字符进行转义,在需要匹配本身的时候,我们必须把它转移为\\
let str: String = "\\Users\\wangchong\\Desktop\\" label.attributedText = textRegex(pattern: "\\\\",str: str, font: 22)
- 4.2.匹配空白字符
元字符大多分为2种,一种是用来匹配文本的(比如.
),另一种是正则表达式的语法所要求的(比如[和])。随着学习的深入,我们会发现越来越多的这两种语法,下面是匹配空白字符的元字符。
let str: String = "Oviri is an \n\n 1894 ceramic sculpture by the French artist Paul Gauguin." label.attributedText = textRegex(pattern: "\n\n",str: str, font: 22)
\n\n
作为文本结束的标签
- 元字符我们已经见了好几个了,但是差异性我们真的链接吗?比如
.
和[
是元字符(前提是没有对他们进行转义);f
、n
、r
、t
、也是元字符(前提是对它们进行了转义),否则它们将被视为普通的字符,从而也只能匹配它们本身。
- 4.3.匹配特定的字符类别
.
匹配任意字符[和]
多个字符中的某一个^
取非匹配- 字符集合(匹配多个字符中的某一个)是最常见的匹配形式,而一些常用的字符集合可以用特殊的元字符来代替。这些元字符匹配的是某一类别的字符(术语为字符类)。看下面的例子
- 4.3.1. 匹配数字(与非数字)
let str: String = "Oviri is an 1894 ceramic scu1lpture by the French artist Paul Gauguin." label.attributedText = textRegex(pattern: "\\d",str: str, font: 22)
- 提示:正则表达式的语法是区分字母大小写的。
\d匹配数字
,\D
匹配非数字,它们正好相反。其他的元字符也一样
- 4.3.2.匹配字母和数字(与非字母和数字)
例如
let str: String = "你%¥吗()==12_3" label.attributedText = textRegex(pattern: "\\W",str: str, font: 22)
- 4.3.3.匹配空字符串(与空白字符)
看下面的例子
let str: String = "11213 A1C2E3 48075 48237 M14F2 90046 H1H2H2" label.attributedText = textRegex(pattern: "\\s",str: str, font: 22)
- 提示:用来匹配退格字符的
[\b]
元字符就是一个特例;它不在类元字符\s
的覆盖范围内,当然也就没有被排除在类元字符\S
的覆盖范围外。
- 4.4.使用POSIX字符类
let str: String = "K$P1-21_3@" label.attributedText = textRegex(pattern: "[:alnum:]",str: str, font: 22)