概述
Swift 语言是一门现代化、安全、强大且还算性感的语言。在去年 WWDC 24 中苹果正式推出了秃头码农们期待许久的 Swift 6.0,它进一步完善了 Swift 语言的语法和语义,并再接再厉——强化了现代化并发模型的安全性和灵活性。
这里我们不妨用 Swift 来验证一个实际世界中有趣的小疑问:汉语中到底有没有拼音首字母全部相同的 4 字成语呢?
到底问题的谜底将会如何?结果绝对会让大家“始料不及”!
那还等什么呢?Let’s go!!!😉
4. 大施拳脚
解决像这种小“鸡”问题无需 Xcode “牛刀”,我们可以将全部测试都放在 Playground 中即可。
首先,我们新建一个 Playground 项目,然后将下载的成语文本文件拖入到其中的 Resources 目录中:
糟糕,怎么全是乱码!
别急,这是因为 Playground 默认都是以 utf8 编码来打开文本文件,可偏偏这个文件的编码是中文 GB18030。我们会在后面“撸码”打开此文件时再给出解决方案。
5. 字符串的编码
前面提及,这个包含超过 3w 条成语的文本文件实际是由中文 GB18030 来编码的。首先,我们必须在 Playground 中以正确的方式将其打开并转换为字符串:
if let fileUrl = Bundle.main.url(forResource: "all", withExtension: "Txt"), let data = try? Data(contentsOf: fileUrl) {
let encoding = String.Encoding(rawValue: CFStringConvertEncodingToNSStringEncoding(CFStringEncoding(CFStringEncodings.GB_18030_2000.rawValue)))
if let text = String(data: data, encoding: encoding) {
// 待实现...
}
}
上面代码的逻辑很清楚,我们尝试打开指定的成语文本文件 “all.Txt”,并将其内容用 GB18030 编码格式转换为字符串(String)。
6. 遍历每条成语
在将海量成语以字符串的形式放到内存之后,我们现在可以来遍历每一条成语的文本了。
经过简单测试发现,每条成语文本是以两对 \r\n 换行符组合来分割的。由此可见,该文件大概率是由 Windows 下的文本编辑器创建的。
这是因为 Windows 和 macOS 的换行符存在显著差异。Windows 系统使用“回车(CR)”和“换行(LF)”两个字符序列作为换行符,即 \r\n。而 macOS 则遵循 Unix/Linux
标准,使用“换行(LF)”作为换行符,即\n。
有的小伙伴可能会好奇,我是怎么知道每条成语文本实际换行符的?其实很简单,我们让它们自己开口说话。
if let text = String(data: data, encoding: encoding) {
print(text.prefix(150).debugDescription)
}
代码运行结果如下所示:
" 樽酒论文 拼音:zūn jiǔ lùn wén释义:一边喝酒,一边议论文章。出处:唐·杜甫《春日忆李白》诗何时一樽酒,重与细论文。”示例:连年客里度初度,~第一遭。★陈世宜《上巳社集是日值余初度》诗\r\n\r\n 遵养时晦 拼音:zūn yǎng shí huì释义:遵遵循,按照;时时势;晦隐藏。原为颂"
看到里面隐藏着的换行符了吗?
据此,我们撸起袖子继续完成遍历每条成语的逻辑代码,为了快速调试之目的我们只显示前 10 条成语:
if let fileUrl = Bundle.main.url(forResource: "all", withExtension: "Txt"), let data = try? Data(contentsOf: fileUrl) {
let encoding = String.Encoding(rawValue: CFStringConvertEncodingToNSStringEncoding(CFStringEncoding(CFStringEncodings.GB_18030_2000.rawValue)))
if let text = String(data: data, encoding: encoding) {
let idiomStrings = text.split(separator: "\r\n\r\n")
print("共有 \(idiomStrings.count) 条成语")
for (i,string) in idiomStrings.enumerated() {
guard i < 10 else { break }
let idiomString = string.trimmingCharacters(in: .whitespacesAndNewlines)
print("[\(i + 1)]:\(idiomString)")
// 待实现...
}
}
}
从上图的运行结果可以看到:
- 该文本文件一共包含 30806 条成语;
- 前 10 条成语的详细信息;
7. 避繁就简:正则表达式
现在我们已经可以取得每一条成语的详细内容了,接下来就是截取成语名称和对应的拼音了。利用 Swift 中的正则表达式这很好解决。
乐业安居拼音:lè yè ān jū释义:愉快地从事自己的职业,过着安定的生活。形容生活、生产、思想状况安定正常。出处:元·无名氏《延安府》第一折见如今四海无虞,八方黎庶皆丰富,乐业安居。”示例:无
首先,通过观察上面输出成语的具体信息,我们可以得出正则表达式的构建方法:
let idiomNameReg = /(.+?)拼音/
let idiomPinyinReg = /拼音:(.+?)释义/
可以看到,我们根据成语信息中固定“锚点文本”(“拼音”、“释义”)合成了对应成语名称和拼音的正则表达式。
由此,我们可以再接再厉补全后续的代码了:
for (i,string) in idiomStrings.enumerated() {
guard i < 10 else { break }
let idiomString = string.trimmingCharacters(in: .whitespacesAndNewlines)
let idiomNameReg = /(.+?)拼音/
let idiomPinyinReg = /拼音:(.+?)释义/
if let idiomName = try? idiomNameReg.firstMatch(in: idiomString)?.output.1, let idiomPinyin = try? idiomPinyinReg.firstMatch(in: idiomString)?.output.1 {
let pinyins = idiomPinyin.split(separator: " ")
print("\(idiomName)(\(idiomPinyin)),拼音个数: \(pinyins.count)")
}
}
运行代码,我们离最终目标又进了一步:
但先别高兴的太早!因为上面的逻辑隐藏着一个不为人知的“陷阱”,要知后事如何且听下回分解!
想要进一步系统地学习 Swift 开发的小伙伴们,可以来我的《Swift 语言开发精讲》专栏逛一逛哦:
总结
在本篇博文中,我们开始了用 Swift 语言实现查找拼音首字母全部相同的 4 字成语的撸码之旅,我们离最后的胜利越来越近了。
感谢观赏,我们下一篇再见!😎