Swift3.0P1 语法指南——字符串与字符

原档:https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/StringsAndCharacters.html#//apple_ref/doc/uid/TP40014097-CH7-ID285

1、字符串字面值

1 let someString = "Some string literal value"

2、初始化空的字符串

两种方法:

1 var emptyString = ""               // empty string literal
2 var anotherEmptyString = String()  // initializer syntax
3 // these two strings are both empty, and are equivalent to each other

可以通过isEmpty属性检查字符串是否为空:

1 if emptyString.isEmpty {
2     print("Nothing to see here")
3 }
4 // prints "Nothing to see here"

3、字符串的可变性

通过声明为常量或变量来判断字符串是否可变。

1 var variableString = "Horse"
2 variableString += " and carriage"
3 // variableString is now "Horse and carriage"
4  
5 let constantString = "Highlander"
6 constantString += " and another Highlander"
7 // this reports a compile-time error - a constant string cannot be modified

这种方式与Objective-C不同。Objective-C需要用NSString和NSMutableString两种类来分别表示不可变字符串和可变字符串。

4、字符串是值类型

Swift 的 String 类型是值类型。如果你创建了一个新的字符串值,那么当其进行常量、变量赋值操作或在函数中传递时,会进行值拷贝。在不同情况下,都会对已有字符串值创建新副本,并对该新副本进行传递或赋值。

Swift 默认字符串拷贝的方式保证了在函数/方法中传递的是字符串的值,这种方式保证了你可以独有该字符串的值,无论它来自哪里,传递给你的字符串本身不会被更改,除非是你自己更改它。
在实际编译时,Swift编译器会优化字符串的使用,使实际的复制只发生在绝对必要的情况下,这意味着你始终可以在将字符串作为值类型的同时获得极高的性能。

5、使用字符

通过遍历字符串的characters属性,可以访问字符串的单个字符:
1 for character in "Dog!🐶".characters {
2     print(character)
3 }
4 // D
5 // o
6 // g
7 // !
8 // 🐶

当然,也可以单独声明一个Character字符:

1 let exclamationMark: Character = "!"

String可以用Character数组来构造:

1 let catCharacters: [Character] = ["C", "a", "t", "!", "🐱"]
2 let catString = String(catCharacters)
3 print(catString)
4 // prints "Cat!🐱"

6、连接字符和字符串

连接字符串:

1 let string1 = "hello"
2 let string2 = " there"
3 var welcome = string1 + string2
4 // welcome now equals "hello there"

也可以用已有字符串接在当前字符串后面:

1 var instruction = "look over"
2 instruction += string2
3 // instruction now equals "look over there"

可以在字符串后面添加字符:

1 let exclamationMark: Character = "!"
2 welcome.append(exclamationMark)
3 // welcome now equals "hello there!"

注意:Character只是一个单独的字符,不能在后面连接字符或字符串。

7、字符串插值

1 let multiplier = 3
2 let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
3 // message is "3 times 2.5 is 7.5"

注意:插值字符串中写在括号中的表达式不能包含非转义双引号 (") 和反斜杠 (\),并且不能包含回车或换行符。(V2.1)

注意:插值字符串中写在括号中的表达式不能包含反斜杠 (\),不能包含回车或换行符。(V3.0P1)

8、Unicode

Unicode 是文本编码和表示的国际标准。它可以用标准格式表达来自任意语言几乎所有的字符,并能够对文本文件或网页这样的外部资源中的字符进行读写操作。
Swift 的字符串和字符类型是完全兼容 Unicode 的,它支持如下所述的一系列不同的 Unicode 编码。
(1)Unicode Scalars
Swift原生的String类型由Unicode Scalars构建而来。Unicode Scalar是字符或者修饰符的唯一21bit数字,,例如 U+0061 表示小写的拉丁字母A ("a"),U+1F425 表示正面站立的鸡宝宝 ("🐥")。
注意:Unicode Scalar指的是U+0000到U+D7FF之间、或U+E000到U+10FFFF之间的Unicode代码点,不包括U+D800到U+DFFF之间的代码点。
需要注意的是,并不是所有的Unicode标量都可以赋值给Character,有一些保留的Unicode标量目前是不能使用的。
通常,可以赋值给字符的Unicode Scalars都有名字,例如 LATIN SMALL LETTER A和FRONT-FACING BABY CHICK。

9、字符串字面量中的特殊字符

字符串字面量可以包含以下特殊字符:
- 转义特殊字符 \0 (空字符)、\\(反斜线)、\t (水平制表符)、\n (换行符)、\r (回车符)、\" (双引号)、\' (单引号)。
- 任意的Unicode Scalar,写成  \u{n},其中n是1~8位的Unicode十六进制编码,其值为有效的Unicode代码点。
1 let wiseWords = "\"Imagination is more important than knowledge\" - Einstein"
2 // "Imagination is more important than knowledge" - Einstein
3 let dollarSign = "\u{24}"        // $,  Unicode scalar U+0024
4 let blackHeart = "\u{2665}"      // ♥,  Unicode scalar U+2665
5 let sparklingHeart = "\u{1F496}" // ?, Unicode scalar U+1F496

10、扩展字形集群

Swift的Character类型的每一个实例都是一个扩展字形集群。一个扩展字形集群是一个或多个Unicode Scalar的序列(组合后便产生了一个人类可读的字符)。

例如:é可以由一个单独的Unicode标量来表示(LATIN SMALL LETTER E WITH ACUTE,或 U+00E9),但是,也可以由 e (LATIN SMALL LETTER E, or U+0065)和COMBINING ACUTE ACCENT  (U+0301)组成。无论如何,é都看做是一个Swift Character值,表示一个扩展字形集群。
1 let eAcute: Character = "\u{E9}"                         // é
2 let combinedEAcute: Character = "\u{65}\u{301}"          // e followed by ́
3 // eAcute is é, combinedEAcute is é

扩展字形集群是一种灵活的方式,通过不同的组合方式来构造同一个Character值。

1 let precomposed: Character = "\u{D55C}"                  //
2 let decomposed: Character = "\u{1112}\u{1161}\u{11AB}"   // ᄒ, ᅡ, ᆫ
3 // precomposed is 한, decomposed is 한

可以在字符外面加上封闭的符号来构造新的字符:

1 let enclosedEAcute: Character = "\u{E9}\u{20DD}"
2 // enclosedEAcute is é⃝

用于地区表示的符号可以成对组合来构造一个字符:

let regionalIndicatorForUS: Character = "\u{1F1FA}\u{1F1F8}"
// regionalIndicatorForUS is 🇺🇸

11、字符计数

1 let unusualMenagerie = "Koala 🐨, Snail 🐌, Penguin 🐧, Dromedary 🐪"
2 print("unusualMenagerie has \(unusualMenagerie.characters.count) characters")
3 // prints "unusualMenagerie has 40 characters"

注意:扩展字形集群的使用,意味着,字符串的连接和修改可能并不会改变字符串的字符的数目。

例如:

1 var word = "cafe"
2 print("the number of characters in \(word) is \(word.characters.count)")
3 // prints "the number of characters in cafe is 4"
4  
5 word += "\u{301}"    // COMBINING ACUTE ACCENT, U+0301
6  
7 print("the number of characters in \(word) is \(word.characters.count)")
8 // prints "the number of characters in café is 4"

注意:扩展字形集群可以被分解成一个或多个Unicode Scalar。这意味着,不同的字符、同一字符的不同表示方法,将需要不同大小的存储内存。因此,要计算一个字符串的字符数目,一定要根据字符串的字面值来判断扩展字形集群的数目。

返回的characters属性的count并不总是等于NSString的length属性,因为length属性是根据UTF-16编码计算出来的长度,并非Unicode扩展字形集群的数目。

12、访问和修改String

(1)String的索引

每一个String都有一个index类型,String.Index,对应于每一个Character的位置。

如上所述,不同的字符需要不同的内存,为了找到特定位置的Character,你需要遍历String的Unicode Scalar。因此,String不能用整型数据来索引。

用startIndex属性来访问String的第一个字符的位置。

endIndex访问String的最后一个字符的后一个位置。因此,endIndex并不是一个有效的下标。

如果String为空,startIndex等于endIndex。

String.Index可以调用predecessor()访问当前位置的前一个索引,successor()访问当前位置的后一个索引。String的任意索引都可以通过其他索引的successor()和predecessor()方法的组合、或者advancedBy()方法访问到。访问的下标如果超过String的范围,将会产生运行时错误。(V2.1)

给定一个index, 你可以用String的index(before:)index(after:)访问当前index的前一个/后一个索引,用index(_:offsetBy:)可以访问距离当前index几个位置的index.(V3.0P1)

可以使用String的索引来访问该位置上的字符:

 1 let greeting = "Guten Tag!"
 2 greeting[greeting.startIndex]
 3 // G
 4 greeting[greeting.endIndex.predecessor()]
 5 // !
 6 greeting[greeting.startIndex.successor()]
 7 // u
 8 let index = greeting.startIndex.advancedBy(7)
 9 greeting[index]
10 // a

索引越界,将会产生错误:

1 greeting[greeting.endIndex] // error
2 greeting.endIndex.successor() // error

用characters的indices属性,可以获得字符的索引范围:

1 for index in greeting.characters.indices {
2     print("\(greeting[index]) ", terminator: "")
3 }
4 // prints "G u t e n   T a g !"

注意:任意遵循Indexable协议的类型,都有startIndexendIndex属性,以及 index(before:), index(after:),和 index(_:offsetBy:)方法。String类型如此,其他类型包括 Array, DictionarySet类型也是如此。

(2)插入和移除

插入,用insert(_:at:)方法:(V2.2是insert(_:atIndex:)方法)

1 var welcome = "hello"
2 welcome.insert("!", at: welcome.endIndex)
3 // welcome now equals "hello!"

在指定位置插入另一字符串的内容:

1 welcome.insert(contentsOf:" there".characters, at: welcome.index(before: welcome.endIndex))
2 // welcome now equals "hello there!"

V2.1的对应方法如下:

1 welcome.insertContentsOf(" there".characters, at: welcome.endIndex.predecessor())
2 // welcome now equals "hello there!"

移除指定下标开始的字符串:

welcome.remove(at: welcome.index(before: welcome.endIndex))
// welcome now equals "hello there"

V2.1的对应方法如下:

1 welcome.removeAtIndex(welcome.endIndex.predecessor())
2 // welcome now equals "hello there"

移除指定范围的字符串:

1 let range = welcome.index(welcome.endIndex, offsetBy: -6)..<welcome.endIndex
2 welcome.removeSubrange(range)
3 // welcome now equals "hello"

V2.1的对应方法如下:

1 let range = welcome.endIndex.advancedBy(-6)..<welcome.endIndex
2 welcome.removeRange(range)
3 // welcome now equals "hello"

注意:任何遵循RangeReplaceableIndexable协议的类型,都可以用insert(_:at:), insert(contentsOf:at:), remove(at:), 和removeSubrange(_:)方法。

String, Array, Dictionary, 和Set都是这样。

13、字符串的比较

(1)相等

1 let quotation = "We're a lot alike, you and I."
2 let sameQuotation = "We're a lot alike, you and I."
3 if quotation == sameQuotation {
4     print("These two strings are considered equal")
5 }
6 // prints "These two strings are considered equal"

如果两个字符串(或两个字符)的扩展字形集群符合canonically equivalent,则两个字符串(或字符)看做是相等的。

如果两个扩展字形集群的字面意义和字形都是相同的,则认为符合canonically equivalent,即使它们被分解成不同的Unicode Scalar,它们也看做是相等的。

例如,LATIN SMALL LETTER E WITH ACUTE (U+00E9),以及LATIN SMALL LETTER E (U+0065)+COMBINING ACUTE ACCENT (U+0301)是é字符的两种构建方式,两者看做canonically equivalent.

 1 // "Voulez-vous un café?" using LATIN SMALL LETTER E WITH ACUTE
 2 let eAcuteQuestion = "Voulez-vous un caf\u{E9}?"
 3  
 4 // "Voulez-vous un café?" using LATIN SMALL LETTER E and COMBINING ACUTE ACCENT
 5 let combinedEAcuteQuestion = "Voulez-vous un caf\u{65}\u{301}?"
 6  
 7 if eAcuteQuestion == combinedEAcuteQuestion {
 8     print("These two strings are considered equal")
 9 }
10 // prints "These two strings are considered equal"

相反,英语中使用的LATIN CAPITAL LETTER A(U+0041或"A"),和CYRILLIC CAPITAL LETTER A (U+0410,或 "А")是不相等的。两者看上去形状相同,但是有不同的字面意义。

1 let latinCapitalLetterA: Character = "\u{41}"
2  
3 let cyrillicCapitalLetterA: Character = "\u{0410}"
4  
5 if latinCapitalLetterA != cyrillicCapitalLetterA {
6     print("These two characters are not equivalent")
7 }
8 // prints "These two characters are not equivalent"

注意:Swift中的字符串和字符都不是区域敏感的。

(2)前缀和后缀相等

String的hasPrefix(_:)和hasSuffix(_:)返回一个布尔型变量。

 1 let romeoAndJuliet = [
 2     "Act 1 Scene 1: Verona, A public place",
 3     "Act 1 Scene 2: Capulet's mansion",
 4     "Act 1 Scene 3: A room in Capulet's mansion",
 5     "Act 1 Scene 4: A street outside Capulet's mansion",
 6     "Act 1 Scene 5: The Great Hall in Capulet's mansion",
 7     "Act 2 Scene 1: Outside Capulet's mansion",
 8     "Act 2 Scene 2: Capulet's orchard",
 9     "Act 2 Scene 3: Outside Friar Lawrence's cell",
10     "Act 2 Scene 4: A street in Verona",
11     "Act 2 Scene 5: Capulet's mansion",
12     "Act 2 Scene 6: Friar Lawrence's cell"
13 ]

可以用hasPrefix(_:)方法来计数有多少个Act 1场景,并且播放:

 

1 var act1SceneCount = 0
2 for scene in romeoAndJuliet {
3     if scene.hasPrefix("Act 1 ") {
4         ++act1SceneCount
5     }
6 }
7 print("There are \(act1SceneCount) scenes in Act 1")
8 // prints "There are 5 scenes in Act 1"

使用hasSuffix(_:)方法:

 1 var mansionCount = 0
 2 var cellCount = 0
 3 for scene in romeoAndJuliet {
 4     if scene.hasSuffix("Capulet's mansion") {
 5         ++mansionCount
 6     } else if scene.hasSuffix("Friar Lawrence's cell") {
 7         ++cellCount
 8     }
 9 }
10 print("\(mansionCount) mansion scenes; \(cellCount) cell scenes")
11 // prints "6 mansion scenes; 2 cell scenes"

14、字符串的Unicode表示

let dogString = "Dog‼🐶"

(1)uft-8表示

1 for codeUnit in dogString.utf8 {
2     print("\(codeUnit) ", terminator: "")
3 }
4 print("")
5 // 68 111 103 226 128 188 240 159 144 182

(2)utf-16表示

1 for codeUnit in dogString.utf16 {
2     print("\(codeUnit) ", terminator: "")
3 }
4 print("")
5 // 68 111 103 8252 55357 56374

(3)Unicode Scalar表示

可以通过String的unicodeScalars访问字符串的Unicode Scalar表示, 这个属性的类型是UnicodeScalarView(一种UnicodeScalar的集合)。

每个UnicodeScalar都有一个21bit的value属性,用UInt32进行表示。

1 for scalar in dogString.unicodeScalars {
2     print("\(scalar.value) ", terminator: "")
3 }
4 print("")
5 // 68 111 103 8252 128054

每个Unicode标量值可以构造一个字符串,比如用于字符串插值:

for scalar in dogString.unicodeScalars {
    print("\(scalar) ")
}
// D
// o
// g
//// 🐶

 

posted @ 2016-07-04 17:17  庭庭小妖  阅读(394)  评论(0编辑  收藏  举报