理解了Unicode你才能理解的String与Character

先说一件事,因为写代码几乎从来没用过Character,所以说难听一点一直就以为就只有String这么一个字符相关的类,现在才发现,我去,还有个Character.虽然以前看到过好几次,但莫名其妙本能的就忽略了,不过还是觉得character单独使用貌似也没什么卵用.哈哈哈,就是调侃一下自己而已。

看完这章文档,发现要想弄明白字符串和字符,就一定要先搞懂Unicode,看的时候没搞懂,写着写着,才发现这里面大有文章,原来自己完全不知道,来吧,现在开始.

Unicode — swift的字符编码方式(Unicode Scalars)

这玩意儿需要跟UTF-8,UTF-16,UTF-32放在一起说估计才能明白,

UTF这玩意儿看到很多次就是没弄明白,不对,就是没有想过去弄明白,反正现在是一个字一个字的啃文档,不慌不忙,就查了一下,

UTF是 Unicode Transformation Formate的缩写,直译过来就是Unicode格式转换

而它为什么会取这个毛名字呢,接下来就要扒历史了,

  • 起点就是比较流行的ASCII字符集,这玩意儿是美国搞出来的,用128个字符搞定了英语国家所需要的所有字符
  • 流行起来之后又因为欧洲很多国家的文字与英语又稍有不同在字母上有音调标号等等,所以就对ASCII字符集进行扩展整出个扩展字符集(extended grapheme clusters)
  • 等ASCII流行的更广了,亚洲和非洲同胞们就提出来,是不是就该解决一下咱们国家的语言编码了呢? 但你想想像汉语这样的象形文字的数量远超英语26个字母啊,小小的ASCII就算是扩展了也搞不定啊,于是,Unicode就这么诞生了,从而把各类象形文字都囊括其中.
  • 但是其实从这个发展史来看,其实每种语言对空间的需求都不一样,你像ASCII字符集在Unicode上使用起来并不高效,因为ASCII并不需要Unicode上多出来的空间,所以就有了UTF(Unicode格式转换),来生成一些小一点的Unicode,比如UTF-8,UTF-16,UTF-32

好了,历史讲完了,又说回来了,swift里的String都是按21-bit Unicode Scalar来编码的,而这个Unicode Scalar相当于就是UTF-32的编码方式,这里我也不是很明白,明明是21比特,为什么说要等价与32比特,这个问题先放在这吧~

但是,从文档里能看出来,这个是跟for-in循环有关系的,下面就顺便提到一个我觉得没有卵用的String方法吧:

for character in "Dog!?".characters {
  print(character)
}
// D
// o
// g
// !
//?

你看,用for-in循环可以直接把String(字符串)里的每一个character(字符)都取出来

(虽然我也不不知道其它编程语言是否有这功能,但这文档里写的语气就好像在说,老子这方法碉堡了,老子为了做到这种效果整出个for-in循环碉堡了,好了,我也就是虾球说的,调剂一下心情,就我这英语水平看文档,哪看的出来神马语气...唉...)

说到.character这个方法,可以拿出每个字符,那么.count这个查看有多少个字符的方法也就顺利成章了嘛.

Unicode在字符串里的应用与体现

再说回Unicode在字符串里的应用与体现(Unicode Representation Of Strings),文档中提到一个例子,

let dogString = "Dog!!?"

当你通过dogString.utf8 dogString.utf16 dogString.unicodeScalars这些取对应UTF的方法,来取出上面这段字符串的每个字符的codeUnit value,你就会发现5个字符,不论是用utf8,还是utf16都会出现某一个字符需要用到2-4个codeUnit才能搞定的情况,而使用Unicode Scalars 每个字符就是一个codeUnit Value就搞定了.

(具体情况可以去看文档,我就偷个懒哈)

这里解释一下codeUnit,字面理解应该就是编码单元,我自己理解的就是一个次最小编码单元,因为bit是属于最小编码单元了,而这种8bit组成或者16bit组成或者21bit或者32bit组成的codeUnit就可以理解为次最小编码单元,从上例也可以看出来,utf-8需要4个codeUnit才能表示"?",utf-16需要2个codeUnit,而UnicodeScalars只需要1个codeUnit

这也就应该是for-in循环为什么可以快速取出字符串中的字符的原因吧

再提一下这个21-bit Unicode scalars,目前其中并不是所有的值都拿出来表示一些确定的字符了,而是留了一些没有分配任务,可能是留给将来开发继续使用的.

通过UnicodeScalars来理解记忆String的各种函数方法

好了,说完这些,终于可以说String的提取索引Index的方法了.String并不能像数组那样array[index]的方式来快速提取String的每一个character,原因其实已经写在上面的UnicodeScalars的例子里了,因为字符串(String)里面可以存各种东西,什么数字,字母,标点符号,表情,等神马神马的,从之前的例子你也会知道,

不同的类型所需要的codeUnit空间是不同的(对应的swift采用的编码也会不同,这句话是我猜的,欢迎指正),

所以在内存中存储的时候长度就不一样,一串字符串在一条线上显示出来就是一会儿长一会儿短,如果你想通过index来快速定位字符的位置就没有办法做到.

这里又要插一句,为什么就没办法做到呢?,这就让我联想到,数组的定义,一个数组里只允许存储同一类型的数据,这又是为什么呢?这两个为什么单独拿出来我都不会想出为什么,现在放在一起我突然感觉到了什么.

为什么数组能快速的通过下标index拿到对于数据呢?因为数组的元素类型唯一确定,在初始化数组的时候,在内存上就会直接生成等长的空间,因为接下来放进来的元素类型是确定的,所以放相应长度的等长空间是肯定不会有有问题的.而正是因为空间是等长的,在用index查找的时候,电脑只需要做等长的跳跃操作就可以拿到对于的数组元素,而不需要去检查里面的内容到底是什么.

而再说回字符串(String)就不一样了,String中会有各种不同类型的元素,所以对于的内容空间就不可能是等长的,而是一会儿长一会儿短,所以电脑没法直接通过跳跃的方式直接找到某段内容,它需要做的是通过swift的UnicodeScalar编码去从头遍历并识别字符串(String)里的每一个字符(character).

了解了这个原理,再回去看,字符串(String)的查找元素的方法你就会理解了,字符串在调用索引(Index)的时候,就是从头到尾一个一个获取一个一个遍历,是不是很蠢,哈哈哈,好了,下面我们来看看有些啥方法:

  • .StartIndex 获取字符串的第一个字符的位置(position)
  • .endIndex 获取字符串的最后一个字符的后面的位置,注意是后面的位置,也就是获取到了字符串屁股外面的位置
  • predecessor() 获取当前index的前一个index的位置
  • successor() 获取当前index的后一个index的位置
  • advancedBy(_:)获取当前index之后的第n个index的位置
  • .characters.indices 可以结合for-in顺次拿到其中的每一个index,不过这个貌似是在for-in里用的,不知道拿出来是否有用,当然也不知道该怎么用?留着以后探索吧

关于提取索引(Index)方法,再说一点,我跑去看了一下swift3.0的beta文档,这几个方法名字会变,功用还是那些,只是名字变了一下,不过确实变的比较容易读,比上面这神马函数名要好多了,有兴趣的可以去看一下.

除了查找方法,还有插入和删除的方法,搞的跟数组一样,但你想想前面取index有多难,再还要通过index再取插入和删除,我去....我觉得都不如直接删掉String来重新建一个新的来的快,不说了,说多了都是错,就是发个牢骚,来看看这些方法吧:

  • .insert(_:atIndex:) 插一个字符
  • .insertContentsOf(_:at:) 插一个字符串
  • .removeAtIndex(_:) 删除一个字符
  • .removeRange(_:) 删除一片字符

再说说字符串的比较,可以== != > <这些都可以,其中有一点要说一下,两个字符串编码不同是可能相等的,只要它们是属于同一个扩展字符集就可以,这里其实我不是特别明白,大概的意思可以结合上面讲的历史联想一下,估计也就是欧洲那些拉丁语国家之间的编码稍有不同,但因为都是一个妈生的,所以会出现相等的情况,不过对我们大天朝好像没什么影响,直接忽略吧,知道是值类型,可以比较就好了.

下面还有两个特殊的方法,hasPrefix hasSuffix 这两个方法是分别用来判断字符串是否有某某前缀,某某后缀用的,也没什么好说的.

总结

对字符串(String)中的任何指定字符(character)的操作都需要从头到尾遍历,并通过UnicodeScalars来匹配解码才能进行之后的操作.



作者:我啃晴天
链接:https://www.jianshu.com/p/27b237d7843f
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值