Swift learning part 3 - 字符串和字符

本文介绍了Swift中字符串的使用,包括多行字符串字面量、特殊字符、扩展字符串分隔符、初始化空字符串、字符串的可变性、连接和插入字符、字符串插值、计算字符数量、访问与修改字符串、子字符串以及字符串比较。同时,文章指出Swift的String与Foundation的NSString之间的无缝桥接,使得字符串操作更加便捷。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


字符串是诸如 “hello, world”,“abcdefg” 这样有序的字符(Character)类型的值的集合,通过 String 类型来表示。

注意:
Swift 的 String 类型与 Foundation NSString 类进行了无缝桥接。Foundation 还对 String 进行扩展使其可以访问 NSString 类型中定义的方法。这意味着调用那些 NSString 的方法,你无需进行任何类型转换。

字符串字面量

字符串字面量可以用于为常量和变量提供初始值。

let something = "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."
"""
print(quotation)

如果代码中,多行字符串字面量包含换行符的话,则多行字符串字面量中也会包含换行符。如果代码中为了增加可读性包含了换行符,但又不想在多行字符串字面量中出现换行符的话,可以在行尾写一个反斜杠(\)作为续行符。

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位码。

下面的代码为各种特殊字符的使用示例。wiseWords 常量包含了两个双引号,dollarSign、blackHeart 和 sparklingHeart 常量演示了三种不同格式的 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”# 打印换行符转义序列(\n)而不是进行换行打印。

如果需要字符串文字中字符的特殊效果,请匹配转义字符(\)后面添加与起始位置个数相匹配的 # 符。
例如,如果你的字符串是 #“Line 1 nLine 2”# 并且您想要换行,则可以使用 #“Line 1 #nLine 2”# 来代替。
同样,###“Line1 ### nLine2”### 也可以实现换行效果。

扩展分隔符创建的字符串文字也可以是多行字符串文字。
你可以使用扩展分隔符在多行字符串中包含文本 “”",覆盖原有的结束文字的默认行为。例如:

let threeMoreDoubleQuotationMarks = #"""
Here are three more double quotes: """
"""#

初始化空字符串

要创建一个空字符串作为初始值,可以将空的字符串字面量赋值给变量,也可以初始化一个新的String实例。

var emptyString = ""
var anotherEmptyString = String() // 两个字符串均为空并且等价

通过检查其 Bool 类型的 isEmpty 属性来判断该字符串是否为空。

if emptyString.isEmpty {
    print("Nothing to see here")
}

字符串可变性

可以通过将一个特定字符串分配给一个变量来对其进行修改,分配给一个常量的话不能对其进行修改。

var variableString = "Horse"
variableString += " and carriage"

字符串是值类型

如果创建了一个新的字符串,那么当其进行常量、变量赋值操作,或在函数/方法中传递时,会进行拷贝。任何情况下,都会对已有字符串值创建新副本,并对该新副本进行传递或赋值操作。

在实际编译时,Swift 编译器会优化字符串的使用,使实际的复制只发生在绝对必要的情况下,这意味着你将字符串作为值类型的同时可以获得极高的性能。

使用字符

可以通过 for-in 循环来遍历字符串,获取字符串中每一个字符的值。

for character in "Dog!" {
    print(character)
}

通过标明一个 Character 类型并用字符字面量进行赋值,可以建立一个独立的字符常量或变量。

let exclamationMark: Character = "!"

字符串可以通过传递一个值类型为 Character 的数组作为自变量来初始化。

let catCharacters: [Character] = ["C", "a", "t", "!"]
let catString = String(catCharacters)

连接字符串和字符

字符串可以通过加法运算符(+)相加在一起创建一个新的字符串。

let string1 = "hello"
let string2 = "there"
var welcome = string1 + string2 // welcome 等于 "hello there"

可以通过加法赋值运算符(+=)将一个字符串添加到一个已经存在的字符串变量上。

var instruction = "look over"
instruction += string2 // instruction 等于 "look over there"

可以使用 append() 方法将一个字符附加到一个字符串变量的尾部。

welcome.append(exclamationMark) // welcome 等于 "hello there!"

如果需要使用多行字符串字面量来拼接字符串,并且需要拼接后的字符串每一行都以换行符结尾,那么前边的字符串的每一行都要以换行符结尾。

let badStart = """
one
two
"""
let end = """
three
"""
print(badStart + end)
// 打印两行
// one
// twothree
let goodStart = """
one
two

"""
print(goodStart + end)
// 打印三行
// one
// two
// three

字符串插值

字符串插值是一种构建新字符串的方式,可以在其中包含常量、变量、字面量和表达式,插入的字符串字面量的每一项都要在以反斜线为前缀的圆括号中。

let multiplier = 3
let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"

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

计算字符数量

获取一个字符串中 Character 值的数量。

let unusualMenagerie = "Koala ?, Snailm?, Penguin ?, Dromedary ?"
print(unusualMenagerie.count)

访问和修改字符串

可以通过字符串的属性和方法来访问和修改它,也可以用下标语法完成。

字符串索引

每一个 String 值都有一个关联的索引(index)类型,String.index,它对应着字符串中每一个 Character 的位置。不同的字符可能会占用不同数量的内存空间,确定 Character 的位置需要从 String 开头遍历每一个 Unicode 标量直到结尾,因此 Swif 字符串不能用整数做索引。

使用 startIndex 属性可以获取一个 String 的第一个 Character 的索引;使用 endIndex 属性可以获取最后一个 Character 的 后一个位置 的索引,endIndex 不能作为一个字符串的有效下标。如果字符串为空,startIndex 和 endIndex 是相等的。通过调用 String 的 index(before:) 或 index(after:) 方法,可以立即得到前面或后面的一个索引,可以通过调用 index(_:offsetBy:) 方法获取对应偏移量的索引,可以使用下标语法来访问 String 特定索引的 Character。

let greeting = "Guten tag!"
print(greeting[greeting.startIndex])
print(greeting[greeting.index(before: greeting.endIndex)])
print(greeting[greeting.index(after: greeting.startIndex)])
print(greeting[greeting.index(greeting.startIndex, offsetBy: 7)])
print(greeting.endIndex)

试图获取越界索引对应的 Character 将引发一个运行时错误。

使用 indices 属性会创建一个包含全部索引的范围(Range),用来在一个字符串中访问单个字符。

for index in greeting.indices {
    print("\(greeting[index]) ", terminator:"")
}

你可以使用 startIndex 和 endIndex 或者 index(before:)、index(after:) 和 index(_:offserBy:) 方法在任意一个确认的并遵循 Collection 协议的类型里面,例如在 Array、Dictionary 和 Set 中。

插入和删除

调用 insert(_:at:) 方法可以在一个字符串的指定索引插入一个字符,调用 insert(contentsOf:at:) 方法可以在一个字符串的指定索引插入一段字符串。

var welcome_ = "hello"
welcome_.insert("!", at: welcome_.endIndex)
print(welcome_)
welcome_.insert(contentsOf: " there", at: welcome_.index(before: welcome_.endIndex))
print(welcome_)

调用 remove(at:) 方法可以在一个字符串的指定索引删除一个字符,调用 removeSubrange(_? 方法可以在一个字符串的指定索引删除一个子字符串。

welcome_.remove(at: welcome_.index(before: welcome_.endIndex))
print(welcome_)
let range = welcome_.index(welcome_.endIndex, offsetBy: -6)..<welcome_.endIndex
welcome_.removeSubrange(range)
print(welcome_)

你可以使用 insert(:at:) 和 insert(contentsOf:at:)、remove(at:)、removeSubrange(? 方法在任意一个确认的并遵循 RangeReplaceableCollection 协议的类型里面,例如在 Array、Dictionary 和 Set 中。

子字符串

当你从字符串中获取一个子字符串时,可以得到一个 SubString 实例,而非另外一个 String。Swift 中的 SubString 绝大部分函数都跟 String 一样,意味着可以使用同样的的方式去操作 SubString 和 String。跟 String 不同,只有在短时间内需要操作字符串时,才会使用 SubString,需要长时间保存结果时,就把 SubString 转化为 String。

let greeting_ = "Hello, world!"
let index = greeting_.firstIndex(of: ",") ?? greeting_.endIndex
let beginning = greeting_[..<index]
let newString = String(beginning)

就像 String,每一个 SubString 都会在内存里保存字符集。而 String 和 SubString 的区别在于性能优化上,SubString 可以重用原 String 的内存空间,或者另一个 SubString 的内存空间(String 也有同样的优化,但如果两个 String 共享内存的话,它们就会相等)。这一优化意味着你在修改 String 和 SubString 之前都不需要消耗性能去复制内存。就像前面说的那样,SubString 不适合长期存储,因为它重用了原 String 的内存空间,原 String 的内存空间必须保留直到它的 SubString 不再被使用为止。

上面的例子,greeting_ 是一个 String,意味着它在内存里有一片空间保存字符集。而由于 beginning 是 greeting_ 的 SubString,它重用了 greeting_ 的内存空间。相反,newString 是一个 String,它是使用 SubString 创建的,拥有一片自己的内存空间。

注意:
String 和 SubString 都遵循 StringProtocol<//apple_ref/swift/intf/s:s14StringProtocolP> 协议,这意味着操作字符串的函数使用 StringProtocol 会更加方便。你可以传入 String 或 SubString 去调用函数。

比较字符串

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")
}

前缀/后缀相等

通过调用字符串的 hasPrefix(? / hasSuffix(? 方法来检查字符串是否拥有特定前缀/后缀。

print(quotation_.hasPrefix("we"), quotation_.hasSuffix("and I."))

hasPrefix(? 和 hasSuffix(? 方法都是在每个字符串中逐字符比较其可扩展的字符群集是否标准相等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值