Swift中的字符串长度:不仅仅是count,而是一场冒险

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

引言:为什么“字符串长度”不仅仅是一个数字?

\n\n

想象一下,你正在编写一个iOS应用。用户在文本框中输入自己的名字。很简单,对吧?但如果名字包含表情符号、变音符号(例如“café”)或汉字呢?突然间,“获取字符串长度”这个简单的任务就变成了一场带有Unicode元素的侦探游戏。

\n\n

在Swift中,处理字符串不仅仅是计算字符数量。这是一段进入字素簇、Unicode标量以及Objective-C遗产(没错,NSString依然与我们同在)世界的旅程。如果你刚开始学习Swift,或者从其他语言迁移过来,这篇文章就是为你准备的。我们将探讨如何正确确定字符串长度,你会遇到哪些陷阱,以及何时使用count,何时使用NSString。

\n\n

本文基于Stack Overflow上一个真实且热门的问题。成千上万的开发者都曾在这个问题上栽过跟头。让我们不要重蹈他们的覆辙。开始吧!

\n\n
\n\n

1. 基础:String的count属性

\n\n

在Swift中获取字符串长度最直接的方法是使用count属性。它返回字符串中“字符”(Character)的数量。但Swift中的“字符”是什么呢?

\n\n
let greeting = "Привет, мир!"\nprint(greeting.count) // 12 (包括空格和感叹号)
\n\n

一切如预期那样工作。但让我们增加一点难度:

\n\n
let emojiString = "👨‍👩‍👧‍👦"\nprint(emojiString.count) // 1 (!!!)
\n\n

一个?认真的吗?而视觉上这是一个由四个人组成的完整家庭。玄机在哪里?

\n\n

答案在于Swift如何处理字符串。count计算的不是字节,也不是单个Unicode字符,而是扩展字素簇。这是一个或多个Unicode标量的序列,用户将其视为一个字符。表情符号“家庭”由多个部分组成,但Swift将其视为一个字符,因为人类就是这样看待它的。

\n\n

这种行为不是bug,而是特性。Swift的设计考虑到了国际化。如果count计算每个Unicode标量,字符串“café”(带有一个字符é)可能会因为é的编码方式不同而得到不同的结果:是作为一个字符U+00E9,还是作为e加重音的组合(U+0065 + U+0301)。

\n\n
let cafe1 = "café" // é 作为一个字符\nlet cafe2 = "cafe\\u{0301}" // e + 重音\nprint(cafe1.count) // 4\nprint(cafe2.count) // 4 (尽管字符串中有5个标量)
\n\n

Swift将两种变体都计为4个字符,因为用户看到的是由4个字母组成的“café”。这就是字素簇的魔力。

\n\n
\n\n

2. 替代方法:NSString和length属性

\n\n

有时你需要获取的不是“人类可读”的字符数量,而是UTF-16编码单元的数量。例如,在使用旧版API、Core Text,或者在期望UTF-16长度的格式(如某些网络协议)中传输数据时。

\n\n

这时,NSString——来自Objective-C的经典类——就派上用场了。Swift字符串会自动桥接到NSString,因此你可以使用它的length属性:

\n\n
let swiftString = "👨‍👩‍👧‍👦"\nlet nsString = swiftString as NSString\nprint(nsString.length) // 11 (UTF-16编码单元的数量)
\n\n

为什么是11?因为组合表情符号“家庭”在UTF-16中占用了11个单元(每个组件由一个代理对或单个字符编码)。如果你处理的文本将由期望NSString的旧C库或API处理,请使用length。但请记住:这不是用户看到的字符数量。

\n\n

另一个细微差别:NSString不考虑字素簇。对于字符串“café”(第二种变体),length将返回5,而count返回4。结论:根据你的需求选择方法。

博客

书籍推荐