我有一个可以包含任意Unicode字符的字符串,我想得到这个字符串的前缀,它的UTF-8编码长度尽可能接近32字节,同时仍然是有效的UTF-8,并且不改变字符的含义(即不切断扩展的字形簇)。
考虑以下正确示例:
let string = "\u{1F3F4}\u{E0067}\u{E0062}\u{E0073}\u{E0063}\u{E0074}\u{E007F}\u{1F1EA}\u{1F1FA}"
print(string) // 🏴🇪🇺
print(string.count) // 2
print(string.utf8.count) // 36
let prefix = string.utf8Prefix(32) // <-- function I want to implement
print(prefix) // 🏴
print(prefix.count) // 1
print(prefix.utf8.count) // 28
print(string.hasPrefix(prefix)) // true
下面是一个错误实现的示例:
let string = "ar\u{1F3F4}\u{200D}\u{2620}\u{FE0F}\u{1F3F4}\u{200D}\u{2620}\u{FE0F}\u{1F3F4}\u{200D}\u{2620}\u{FE0F}"
print(string) // ar🏴☠️🏴☠️🏴☠️
print(string.count) // 5
print(string.utf8.count) // 41
let prefix = string.wrongUTF8Prefix(32) // <-- wrong implementation
print(prefix) // ar🏴☠️🏴☠️🏴
print(prefix.count) // 5
print(prefix.utf8.count) // 32
print(string.hasPrefix(prefix)) // false
有什么优雅的方法可以做到这一点?(除了试错法)
4条答案
按热度按时间6rvt4ljy1#
你没有尝试解决问题,SO通常也不会为你写代码,所以这里有一些算法建议:
有什么优雅的方法可以做到这一点?(除了试错法)
优雅的定义是什么?(就像美丽一样,它取决于旁观者的眼睛......)
从
String.makeIterator
开始,写一个while
循环,只要字节数≤ 32,就在前缀后面加上Character
s。这是一个非常简单的循环,最坏的情况是32次迭代和32次附加。
您可以根据
String
中每个Character
的 * 平均 * 字节长度并使用String.Prefix(Int)
来实现策略。例如,对于您的第一个示例,字符计数为2,字节计数为36,给出平均18字节/字符,18仅一次变为32(我们不处理小数字符或字节!)所以从
Prefix(1)
开始,其具有字节计数28并且留下1个字符和8个字节-因此剩余部分具有平均字节长度8并且您正在寻找至多4个更多字节,8等于4的零倍你就完了。上面的例子显示了扩展(或不扩展)前缀猜测的情况。如果前缀猜测太长,可以使用前缀字符和字节计数而不是原始字符串的计数从头开始算法。
如果你在实现你的算法时遇到困难,问一个新的问题,展示你写的代码,描述问题,毫无疑问有人会帮助你完成下一步。
高温加热
yv5phkfx2#
我发现
String
和String.UTF8View
共享相同的索引,所以我设法创建了一个非常简单(并且高效?)的解决方案,我认为:说明(假设
maxLength == 32
和startIndex == 0
):第一种情况(
utf8.count <= maxLength
)应该很清楚,这是不需要工作的地方。对于第二种情况,我们首先得到utf8索引
33
,它是因此,如果我们现在将我们的索引向后移动一个字符(具有
formIndex(before:)
),则这将跳转到index
之前的第一扩展文法素簇边界,在A和B的情况下,index
在该字符的开始之前一个字符,在C中,index
在该字符的开始之前一个字符。在任何情况下,utf8-index现在将被保证至多为
32
并且在扩展的字素簇边界处,因此prefix(upTo: index)
将安全地创建长度≤32的前缀。但并不完美
理论上,这也应该是最优的解决方案,即前缀的
count
尽可能地接近maxLength
,但有时当字符串以一个由多个Unicode标量组成的扩展字素簇结尾时,formIndex(before: &index)
会比所需的多后退一个字符,所以前缀结束时会更短,我不确定为什么会这样。编辑:一个不那么优雅但作为交换完全“正确”的解是这样的(仍然只有O(n)):
jfewjypa3#
我喜欢你提出的第一个解决方案,我发现如果去掉
formIndex
,它会更正确(也更简单):0sgqnhkj4#
我的解决方案如下所示:
它获取可能的最大utf8索引,使用
Index.samePosition(in:)
方法检查它是否是一个有效的字符索引,如果不是,它逐个减少utf8索引,直到找到一个有效的字符索引。优点是你可以用utf16代替utf8,它也可以工作。