在Swift語法中相對OC新增了一個修辭詞:String,它與原來的NSString可以很方便的轉(zhuǎn)換,但是在實際的開發(fā)過程中我們應該如何選擇?
1.能用String的時候就使用String
(1).因為現(xiàn)在所有的Cocoa框架都能接收和也能返回String類型,所以沒有必要特地轉(zhuǎn)換
(2).Swift中String是struct 而 NSString是NSObject,所以String更符合字符串不變的特性;同時在不觸及NSString的特有操作的情況下,使用String的方法也有性能的提升:
(3).由于String類型實現(xiàn)了Collection type的這樣的接口,所以某些Swift的特性只有String類型能用,而NSString卻沒有;
例如 for ... in 的枚舉遍歷所有字符:
let words: String = "ILOVEYOU"
for character in words{
printf(charcter)
}
2.要使用NSString的情況
(1).String類型有hasPrefix/hasSuffix方法來判斷是否以某字符串開頭或者結(jié)尾;
if words.hasPrefix("ILOVE") {
print("YES")
}else{
print("NO")
}
//以上代碼輸出結(jié)果為YES
NSString類型中雖然沒有這種方法,但是NSString類型中卻有containsString方法來判斷字符串內(nèi)部是否包含了某些字符,目前4.0版本containsString方法被縮寫為:contains
//String類型轉(zhuǎn)換成NSString類型
let nsWords = (words as NSString)
print(nsWords)
if nsWords.contains("ILOVE") {
NSLog("YES")
}else{
NSLog("NO")
}
//輸出結(jié)果YES
(2). String類型截取字段的時候比較麻煩
//NSString類型截取字段的方法(Swift4.0)
let rangeStr1 = (words as NSString).substring(with: NSMakeRange(4, 2))
//String類型截取字段的方法(現(xiàn)在這個方法在Swift4.0中已被取消)
let index = words.startIndex.advancedBy(4)
let index2 = words.startIndex.advancedBy(6)
let range = Range<String.Index>(start: index, end: index2)
let rangeStr2 = words.substringWithRange(range)
字符串的字面量
你可以在代碼里使用一段預定義的字符串值作為字符串字面量。字符串字面量是由一對雙引號包裹著的具有固定順序的字符集。
//字符串字面量可以用于為常量和變量提供初始值:
let someString = "Some string literal value"
//someString 是一個字符串常量,通過字符串字面量進行初始化,Swift語法系統(tǒng)會自動推斷它為String類型
多行字符串字面量的書寫格式
let quotation = """
The White Rabbit put on his spectacles. "Where shall I begin,
please your Majesty?" he asked.
"Begin at the beginning," the King said gravely, "and go on
till you come to the end; then stop."
"""
//注意:上面這段代碼中,在兩個 ' """ ' 中包含著一個多行字符串的字面量
// 字符串中還可夾雜雙引號,需要分行是直接用回車符號,非常好用.
圖解:
字符串字面量的特殊字符
- 轉(zhuǎn)義字符\0(空字符)、\(反斜線)、\t(水平制表符)、\n(換行符)、\r(回車符)、"(雙引號)、'(單引號)。
- Unicode 標量,寫成\u{n}(u為小寫),其中n為任意一到八位十六進制數(shù)且可用的 Unicode 位碼
//示例代碼
let str1 = "\u{24}"
print("str1:\(str1) str1")
//輸出結(jié)果 :str1:$ str1
let str2 = "\0"
print("str2:\(str2) str2")
//輸出結(jié)果 :str2: str2
let str3 = "\\"
print("str3:\(str3) str3")
//輸出結(jié)果:str3:\ str3
let str4 = "\t"
print("str4:\(str4) str4")
//輸出結(jié)果:str4: str4
let str5 = "\n"
print("str5:\(str5) str5")
//輸出結(jié)果:str5:
str5
let str6 = "\n"
print("str6:\(str6) str6")
//輸出結(jié)果: str6:
str6
let str7 = "\"abc\""
print("str7:\(str7) str7")
//輸出結(jié)果:str7:"abc" str7
let str8 = "\'abc\'"
print("str9 \(str8) str9")
//輸出結(jié)果:str9 'abc' str9
字符與字符串
您可通過for-in循環(huán)來遍歷字符串,獲取字符串中每一個字符的值:
for character in "Dog!??" {
print(character)
}
// D
// o
// g
// !
// ??
另外,通過標明一個Character類型并用字符字面量進行賦值,可以建立一個獨立的字符常量或變量:
let exclamationMark: Character = "!"
字符串可以通過傳遞一個Character的數(shù)組作為自變量來初始化:
let catCharacters: [Character] = ["C", "a", "t", "!", "??"]
let catString = String(catCharacters)
print(catString)
// 打印輸出:"Cat!??"
String類型的拼接
let hello = "Hello,"
let world = "World"
let character: Character = "!"
//過加法運算符(+)相加在一起
var hellworld = hell + world // hellword 現(xiàn)在等于 "Hello,World"
//可以用append()方法將一個字符附加到一個字符串變量的尾部
hellworld.append(character) // hellworld 現(xiàn)在等于 "Hello,World!"
//也可以通過加法賦值運算符 (+=) 將一個字符串添加到一個已經(jīng)存在字符串變量上:
let fuck = " fuck"
hellworld += fuck //hellworld 現(xiàn)在等于 "Hello,World! fuck"
注意:
您不能將一個字符串或者字符添加到一個已經(jīng)存在的字符變量上,因為字符變量只能包含一個字符。這個有點像 Int.max 不能再加 1
字符串插值
let multiplier = 3
let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
// message 是 "3 times 2.5 is 7.5"
獲取字符串的字符數(shù)量
//獲取字符串的字符數(shù)量直接使用String自帶的方法.count
let unusualMenagerie = "Koala ??, Snail ??, Penguin ??, Dromedary ??"
print("unusualMenagerie has \(unusualMenagerie.count) characters")
// 打印輸出 "unusualMenagerie has 40 characters"
- 注意在 Swift 中,使用可拓展的字符群集作為Character值來連接或改變字符串時,并不一定會更改字符串的字符數(shù)量。
var word = "cafe"
print("the number of characters in \(word) is \(word.count)")
// 打印輸出 "the number of characters in cafe is 4"
word += "\u{301}" // 拼接一個重音, U+0301 得到 word = café
print("the number of characters in \(word) is \(word.count)")
// 打印輸出 "the number of characters in café is 4"
Unicode
1). 什么是Unicode?
Unicode是一個國際標準,用于文本的編碼和表示。 它使您可以用標準格式表示來自任意語言幾乎所有的字符,并能夠?qū)ξ谋疚募蚓W(wǎng)頁這樣的外部資源中的字符進行讀寫操作。 Swift 的String和Character類型是完全兼容 Unicode 標準的。
2). Unicode的標量
Unicode 標量是對應字符或者修飾符的唯一的21位數(shù)字:
//例如U+0061表示小寫的拉丁字母(LATIN SMALL LETTER A)("a")
//U+1F425表示小雞表情(FRONT-FACING BABY CHICK) ("??")
let character = "\u{0061}" // character 的值是 "a"
let monkey = "\u{1f435}" // monkey 的值是 "??"
- 注意: Unicode 碼位(code poing) 的范圍是U+0000到U+D7FF或者U+E000到U+10FFFF。Unicode 標量不包括 Unicode 代理項(surrogate pair) 碼位,其碼位范圍是U+D800到U+DFFF。
- 不是所有的21位 Unicode 標量都代表一個字符,因為有一些標量是留作未來分配的。
可擴展的字形群集
每一個 Swift 的Character類型代表一個可擴展的字形群。 一個可擴展的字形群是一個或多個 Unicode 標量的有序排列。
//舉例
//字母é 可以是一個Unicode標量 "\u{00E9}",
//也可以是一對標量:"\u{0065}\u{0301}"
//在寫代碼時,前面的0都可以不謝
let e1 = "\u{E9}" // e1 = é
let e2 = "\u{65}\u{301}" // e2 = é
//并且 e1.count == e2.count
//可擴展的字符群集是一個靈活的方法,用許多復雜的腳本字符表示單一的Character值。
//來自朝鮮語字母表的韓語音節(jié)能表示為組合或分解的有序排列
//在 Swift 都會表示為同一個單一的Character值:
let precomposed: Character = "\u{D55C}" // ?
let decomposed: Character = "\u{1112}\u{1161}\u{11AB}" // ?, ?, ?
// precomposed 是 ?, decomposed 是 ?
//可拓展的字符群集可以使包圍記號(l例如:U+20DD)的標量包圍其他 Unicode 標量:
let enclosedEAcute: Character = "\u{E9}\u{20DD}"http:// enclosedEAcute 是 é?
let myFirstName: Character = "龐\u{20DD}"http:// 日 是 龐?
//地域性指示符號的 Unicode 標量可以組合成一個單一的Character值:
let regionalIndicatorForUS: Character = "\u{1F1FA}\u{1F1F8}"
// regionalIndicatorForUS 是 ????
修改和訪問String
String 類型的:索引
每一個String類型值都有一個關(guān)聯(lián)的索引(index),String.Index,它對應著Sting中的每一個Chracter的位置。
但是不同的Chracter可能占用著不同的內(nèi)存空間,所以要想知道Chracter的確定位置,要從String的開頭遍歷每一個Unicode,因此String的索引不能用整數(shù),例如:
let str1: String = "\u{65}\u{301}" //等于 é
print(a.startIndex)
//ndex(_compoundOffset: 0, _cache: Swift.String.Index._Cache.utf16)
print(a.endIndex)
//Index(_compoundOffset: 8, _cache: Swift.String.Index._Cache.utf16)
使用startIndex屬性可以獲取一個String的第一個Character的索引。使用endIndex屬性可以獲取最后一個Character的后一個位置的索引。因此,endIndex屬性不能作為一個字符串的有效下標。如果String是空串,startIndex和endIndex是相等的。
通過調(diào)用 String 的 index(before:) 或 index(after:) 方法,可以立即得到前面或后面的一個索引。您還可以通過調(diào)用 index(_:offsetBy:) 方法來獲取對應偏移量的索引,這種方式可以避免多次調(diào)用 index(before:) 或 index(after:) 方法。
//定義一個String類型的常量str,這個由3個Chracter組成a / b / ?
let str = "abc\u{301}" //ab?
//獲取 str中的第一位Chracter
let firstChracter = str[str.startIndex] //注意這里使用的是中括號
// 輸出 a
let secondChracter = str[str.index(after: str.startIndex)]
//輸出 b
let thirdChracter = str[str.index(before:龐日富.endIndex)]
//輸出 c
let index = str.index(str.startIndex, offsetBy: str.count - 1)
let lastChracter = str[indx]
//輸出 ?
視圖獲取越界的索引或者越界的索引值都會導致奔潰
greeting[greeting.endIndex] // error
greeting.index(after: endIndex) // error
使用 indices 屬性會創(chuàng)建一個包含全部索引的范圍(Range),用來在一個字符串中訪問單個字符。
let abc = "abc"
for chracter in abc{
print( chracter )
}
//輸出:
//a
//b
//c
for index in abc.indices{
print(abc[index])
}
//輸出:
//a
//b
//c
插入和刪除
調(diào)用 insert(_:at:) 方法可以在一個字符串的指定索引插入一個字符,調(diào)用 insert(contentsOf:at:) 方法可以在一個字符串的指定索引插入一個段字符串。
var welcome = "hello"
welcome.insert("!", at: welcome.endIndex)
// welcome 變量現(xiàn)在等于 "hello!"
welcome.insert(contentsOf:" there", at: welcome.index(before: welcome.endIndex))
// welcome 變量現(xiàn)在等于 "hello there!"
調(diào)用 remove(at:) 方法可以在一個字符串的指定索引刪除一個字符,調(diào)用 removeSubrange(_:) 方法可以在一個字符串的指定索引刪除一個子字符串。
welcome.remove(at: welcome.index(before: welcome.endIndex))
// welcome 現(xiàn)在等于 "hello there"
let range = welcome.index(welcome.endIndex, offsetBy: -6)..<welcome.endIndex
welcome.removeSubrange(range)
// welcome 現(xiàn)在等于 "hello"
注意: 您可以使用 insert(:at:)、insert(contentsOf:at:)、remove(at:) 和 removeSubrange(:) 方法在任意一個確認的并遵循 RangeReplaceableCollection 協(xié)議的類型里面,如上文所示是使用在 String 中,您也可以使用在 Array、Dictionary 和 Set 中。
子字符串
當你從字符串中獲取一個子字符串 —— 例如,使用下標或者 prefix(_:) 之類的方法 —— 就可以得到一個 SubString 的實例,而非另外一個 String。Swift 里的 SubString 絕大部分函數(shù)都跟 String 一樣,意味著你可以使用同樣的方式去操作 SubString 和 String。然而,跟 String 不同的是,你只有在短時間內(nèi)需要操作字符串時,才會使用 SubString。當你需要長時間保存結(jié)果時,就把 SubString 轉(zhuǎn)化為 String 的實例:
let greeting = "Hello, world!"
let index = greeting.index(of: ",") ?? greeting.endIndex
let beginning = greeting[..<index] //beginning在這里不是String類型,而是SubSquence
// beginning 的值為 "Hello"
// 把結(jié)果轉(zhuǎn)化為 String 以便長期存儲。
let newString = String(beginning)
和String一樣,每一個SubString都會在內(nèi)存里保存字符集,String和SubString的區(qū)別在于性能優(yōu)化上,SubString可以重用原String的內(nèi)存空間,或者另一個SubString的內(nèi)存空間,(String也有同樣的優(yōu)化,但是如果兩個String使用同樣的內(nèi)存的話,他們就會相等),著意味著子修改String和SubString之前都不需要消耗性能去賦值內(nèi)存,SubString不適合長期儲存,因為它重用了Stirng的內(nèi)存空間,元String的內(nèi)存空間必須保留,知道它的SubString不再被使用。
//案例
let greeting:String = "Hello,World!"
//意味著它在內(nèi)存里有一片空間保存字符集
let index = greeting.index(of: ",") ?? greeting.endIndex
let beginning = greeting[..<index]
//beginning 是 greeting 的 SubString ,所以它重用了greenting的內(nèi)存
let newString = String(beginning)
//newString 是使用 beginning (beginning是一個SubString)創(chuàng)建的,它擁有自己的內(nèi)存,
greeting beginning newString的關(guān)系可以用下圖解釋:

比較字符串
Swift 提供了三種方式來比較文本值:字符串字符相等、前綴相等和后綴相等。
字符串/字符相等
// 用 == 運算符進行判斷
let quotation = "We're a lot alike, you and I."
let sameQuotation = "We're a lot alike, you and I."
if quotation == sameQuotation {
print("These two strings are considered equal")
}
// 打印輸出 "These two strings are considered equal"
//如果兩個字符串 print出來的值是相等的,我們就認為他是相等的
let strA = "\u{E9}" // é
let strB = "\u{65}\u{301}" // é
if strA == strB {
print( " strA 和 strB 是相等的" ) //代碼會執(zhí)行到這里來
}
注意:
在 Swift 中,字符串和字符并不區(qū)分地域(not locale-sensitive)。
前綴/后綴相等
通過調(diào)用字符串的hasPrefix(:)/hasSuffix(:)方法來判斷字符串是否擁有指定的 前綴/后綴,兩個方法均接收一個String類型的參數(shù),并返回一個布爾值。
let romeoAndJuliet = [
"Act 1 Scene 1: Verona, A public place",
"Act 1 Scene 2: Capulet's mansion",
"Act 1 Scene 3: A room in Capulet's mansion",
"Act 1 Scene 4: A street outside Capulet's mansion",
"Act 1 Scene 5: The Great Hall in Capulet's mansion",
"Act 2 Scene 1: Outside Capulet's mansion",
"Act 2 Scene 2: Capulet's orchard",
"Act 2 Scene 3: Outside Friar Lawrence's cell",
"Act 2 Scene 4: A street in Verona",
"Act 2 Scene 5: Capulet's mansion",
"Act 2 Scene 6: Friar Lawrence's cell"
]
//統(tǒng)計 Act1 前綴出現(xiàn)的次數(shù)
var act1SceneCount = 0
for scene in romeoAndJuliet {
if scene.hasPrefix("Act 1 ") {
act1SceneCount += 1
}
}
print("There are \(act1SceneCount) scenes in Act 1")
// 打印輸出 "There are 5 scenes in Act 1"
//統(tǒng)計后綴:Capulet's mansion 和后綴:Friar Lawrence's cell分別出現(xiàn)的次數(shù)
var mansionCount = 0
var cellCount = 0
for scene in romeoAndJuliet {
if scene.hasSuffix("Capulet's mansion") {
mansionCount += 1
} else if scene.hasSuffix("Friar Lawrence's cell") {
cellCount += 1
}
}
print("\(mansionCount) mansion scenes; \(cellCount) cell scenes")
// 打印輸出 "6 mansion scenes; 2 cell scenes"