如何确定 Swift String 的显示字符数?

3
我已�查阅了一些问题,比如��字符串的长度和为什么表情符�如👩�👩�👧�👦在Swift字符串中被如此奇怪地处�?但这两个问题都没有涵盖这个特定问题。
这一切始��试�表情符�应用肤色修改器(��以编程方��表情符�添加肤色修改器)。这引�了对将肤色修改器应用�常规字符(如"A")会�生什么的疑问。
例如:
let tonedThumbsUp = "" + "" // 
let tonedA = "A" + "" // A

我正在尝试检测第二种情况。这两个字符串的 count 均为1。这两个字符串的 unicodeScalars.count 也均为2。

如何确定结果字符串在显示时是否呈现为单个字符?换句话说,如何确定是否应用了肤色修饰符以创建单个字符?

我已尝试了几种方法来转储字符串的信息,但没有得到期望的结果。

func dumpString(_ str: String) {
    print("Raw:", str, str.count)
    print("Scalars:", str.unicodeScalars, str.unicodeScalars.count)
    print("UTF16:", str.utf16, str.utf16.count)
    print("UTF8:", str.utf8, str.utf16.count)
    print("Range:", str.startIndex, str.endIndex)
    print("First/Last:", str.first == str.last, str.first, str.last)
}

dumpString("A")
dumpString("\u{1f469}\u{1f3fe}")

结果:

原始数据: A 1
标量值: A 2
UTF16编码: A 3
UTF8编码: A 3
第一/最后一个: true 可选("A") 可选("A")
原始数据:  1
标量值:  2
UTF16编码:  4
UTF8编码:  4
第一/最后一个: true 可选("") 可选("")
2个回答

3

如果您在不支持Fitzpatrick修饰符的系统上打印 ,将会显示该字符后跟该系统用于未知字符占位符的内容。

因此,我认为要回答这个问题,您必须查阅系统的排版程序。对于苹果平台,您可以使用Core Text创建CTLine,然后计算行的字形运行数。例如:

import Foundation
import CoreText

func test(_ string: String) {
    let richText = NSAttributedString(string: string)
    let line = CTLineCreateWithAttributedString(richText as CFAttributedString)
    let runs = CTLineGetGlyphRuns(line) as! [CTRun]
    print(string, runs.count)
}

test("" + "")
test("A" + "")
test("B\u{0300}\u{0301}\u{0302}" + "")

在 macOS 10.14.6 Beta (18G48f) 上,使用 Xcode 10.2.1 的 macOS playground 输出如下:
 1
A 2
B̀́̂ 2

有趣的是,当我在我的 Mac 上查看这个答案时,输出中的重音符号被堆叠在 B 的上方。但现在在我的 iPad 上查看它(运行 iPadOS 13.0 beta 2),它们在 B 和 Fitzpacktrick 修饰符之间水平分布。 - rob mayoff

1

我认为可以通过查看修饰符是否存在以及是否增加了字符数来推理这一点。

例如:

let tonedThumbsUp = "" + ""
let tonedA = "A" + ""
tonedThumbsUp.count // 1
tonedThumbsUp.unicodeScalars.count // 2
tonedA.count //2
tonedThumbsUp.unicodeScalars.count //2
let c = "\u{1F3FB}"
tonedThumbsUp.contains(c) // true
tonedA.contains(c) // true

好的,所以它们都包含一个修饰字符,它们都包含两个unicode标量,但一个计数为1,另一个计数为2。这肯定是一个有用的区别。


在我的 Xcode 11 beta 2 playground 中,tonedA.count 返回 1。这也是我在问题中展示的内容(尽管作为 "A" 而不是 "A" + "",但两者都给出了计数为 1 的结果),这实际上是我问题的整个基础。 - rmaddy
是的,我也刚注意到了。从某种意义上来说,这是最奇怪的部分。转换为基础 utf8 可能会导致这种情况。我不知道这是否是一个 bug。 - matt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接