首先,這是一篇 關(guān)于Swift 的基礎(chǔ)教程,里面包含 String 的部分API 以及擴(kuò)展(Extension) 、下標(biāo)(Subscripts)、自定義運(yùn)算符、泛型等知識(shí)。不感興趣的童鞋可以直接翻到底部看福利了。
字符串處理一直都是程序開發(fā)中不可避免的,而字符串截取/替換操作更是頻繁。
Swift 的語法一直都在演變進(jìn)化,在1.x到2.x的演變過程中也發(fā)生了很大的改變,在開源后發(fā)展更加迅速,即將發(fā)布的3.0版本也合并到了master.
字符串的處理也是變化的一部分,在最近的coding中遇到了一些頻繁需要字符串處理的地方,過程中發(fā)現(xiàn)目前版本(2.x)字符串處理的語法實(shí)在有些啰嗦。
在Swift1.0 的時(shí)候字符串的截取是這樣的:
var hp = "Hello, playground"
let hello = hp[0 ... 4] // error
如果在Swift2.x可以,那真是極好的。
我喜歡這種以自然數(shù)提取字符串的方式,可是在Swift2.x中已經(jīng)摒棄。
在Swift2.x中需要這樣寫:
let hello = hp.substringWithRange(Range<String.Index>(start: hp.startIndex, end: hp.startIndex.advancedBy(5))) // "Hello"
或者更簡(jiǎn)單點(diǎn):
let hello = hp[hp.startIndex..<hp.startIndex.advancedBy(5)] // "Hello"
當(dāng)然用OC中的NSString也是可以的,只是...
var nsHp = hp as NSString
let hello = nsHp.substringWithRange(NSMakeRange(0, 5)) // "Hello"
因?yàn)樵赟wift2.x (應(yīng)該是在Swift1.2以后吧)中用String.Index代替了自然數(shù), 新的API 毫無疑問的會(huì)占用我僅僅16GB的記憶量,我不想調(diào)用新API中那些所謂的牛逼黑科技代碼。
為什么一個(gè)簡(jiǎn)單的字符串處理需要寫的這么啰嗦,這里很好的總結(jié)了這一切
其實(shí)我更喜歡python截取字符串的風(fēng)格,簡(jiǎn)單粗暴。
hp = "Hello, playground"
hello = hp[0:5] // "Hello"
hello = hp[0:-12] // "Hello"
所以我決定對(duì)它做些改變:
擴(kuò)展 Extension
Swift 中的擴(kuò)展和 Objective-C 中的分類類似,(只是Swift 中擴(kuò)展沒有名字)
Swift中的擴(kuò)展可以:
- 添加計(jì)算型屬性和計(jì)算型類型屬性
- 定義實(shí)例方法和類型方法
- 提供新的構(gòu)造器
- 定義下標(biāo)
- 定義和使用新的嵌套類型
- 使一個(gè)已有類型符合某個(gè)協(xié)議
下標(biāo) Subscripts
下標(biāo)可以定義在類(class)、結(jié)構(gòu)體(structure)和枚舉(enumeration)中,是訪問集合(collection),列表(list)或序列(sequence)中元素的快捷方式。
我現(xiàn)在添加String的擴(kuò)展來定義下標(biāo),然后取出想要的字符串,下標(biāo)接收Int類型的Range,Index 通過advancedBy(n)
來遞增,參數(shù)n是前進(jìn)的個(gè)數(shù),如果 n 大于 0 則會(huì)調(diào)用self的 successor()
n 次,小于 0 則會(huì)調(diào)用 predecessor()
n 次。
successor() 和 predecessor() 分別是可能出現(xiàn)的下一個(gè)值,和上一個(gè)值。
extension String {
subscript (r: Range<Int>) -> String {
get {
let startIndex = self.startIndex.advancedBy(r.startIndex)
let endIndex = self.startIndex.advancedBy(r.endIndex)
return self[Range(start: startIndex, end: endIndex)]
}
}
}
let hello1 = hp[Range(start: 0, end: 5)] // "Hello"
let hello2 = hp[0 ..< 5] // "Hello"
自然數(shù)截取 極好的.
然而發(fā)現(xiàn)了問題,我要像python用下標(biāo)從后向前取怎么辦 :|
let hello = hp[0 ..< -12] // "error" ( system:不懂Range 別亂搞 :( )
不用Range 就是了:) 下面我將重新寫一個(gè)接收兩個(gè)參數(shù)下標(biāo)方法,注意, 這里通過三元表達(dá)式來判斷。在end 小于 0 時(shí)會(huì) 執(zhí)行self.endIndex.advancedBy(end)
, 前面有說到,也就是說會(huì)執(zhí)行abs(end)次的predecessor()
方法。
extension String {
subscript (start: Int, end: Int) -> String {
let s = self.startIndex.advancedBy(start)
let e = end < 0 ? self.endIndex.advancedBy(end) : self.startIndex.advancedBy(end)
return self[Range(start: s, end: e)]
}
}
let hello = hp[0, -12] // "Hello"
搞定!不過參數(shù)中[0, -12]
中間的,
讓整體像個(gè)Array ,感覺怪怪的,我不太喜歡,決定改掉。
自定義運(yùn)算符
- 自定義運(yùn)算符可以由以下的 ASCII 字符
/、=、 -、+、!、*、%、<、>、&、|、^、? 、~
自由組合.
- 支持一元前綴 prefix (例如 --a, ++a, ! false)、一元后綴 postfix (a--, a++)、二元中置 infix (a+b, a-b, a==b)運(yùn)算符
- 支持自定義優(yōu)先級(jí)
注:操作符前后空格也有規(guī)則,這里不多做說明,可自行查看官方文檔。
在Swift中 :
歸為字符,而非運(yùn)算符,所以無法使用,我覺定用~
來替代,創(chuàng)建一個(gè)二元中置運(yùn)算符:
infix operator ~ {
precedence 250 // 優(yōu)先級(jí) 0~255 的一個(gè)值,具體參考官方文檔
associativity none // 結(jié)合 left, right, none
}
func ~(start: Int, end: Int) -> (s: Int, e: Int) {
return (start, end) // 說好的運(yùn)算符,除了返回個(gè)元組,啥也沒干...
}
extension String {
//接收元組的下標(biāo)方法
subscript (myRange: (s: Int, e: Int)) -> String {
get {
let s = self.startIndex.advancedBy(myRange.s)
let e = myRange.e < 0 ? self.endIndex.advancedBy(myRange.e) : self.startIndex.advancedBy(myRange.e)
return self[Range(start: s, end: e)]
}
set {
let s = self.startIndex.advancedBy(myRange.s)
let e = myRange.e < 0 ? self.endIndex.advancedBy(myRange.e) : self.startIndex.advancedBy(myRange.e)
str.replaceRange(Range(start: s, end: e), with: newValue)
}
}
}
let hello = hp[0 ~ 5] // "Hello"
hp[7 ~ 17] = "dimsky" // "Hello, dimsky"
沒錯(cuò),我在下標(biāo)方法加了個(gè)set 方法用來替換指定下標(biāo)的值。
可能有人對(duì) advanceBy
這個(gè)方法還不太理解,下面我將用 +
運(yùn)算符來替代這個(gè)方法:
func +<T: BidirectionalIndexType>(var index: T, count: Int) -> T {
let num = abs(count)
for _ in 0..<num {
index = count < 0 ? index.predecessor() : index.successor()
}
return index
}
let hello = str[str.startIndex ..< str.startIndex.advancedBy(5)] // "hello"
let play = str[str.startIndex + 7 ..< str.startIndex + 11] // "play"
let play2 = str[str.startIndex + 7 ..< str.endIndex + -6] // "play"
let helloPlay = str[str.startIndex ..< str.endIndex.advancedBy(-6)] // "Hello, play"
我們可能還會(huì)需要經(jīng)常的獲取字符的下標(biāo),這是Swift 中正常獲取字符所在下標(biāo)的方法:
let index1 = hp.rangeOfString("H")?.startIndex // 0 (String.CharacterView.Index)
let index2 = hp.rangeOfString("o")?.endIndex // 5 (String.CharacterView.Index)
是的,我們沒法通過上面的方法去直接獲得自然數(shù)的下標(biāo),所以就有了下面的擴(kuò)展方法:
extension String {
func indexOfString(target: String) -> Int {
let range = self.rangeOfString(target)
if let range = range {
return self.startIndex.distanceTo(range.startIndex)
} else {
return -1
}
}
}
let index = hp.indexOfString("o") // 5 (Int)
當(dāng)然接下來可以玩的還有很多
比如字符串替換:
hp["playground"] = "三上悠亞" // waiting for you to complete
hp.stringByReplacingOccurrencesOfString("playground", withString: "鈴原エミリ") // "Hello, 鈴原エミリ" 延用了OC的API
本文的目的并不是推崇自然數(shù)的方式訪問字符串,僅僅是為了偷個(gè)小懶,啰嗦幾句罷了。
與時(shí)俱進(jìn)還是很重要的,Swift 字符串API 為什么難用(這里解釋了這些)
...
...
說好的福利呢。
那就這樣吧...