美文网首页
3、【Swift】字符串

3、【Swift】字符串

作者: Sunday_David | 来源:发表于2020-12-09 08:49 被阅读0次

字符串和字符

  • 字符串 String是字符Character的集合
  • 通过 + 符号就可拼接两个字符串
  • 能否更改字符串的值,取决于其被定义为常量还是变量

Swift 的 String 类型与 Foundation NSString 类进行了无缝桥接。调用 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."
"""
  • 可以在代码中,使用缩进,让代码工整对齐,但字符串里不会有影响
image
  • 如果你在某行的空格超过了结束的双引号( """ ),那么这些空格被包含。

字符串字面量的特殊字符

  • 转义特殊字符 \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 的 StringCharacter 类型是完全兼容 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 AFRONT-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 属性返回的字符数量并不总是与包含相同字符的 NSStringlength 属性相同
  • NSStringlength 属性是利用 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 中,也用在 ArrayDictionarySet

子字符串

  • 使用下标或者 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 创建的,拥有一片自己的内存空间。
img

StringSubstring 都遵循 StringProtocol 协议,这意味着操作字符串的函数使用 StringProtocol 会更加方便。你可以传入 StringSubstring 去调用函数

比较字符串

三种方式比较文本值:字符串字符相等、前缀相等和后缀相等。

字符串/字符相等

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 属性进行访问)
  • D,o,g,(DOUBLE EXCLAMATION MARK, Unicode 标量 U+203C)和 🐶(DOG FACE,Unicode 标量为 U+1F436)组成的字符串中的每一个字符代表着一种不同的表示

let dogString = "Dog‼🐶"

UTF-8 表示

  • Stringutf8 属性可访问它的 UTF-8 表示
  • String.UTF8View 类型的属性,UTF8View 是无符号 8 位(UInt8)值的集合,每一个 UInt8 值都是一个字符的 UTF-8 表示

UTF-16 表示

  • Stringutf16 属性来访问它的 UTF-16 表示

Unicode 标量表示

  • 你可以通过遍历 String 值的 unicodeScalars 属性来访问它的 Unicode 标量表示

相关文章

网友评论

      本文标题:3、【Swift】字符串

      本文链接:https://www.haomeiwen.com/subject/bpbcgktx.html