#3 字符串和字符(Strings and Characters)

swift相对于JS,

  • 除了 String 类型外,还多一种 Character(C语言中称之为 Char)类型
  • JS中字符串既可以用单引号也可以用双引号来表示字符串,而swift中 String 和 Character 均使用双引号 表示
  • swift中的 String 类型是一种快速,现代化的字符串实现,每一个字符串都是由编码无关的 Unicode 字符组成,并支持访问字符的多种Unicode表达形式
  • Swift 中 String 是 值类型
  • swift 中 String 访问字符的方法对学JS的我看来很操蛋

1.多行字符

表示方式是:

let quotation = """
这是第一行
这是第二行
""";

个人感觉这个就是JS中简陋版的字符串模板,一些示例

#1 一行写不下, 可以用 '\' 进行换行
let quotation = """
这是第一行,\
这还是第一行的内容,太长了就换一行
这是第二行
""";

#2 最底下的 """ 的决定空格数 或者说缩进
let quotation = """
    这是第一行,
        这是第二行
    这是第3行
    """; // 注意这个前面有4个空格,因为放置位置在此.
// 等价于
let quotation = """
这是第一行,
    这是第二行
这是第3行
""";

#3 对 """ 进行转义
# 如果字符串中正好存在 """ 转义方式有2种: \""" 或者 \"\"\"
let threeDoubleQuotationMarks = """
Escaping the first quotation mark \"""
Escaping all three quotation marks \"\"\"
"""

#4 多个多行字符串结合
let badStart = """
one
two
"""
let end = """
three
"""
print(badStart + end)
// 打印两行:
// one
// twothree

// 如果想要换行 则需要注意这样写
let goodStart = """
one
two

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

2.需要转义的字符

字符串字面量需转义的字符:

  • \0:空字符
  • \\: 反斜杠
  • \t | \n | \r: 水平制表符 | 换行符 | 回车符
  • \": 双引号
  • \': 单引号
  • Unicode 标量: \u{n}, n 是任意1-8位16进制数,且可用的Unicode位码

示例:

let wiseWord = "\"想象力比知识更重要\" - 爱因斯坦"
let dollarSign = "\u{24}" // $ Unicode标量为 U+0024
let sparklingHeart = "\u{1F496}" // 💖,Unicode 标量 U+1F496

3.字符串插值(string interpolation)

使用 \(variables || expression) 的形式进行插值操作, 这个等同于JS中的 ${}

let count = 5
print("I have \(count) apples") // I have 5 apples

4.字符串的可变性

这里只提一点,就是swift中用 let 声明的表示常量,这和JS中的 const 一样,但是JS中也有 let 关键词,注意别混淆了,因此在swift中下面做法是错误的:

let greet = "hello"
greet += " world" // error 常量字符串不可以被修改

5.Character 类型

注意有些语言中,这个类型称之为 Char,声明时用单引号,但是swift中Character类型用 双引号 进行声明

let aChar: Character = "A"
let charArray: [Character] = ["a", "b", "c"]

另外一般认为String类型是character类型的集合, swift中可以使用 for-in 对字符串进行遍历操作

let str = "Dog!🐶"
for character in "Dog!🐶" {
    print(character)
}
// D
// o
// g
// !
// 🐶

// 另外可以通过 type(of:) 方法来判断是否是Character类型
for char in "Dog!🐶" {
    print(type(of: char))
}
// 打印
// Character (重复5遍)

String 和 Character 类型之间的一些转换和方法

#1 将Character数组转换为字符串
# 使用 String 构造器即可
let cat: [Character] = ["C", "A", "T"]
let catStr = String(cat) // "CAT"

#2 将Character字符添加到String中
# 使用 append 方法
var greet = "hello"
let exclamationMark = "!"
greet.append(exclamationMark) // 现在greet变为了 "hello!"

6.Unicode

个人感觉这部分比较麻烦。

  • swift中的 String 类型是基于 Unicode 标量 建立的。
  • swift中的 Character 类型代表一个可扩展的字形群,一个可扩展的字形群是一个或多个可生成人类刻度的字符Unicode标量的有序排列(好拗口)

通俗讲一个字符的写法可能由Unicode组成的写法不一样,但是最后结果是一样的:

let eAcute: Character = "\u{E9}"                         // é
// 这个相当于对上面 é = e +  ́ 的分解
let combinedEAcute: Character = "\u{65}\u{301}"          // e 后面加上  ́
print(eAcute == combinedEAcute) // true

7.字符串的操作

swift中目前为止感觉语法最难看的部分来了,个人感觉这种设计太复杂了。

isEmpty 属性 判断字符串是否为空

let emptyString = ""
// 或者 let emptyString = String()
if emptyString.isEmpty {
  print("这是一个空字符")
}
// "这是一个空字符"

字符串索引

因为不同字符可能会占用不同数量的内存空间,所以要知道 Character 的确定位置,就必须从 String 开头遍历每一个 Unicode 标量 直到结尾,因此, swift的字符串不能用整数做索引!!!

获取字符位置的方法大概有以下几种:

  • startIndex: 获取String的第一个Character的索引, 注意其返回的不是一个Int类型,而是 String.Index 类型
  • endIndex: 获取字符串中最后一个Character的位置索引,注意 endIndex 属性不能作为一个字符串的有效下标!!!
  • index(before: String.Index): 获取某个字符前面的一个位置的索引
  • index(after: String.Index): 获取某个字符后面的一个位置的索引
  • index(_ i: String.Index, offsetBy: Int): 用来获取对应偏移量的索引,例如 someStr.index(someStr.startIndex, offsetBy: 6) 获取开始位置偏移6位的字符
  • indices: 这个属性会创建一个包含全部索引的范围(Range),用来在一个字符串中访问单个字符
  • count: 获取字符的数量

另外需要注意的是:

  • 如果字符串为空,则 startIndex 等于 endIndex
#1 获取字符串的长度
let koala = "Koala 🐨"
print(koala.count) // 7 , 可以看出 这里 🐨 占一个字符

#2 开始索引
let greet = "hello world!"
print(greet[greet.startIndex]) // "h"

#3 endIndex 属性不能作为一个字符串的有效下标!!!
let greet = "hello world!"
print(greet[greet.endIndex]) // error  Can't form a Character from an empty String

#4 获取最后一个字符
# 使用 index(before i: String.Index) 方法
let greet = "hello world!"
print(greet[greet.index(before: greet.endIndex)]) // "!"

#5 使用 index(after i: String.Index) 方法 获取第一个字符之后的字符
let greet = "hello world!"
print(greet[greet.index(after: greet.startIndex)]) // "e"

#6 使用 index(_ i: String.Index, offsetBy n: int) -> String.Index
let greet = "hello world!"
print(greet[greet.index(greet.startIndex, offsetBy: 4)]) // "o"

#7 使用 indices 创建 Range
let greet = "hello world!"
for index in greet.indices {
    print("\(greet[index])", terminator: "|")
}
// h|e|l|l|o| |w|o|r|l|d|!|Index(_compoundOffset: 0, _cache: Swift.String.Index._Cache.utf16)

字符串增删改查

主要用到这几个方法:

  • insert(newElement: Character, at: String.Index): 可以在一个字符串的指定索引插入一个字符

  • insert(contentsOf: Collection, at: String.Index): 可以在一个字符串的指定索引插入一个字符段

  • remove(at:): 可以在一个字符串指定索引删除一个

  • removeSubrange(_ bounds: Range<String.Index>): 在一个字符串的指定索引删除一个子字符串

    这些方法都遵循 RangeReplaceableCollection 协议,还可以在集合中(比如 Array, Dictionary, Set )使用这些方法

示例:

var greet = "hello"

// 在尾部添加单个字符 '!'
greet.insert("!", at: greet.endIndex) // "hello!"

// 在 '!' 前添加 "world" 字符段
greet.insert(contentOf: "world", at: greet.index(greet.endIndex, offsetBy: -1)) // "hello world!"

// 在尾部移除单个字符 '!'
// 返回被移除的字符 "!"
// 同时greet 变为 "hello world"
greet.remove(at: greet.index(before: greet.endIndex))

// 移除 " world"
// 注意 removeSubrange 接受的参数类型是 Range<String> 类型
// 需要使用到前面学习的区间的表示法
let range = greet.index(greet.index(greet.endIndex, offsetBy: -6))..<greet.endIndex
greet.removeSubrange(range) // "hello"

SubString 类型

当从字符串中获取一个子字符串,得到的是一个 SubString 实例,而不是 String 类型,Swift中如果需要短时间内操作字符串,才会使用SubString,如果要长时间保存结果,就把 SubString 转化为 String 的实例:

  • 两者都在内存里保存字符集
  • 两者不同之处在于性能优化,SubString 可以复用原 String的内存空间,或者另一个SubString 的内存空间
  • String也有同样的优化,但是如果2个String共享内存的话,它们就会相等

下面示例使用方法:

  • index(of: Character) -> String.Index: 用来查找某个字符的索引
var greet = "hello, world!"
let index = greet.index(of: ",") ?? greet.endIndex
let beginning = greet[..<index] // beginning 的值为 "hello"
print(type(of: beginning)) // 打印其类型为 "SubString"

// 将其转化为 String 类型, 以便长期存储
let newString = String(beginning)

本章中其它的字符串方法

  • hasPrefix(_ prefix: String): 字符串是否有某个前缀
  • hasSuffix(_ suffix: String): 字符串是否有某个后缀
let greet = "hello, world";
print(greet.hasPrefix("hell")) // true
print(greet.hasSuffix("world")) // true

另外swift中好像没有类似JS中的 substring 方法对字符进行截取,原生的小伙伴告诉我可以自定义:

extension String {
  func substring(start: Int, end: Int = 0) -> String? {
    if start > end && end != 0 {
      return nil
    }
    
    let startIndex = self.index(self.startIndex, offsetBy: start)
    let endIndex = end == 0 ? self.endIndex : self.index(self.startIndex, offsetBy: end)
    
    return String(self[startIndex..<endIndex])
  }
}

// 使用
let greet = "hello, world!"
greet.sub(start: 3, end: 7) // "lo,"
greet.sub(start: 3) // "lo, world!"

总结

这一章主要讲了swift中的2种(加上SubString应该是3种)类型,以及字符串索引的操作,最复杂的Unicode的相关表示法此处忽略了,待后面再学习。另外介绍了一些操作字符串的方法,还有大把的方法暂时还未接触,待学习。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,402评论 6 499
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,377评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,483评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,165评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,176评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,146评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,032评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,896评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,311评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,536评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,696评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,413评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,008评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,815评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,698评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,592评论 2 353

推荐阅读更多精彩内容