iOS一点点 - Swift 标准库中的 String

参考资料

Swift Standard Library Reference 主体为对该链接 String 部分理解基础上的翻译,但有较大改动且加入更多个人见解与扩展内容 Unicode and you by BetterExplained BetterExplained对于Unicode的解释,随手找的 ?? ~ emoji unicode characters ~ ??

所有我不能确定标准与否的术语翻译,均会在第一次出现时于括号内标注原文。

可能稍显啰嗦,我是希望能够借助尽量 细致、直观、全面、有理有据 的讲解,来提升自己的理解,相信也能帮到其他人。我很喜欢读这种态度的文章,也希望自己的文章能有这种水平,希望你也喜欢。

正文

Swift 标准库提供了 String 文本类型,适用 Unicode 文本。本文内容就是,如何对它进行定位(index)和切分(slice)。

先看下面的例子

let str = "Héllo, ??laygr?und!"

let badRange = 4...12
// str[badRange]
// 取消上行的注释将会看到一个错误 “Subscript is unavailable: cannot subscript String with a range of Int”

不能用 Int 定义的 Range 范围来取子串,为毛? 字符串的第 n 个位置存第 n 个字 这样的逻辑有什么问题?为了理解 Swift 这样设计的目的,下面要简单扯下字符集。

C语言的字符串是这样的

01000001

01000010

01000011

01000100

A

B

C

D

一个字节存一个字符,第 n 个字节存第 n 个字符,没问题。但是 Unicode 可以表示的字符很多,一个字节表示不完。于是要用更多字节表示一个字符,但 ASCII 中 ABCD 这些字符只要一个字节就够了,在这里也要统一用多个字节就会造成浪费。因此有了变长编码如 UTF-8 ,一些字符用一字节表示,另一些用多个字节。如字符串 "A??" ,utf-8的表示如下

01000001

11110000 …这里省略6个字节,呵呵呵… 10111000

A

??

一个有趣的细节: UTF-8 的 “A” 和 前面 ASCII 的 “A” 编码一致,都是 65 。实际上不止是 “A” ,UTF-8 是兼容 ASCII 的,所有 ASCII 内的字符的在 UTF-8 和 ASCII 中的表示都一样,也即都是占一个字节

另一个有趣的细节。。。: UTF-8 一个字符使用的最多是 4 个字节而不是 8 个,“??” 符号其实是由两个地区标记符(regional indicator symbol letter)“u” 和 “s” 拼起来的,所以才用了 8 字节。 这里的地区标记符 “u” 和 “s” 不是英文字母,是专门用来拼装国家、地区标记的特殊字符。从它们占用了 4 个字节而不是 ASCII 的 1 字节这里也可以看出来区别

表示 “A” 只用一个字节,表示 “??” 却用了足足八个字节。这就破坏了上表中字节和字符一一对应的关系,数据结构中的第 n 位和字符串的第 n 个字符之间的对应关系没了。

前面我们说过 Swift 标准库提供的 String 用的是 Unicode ,现在再回去看前面那句报错 Subscript is unavailable: cannot subscript String with a range of Int 就知道了。不能用 Int 指定的范围来定位、切分字符串的原因就是因为,由于使用了变长编码,导致 String 的数据结构的第 n 个元素,不是我们要的第 n 个字符。(姑且先这么说吧)

String 中要定位、切分字符串,需要使用 String.Index 对象提供的一系列方法,它们会确保操作以字符为单位进行,不会出现让你把一个多字节字符砍成两半的问题:

// successor() 下一个字符
str[str.startIndex]                            // "H"
str[str.startIndex.successor()]                // "é"
str[str.startIndex.successor().successor()]    // "l"

// predecessor() 上一个字符
str[str.endIndex.predecessor()]                // "!"
str[str.endIndex.predecessor().predecessor()]  // "d"

// advancedBy(Int) 与按给定次数执行前两个方法效果相同
str[str.startIndex.advancedBy(7)]              // 与执行七次 successor() 效果相同 "??"
str[str.endIndex.advancedBy(-7)]               // 与执行七次 predecessor() 效果相同 "g"

// 此处创建的 Range 是 Range<Index> 类型,而非 Range<Int>
let range = str.startIndex.advancedBy(4)...str.startIndex.advancedBy(12)
str[range]                                     // "o, ??laygr"

上面是相关的常用用法。

至此,这部分的学习就暂时告一段落了,关于 String 的其它内容,以后遇到再谈。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏C语言C++游戏编程

C语言第一个字符串Hello,C语言基础教程之字符串

C 语言中,字符串实际上是使用 null 字符 '' 终止的一维字符数组。因此,一个以 null 结尾的字符串,包含了组成字符串的字符。

1152
来自专栏blackheart的专栏

[程序设计语言]-[核心概念]-03:控制流

0.概述 前面介绍了语言的演进以及一些基础概念后,从本篇开始进入了语言的核心问题中。这一篇讨论的是语言计算模型(大致可以用控制流来表述),大致如下7种: 顺序执...

20910
来自专栏HTML5学堂

原生JS | 数据类型检测,并没你想象的那么简单

HTML5学堂-码匠:看上去,JavaScript中的数据类型检测,并没有什么难度,但是……它包含了不少的知识,如果你只知道一个typeof的话,那很建议你读读...

3295
来自专栏zhisheng

运算优先级、结合性、求值顺序、副作用和顺序点

标题中这几个概念,是很多C/C++程序员在表达式上容易出问题或不清楚的地方,虽然这些概念在很多语言都有体现,但C里面特别明显,所以就以C语言为例子总结下 运算...

4597
来自专栏MyBlog

Effective.Java 读书笔记(8)关于equals方法

重写equals看上去十分简单对吧,但是我觉得很多时候重写equals可能会招致一些问题,这些问题有时可能会特别严重,当然了不重写不就完事了吗?但是这只适用于那...

814
来自专栏编程

浅谈如何定义和调用Python的函数

函数是python编程核心内容之一,笔者在本文中主要介绍下函数的概念和基础函数相关知识点。函数是什么?有什么作用、定义函数的方法及如何调用函数。 函数是可以实现...

1835
来自专栏大数据钻研

如何正确实现Java中的hashCode方法

你知道一个对象的唯一标志不能仅仅通过写一个漂亮的equals来实现 太棒了,不过现在你也必须实现hashCode方法。 让我们看看为什么和怎么做才是正确的。 相...

2809
来自专栏开发技术

排序之归并排序

  “归并”一词的中文含义就是合并、并入的意思,而在数据结构中的定义是将两个或两个以上的有序表组合成一个新的有序表。既然是归并、并入,那么必然就有子序列了,子序...

1194
来自专栏程序员叨叨叨

6.8 控制流语句(Control Flow Statement)

程序最小的独立单元是语句(statement),语句一般由分号结尾,缺省情况下,语句是顺序执行的,但是当涉及逻辑判断控制时,就要求有控制流程序语句。控制流程序语...

2413
来自专栏非著名程序员

鸡蛋问题来了,是先有Class还是先有Object?

周末比较无聊,在浏览论坛的时候,偶然看到一个程序猿提问的问题,他时这样提问的:突然想到一个很菜的问题, 倒底先有Object还是先有Class?所有类都是Obj...

2036

扫码关注云+社区

领取腾讯云代金券