字符串和字符
- 字符串 String是字符Character的集合
- 通过
+
符号就可拼接两个字符串 - 能否更改字符串的值,取决于其被定义为常量还是变量
Swift 的
String
类型与 FoundationNSString
类进行了无缝桥接。调用NSString
的方法,无需进行类型转换。更多关于在 Foundation 和 Cocoa 中使用
String
的信息请查看 Bridging Between String and NSString。
字符串字面量
- 字符串字面量作为常量或者变量的初始值:
let someString = "Some string literal value"
多行字符串字面量
- 用三个双引号引起来的一系列字符
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."
"""
- 为代码可读性,可在代码里加反斜杠(
\
),对字符串换行(输出的字符串不换行, 且没有反斜杠)
let softWrappedQuotation = """
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."
"""
- 可以在代码中,使用缩进,让代码工整对齐,但字符串里不会有影响
- 如果你在某行的空格超过了结束的双引号( """ ),那么这些空格会被包含。
字符串字面量的特殊字符
-
转义特殊字符 \0 (空字符), \ (反斜杠), \t (水平制表符), \n (换行符), \r(回车符), " (双引号) 以及 ' (单引号)
-
Unicode 标量,写成
\u{n}
(u 为小写),其中n
为任意一到八位十六进制数且可用的 Unicode 位码
let wiseWords = "\"Imagination is more important than knowledge\" - Einstein"
// "Imageination is more important than knowledge" - Enistein
let dollarSign = "\u{24}" // $,Unicode 标量 U+0024
let blackHeart = "\u{2665}" // ♥,Unicode 标量 U+2665
let sparklingHeart = "\u{1F496}" // 💖,Unicode 标量 U+1F496
- 多行字符串, 直接使用双引号(
"
)而不必加上转义符(\
) - 多行字符串字面量中使用
"""
, 使用至少一个转义符(\
)
let threeDoubleQuotes = """
Escaping the first quote \"""
Escaping all three quotes \"\"\"
"""
扩展字符串分隔符
- 井号( # )包裹,可以使特殊字符不生效
#"Line 1\nLine 2"#
- 使某个特殊字符生效,使用#号标记
#"Line 1\#nLine 2"#
###"Line1\###nLine2"###
- 多行字符串,包含"""
let threeMoreDoubleQuotationMarks = #"""
Here are three more double quotes: """
"""#
初始化空字符串
var emptyString = "" // 空字符串字面量
var anotherEmptyString = String() // 初始化方法
// 两个字符串均为空并等价。
- 通过检查
Bool
类型的isEmpty
属性来判断该字符串是否为空
if emptyString.isEmpty {
print("Nothing to see here")
}
// 打印输出:“Nothing to see here”
字符串可变性
var variableString = "Horse"
variableString += " and carriage"
// variableString 现在为 "Horse and carriage"
let constantString = "Highlander"
constantString += " and another Highlander"
// 这会报告一个编译错误(compile-time error) - 常量字符串不可以被修改。
字符串是值类型
-
Swift 中
String
类型是值类型 -
如果你创建了一个新的字符串,那么当其进行常量、变量赋值操作,或在函数/方法中传递时,会进行值拷贝。
使用字符
-
for-in
循环来遍历, 获取字符串中每一个字符的值
for character in "Dog!🐶" {
print(character)
}
// D
// o
// g
// !
// 🐶
-
Character
类型
let exclamationMark: Character = "!"
- 字符串可通过值类型为
Character
的数组来初始化
let catCharacters: [Character] = ["C", "a", "t", "!", "🐱"]
let catString = String(catCharacters)
print(catString)
// 打印输出:“Cat!🐱”
连接字符串和字符
- 通过加法运算符(
+
)相加在一起(或称“连接”)创建一个新的字符串:
let string1 = "hello"
let string2 = " there"
var welcome = string1 + string2
// welcome 现在等于 "hello there"
- 通过加法赋值运算符(
+=
)将一个字符串, 添加到另一个字符串变量上:
var instruction = "look over"
instruction += string2
// instruction 现在等于 "look over there"
你不能把 String或者 Character追加到已经存在的 Character变量当中,因为 Character值能且只能包含一个字符。
字符串插值
- 字符串插值:在字符串中插入 常量、变量、字面量、表达式
let multiplier = 3
let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
// message 是 "3 times 2.5 is 7.5"
- 扩展字符串分隔符 使插值符号失效/不生效
print(#"Write an interpolated string in Swift using \(multiplier)."#)
// 打印 "Write an interpolated string in Swift using \(multiplier)."
- 部分生效
print(#"\(6 * 7) times is \#(6 * 7)."#)
// 打印 \(6 * 7) times is 42.
Unicode
- Swift 的
String
和Character
类型是完全兼容 Unicode 标准的。
Unicode 标量
-
String
类型是基于 Unicode 标量 建立 -
Unicode 标量: 是对应字符或者修饰符的唯一的 21 位数字
-
U+0061
表示小写的拉丁字母(LATIN SMALL LETTER A
)("a
") -
U+1F425
表示小鸡表情(FRONT-FACING BABY CHICK
)("🐥
")
print("\u{0061}")// a
print("--------------------")
print("\u{1F425}")// 🐥
print("--------------------")
Unicode 标量码位位于 U+0000到 U+D7FF或者 U+E000到 U+10FFFF之间。Unicode 标量码位不包括从 U+D800到 U+DFFF的16位码元码位。
- 不是所有的 21 位 Unicode 标量都指定了字符——有些标量是为将来所保留或用于 UTF-16 编码。
- 有了字符的标量通常来说也会有个名字,比如上边例子中的
LATIN SMALL LETTER A
和FRONT-FACING BABY CHICK
。
可扩展的字形群集
-
Character
类型代表一个可扩展的字形集 - 一个可扩展的字形群构成了人类可读的单个字符,它由一个或多个 Unicode 标量的序列组成。
- 字母
é
代表了一个单一的 Swift 的Character
值, 同时代表了一个可扩展的字形群 - 第一种情况,这个字形群包含一个单一标量
- 第二种情况,它是包含两个标量的字形群
let eAcute: Character = "\u{E9}" // é
let combinedEAcute: Character = "\u{65}\u{301}" // e 后面加上 ́
// eAcute 是 é, combinedEAcute 是 é
-
可扩展的字形集: 是一个将许多复杂的脚本字符, 表示为单个字符值的灵活方式
-
朝鲜语字母表的韩语音节能表示为组合或分解的有序排列
let precomposed: Character = "\u{D55C}" // 한
let decomposed: Character = "\u{1112}\u{1161}\u{11AB}" // ᄒ, ᅡ, ᆫ
// precomposed 是 한, decomposed 是 한
- 扩展字形集群允许封闭标记的标量 (比如 COMBINING ENCLOSING CIRCLE, 或者说 U+20DD) 作为单一 Character值来圈住其他 Unicode 标量:
let enclosedEAcute: Character = "\u{E9}\u{20DD}"
// enclosedEAcute 是 é⃝
- 区域指示符号的 Unicode 标量可以成对组合来成为单一的 Character值,比如说这个 REGIONAL INDICATOR SYMBOL LETTER U ( U+1F1FA)和 REGIONAL INDICATOR SYMBOL LETTER S (U+1F1F8):
let regionalIndicatorForUS: Character = "\u{1F1FA}\u{1F1F8}"
// regionalIndicatorForUS 是 🇺🇸
计算字符数量/字符统计
- Character值的总数,使用字符串的 count属性
let unusualMenagerie = "Koala 🐨, Snail 🐌, Penguin 🐧, Dromedary 🐪"
print("unusualMenagerie has \(unusualMenagerie.count) characters")
// 打印输出“unusualMenagerie has 40 characters”
注意: 使用可拓展的字符群集作为
Character
值来连接或改变字符串时,并不一定会更改字符串的字符数量。
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
print("the number of characters in \(word) is \(word.count)")
// 打印输出“the number of characters in café is 4”
- 扩展字形集群能够组合一个或者多个 Unicode 标量。这意味着不同的字符——以及相同字符的不同表示——能够获得不同大小的内存来储存. 特殊的长字符串值,要注意 count属性为了确定字符串中的字符要遍历整个字符串的 Unicode 标量。
count
属性返回的字符数量并不总是与包含相同字符的NSString
的length
属性相同NSString
的length
属性是利用 UTF-16 表示的十六位代码单元数字,而不是 Unicode 可扩展的字符群集
访问和修改字符串
- 通过字符串的属性和方法来访问和修改它,当然也可以用下标语法完成
字符串索引
- 每一个
String
值都有一个关联的索引(index)类型,String.Index
,它对应着字符串中的每一个Character
的位置 - 使用 startIndex属性来访问 String中第一个 Character的位置
- endIndex属性就是 String中最后一个字符后的位置
- endIndex属性并不是字符串下标脚本的合法实际参数。如果String为空,则 startIndex与 endIndex相等。
- 用 index(before:) 和 index(after:) 方法来访问给定索引的前后
- 给定索引更远的索引,你可以使用 index(_:offsetBy:) 方法
let greeting = "Guten Tag!"
greeting[greeting.startIndex]
// G
greeting[greeting.index(before: greeting.endIndex)]
// !
greeting[greeting.index(after: greeting.startIndex)]
// u
let index = greeting.index(greeting.startIndex, offsetBy: 7)
greeting[index]
// a
- 获取越界索引对应的
Character
,将引发一个运行时错误。
greeting[greeting.endIndex] // error
greeting.index(after: greeting.endIndex) // error
- 用
indices
属性会创建一个包含全部索引的范围(Range
)
for index in greeting.indices {
print("\(greeting[index]) ", terminator: "")
}
// 打印输出“G u t e n T a g ! ”
可以在任何遵循了 Collection 协议的类型中使用 startIndex 和 endIndex 属性以及 index(before:) ,index(after:) 和 index(_:offsetBy:) 方法。这包括这里使用的 String ,还有集合类型比如 Array ,Dictionary 和 Set
插入和删除
- 特定位置插入字符,使用 insert(_:at:)方法
- 插入一个段字符串, 调用
insert(contentsOf:at:)
方法
var welcome = "hello"
welcome.insert("!", at: welcome.endIndex)
// welcome 变量现在等于 "hello!"
welcome.insert(contentsOf:" there", at: welcome.index(before: welcome.endIndex))
// welcome 变量现在等于 "hello there!"
- 移除字符,使用 remove(at:)方法
- 移除一小段字符串,removeSubrange(_:)
welcome.remove(at: welcome.index(before: welcome.endIndex))
// welcome 现在等于 "hello there"
let range = welcome.index(welcome.endIndex, offsetBy: -6)..<welcome.endIndex
welcome.removeSubrange(range)
// welcome 现在等于 "hello"
任意一个确认的并遵循
RangeReplaceableCollection
协议的类型里面,可使用insert(_:at:)
、insert(contentsOf:at:)
、remove(at:)
和removeSubrange(_:)
方法, 在如上文用在String
中,也用在Array
、Dictionary
和Set
子字符串
- 使用下标或者
prefix(_:)
之类的方法 —— 就可以得到一个Substring
的实例,而非另外一个String
-
Substring
绝大部分函数都跟String
一样 - 与字符串不同,在字符串上执行动作的话你应该使用子字符串执行短期处理。当你想要把结果保存得长久一点时,你需要把子字符串转换为 String 实例
let greeting = "Hello, world!"
let index = greeting.index(of: ",") ?? greeting.endIndex
let beginning = greeting[..<index]
// beginning is "Hello"
// Convert the result to a String for long-term storage.
let newString = String(beginning)
-
Substring
可以重用原String
的内存空间,或者另一个Substring
的内存空间 -
newString
是一个String
—— 它是使用Substring
创建的,拥有一片自己的内存空间。
String
和Substring
都遵循StringProtocol
协议,这意味着操作字符串的函数使用StringProtocol
会更加方便。你可以传入String
或Substring
去调用函数
比较字符串
三种方式比较文本值:字符串字符相等、前缀相等和后缀相等。
字符串/字符相等
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”
- 两个字符串(或者两个字符)的可扩展的字形群集是标准相等,那它们是相等的,
- 只要可扩展的字形群集有同样的语言意义和外观, 即使它们是由不同的 Unicode 标量构成
- 如,
LATIN SMALL LETTER E WITH ACUTE
(U+00E9
)就是标准相等于LATIN SMALL LETTER E
(U+0065
)后面加上COMBINING ACUTE ACCENT
(U+0301
)
// "Voulez-vous un café?" 使用 LATIN SMALL LETTER E WITH ACUTE
let eAcuteQuestion = "Voulez-vous un caf\u{E9}?"
// "Voulez-vous un café?" 使用 LATIN SMALL LETTER E and COMBINING ACUTE ACCENT
let combinedEAcuteQuestion = "Voulez-vous un caf\u{65}\u{301}?"
if eAcuteQuestion == combinedEAcuteQuestion {
print("These two strings are considered equal")
}
// 打印输出“These two strings are considered equal”
- 相反,英语中的
LATIN CAPITAL LETTER A
(U+0041
,或者A
)不等于俄语中的CYRILLIC CAPITAL LETTER A
(U+0410
,或者A
)。两个字符看着是一样的,但却有不同的语言意义:
let latinCapitalLetterA: Character = "\u{41}"
let cyrillicCapitalLetterA: Character = "\u{0410}"
if latinCapitalLetterA != cyrillicCapitalLetterA {
print("These two characters are not equivalent")
}
// 打印“These two characters are not equivalent”
前缀/后缀相等
-
hasPrefix(_:)
/hasSuffix(_:)
方法检查字符串是否有特定前缀/后缀,两个方法均接收一个String
类型的参数,并返回一个布尔值
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"
]
- 用
hasPrefix(_:)
方法来计算话剧中第一幕的场景数:
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”
- 用
hasSuffix(_:)
方法来计算发生在不同地方的场景数:
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”
字符串的 Unicode 表示形式
-
一个 Unicode 字符串被写进文本文件或者其他储存时,字符串中的 Unicode 标量会用 Unicode 定义的几种
编码格式
(encoding forms)编码 -
每一个字符串中的小块编码都被称
代码单元
(code units), 包括 UTF-8 编码格式(编码字符串为 8 位的代码单元), UTF-16 编码格式(编码字符串位 16 位的代码单元),以及 UTF-32 编码格式(编码字符串32位的代码单元) -
访问字符串的 Unicode 表示形式, 利用
for-in
来对字符串进行遍历 -
其他三种 Unicode 兼容的方式访问字符串的值
- UTF-8 代码单元集合(利用字符串的
utf8
属性进行访问) - UTF-16 代码单元集合(利用字符串的
utf16
属性进行访问) - 21 位的 Unicode 标量值集合,也就是字符串的 UTF-32 编码格式(利用字符串的
unicodeScalars
属性进行访问)
- UTF-8 代码单元集合(利用字符串的
-
D
,o
,g
,‼
(DOUBLE EXCLAMATION MARK
, Unicode 标量U+203C
)和🐶
(DOG FACE
,Unicode 标量为U+1F436
)组成的字符串中的每一个字符代表着一种不同的表示
let dogString = "Dog‼🐶"
UTF-8 表示
-
String
的utf8
属性可访问它的UTF-8
表示 - 为
String.UTF8View
类型的属性,UTF8View
是无符号 8 位(UInt8
)值的集合,每一个UInt8
值都是一个字符的 UTF-8 表示
UTF-16 表示
-
String
的utf16
属性来访问它的UTF-16
表示
Unicode 标量表示
- 你可以通过遍历
String
值的unicodeScalars
属性来访问它的 Unicode 标量表示
网友评论