我如何将一个字符串拆分成一个子字符串数组,使得当编码为Uft8且尾部为null时,没有子字符串的字节长度〉n?
n总是〉= 5,因此4字节编码字符+ null将适合。
我现在唯一能想到的办法就是:
- 拆分、编码、测试长度,如果太大,则用较小的拆分重复。
- 编码、遍历、掩码和计数字节,并跟踪边界,手动拆分
有更好的办法吗?
输出数组也可以是一个utf8字节数组,而不是字符串,如果这更容易。
我已经知道代码点,编码,代理等等。这具体涉及utf8编码的字节长度。
我如何将一个字符串拆分成一个子字符串数组,使得当编码为Uft8且尾部为null时,没有子字符串的字节长度〉n?
n总是〉= 5,因此4字节编码字符+ null将适合。
我现在唯一能想到的办法就是:
有更好的办法吗?
输出数组也可以是一个utf8字节数组,而不是字符串,如果这更容易。
我已经知道代码点,编码,代理等等。这具体涉及utf8编码的字节长度。
2条答案
按热度按时间sshcrbum1#
好的,首先,我所知道的关于UTF-8的二进制编码过程的一切都是刚刚从UTF-8 Wikipedia page中学到的,所以注意:-)。也就是说,与@mklement0的答案相比,下面的代码似乎给予了一堆测试数据的正确结果(我毫不怀疑这是正确的),所以也许其中有一些里程碑。..
很高兴听到里面是否有任何咆哮的声音-我认为它 * 应该 * 在原则上工作,即使下面的实现是错误的地方:-)。
在任何情况下,核心函数是下面的函数,它返回utf-8编码字节数组中块的 * 位置 *。我想,一旦你知道了位置,你可能想使用字节的地方(e。例如,使用
Stream.Write(byte[] buffer, int offset, int count)
或类似的方法),所以我避免了将块的副本提取到一个新列表中,但是如果需要的话,使用输出来做这件事非常容易(请参阅下面的内容)。此函数利用了 * 有效 * utf8字节流为self-synchronizing这一事实,这意味着实际上我们 * 不必 * 遍历流中的每个字节-我们可以跳到任何我们喜欢的地方,并通过找到从
00xxxxxx
、01xxxxxx
或11xxxxxx
开始的最近字节来找到编码码点的开始(或者等价地,* doesn 't * start10xxxxxx
),这是 next 块的开始。例如,如果我们从当前块的开头跳转
n
字节,并找到一个以10xxxxxx
开头的字节:然后我们回溯到
n-2
作为 * 下一个 * 块的开始(所以当前块的结束逻辑上比n-3
的结束早一个字节):示例:
块
如果你真的想要一个带有null终止符的分块字节数组,你可以像这样把位置转换成块:
字符串
一旦你得到了单独的以null结尾的utf-8编码块,如果你真的需要它们,你可以像这样将它们恢复为以null结尾的字符串:
性能
就性能而言,这似乎可以很好地扩展,即使您必须专门将字符串编码为utf8字节数组才能调用它。对于
$MaxLen
来说,它的执行速度也要快得多,因为它可以为每个块跳跃更长的距离。..在我的机器上做一个粗略的测试:
虽然如果性能是你主要关心的问题,那么PowerShell可能不是你的正确选择:-)。..
slsn1g292#
注意事项:
以下:
[char]
示例,其中。NET[string]
示例由无符号的16位Unicode * 代码单元 * 组成,因此只能 * 直接 * 编码代码点高达U+FFFF
的Unicode字形(所谓的BMP(基本多语言平面)中的字形),并需要一个(代理)* 对 * 代码单元来编码平面外的Unicode字符,即。也就是那些代码点 * 大于 *U+FFFF
的字符,特别是包括 emoji 的字符范围,例如👍
。U+FFFF
,这反过来意味着4
字节)推断,基于此Wikipedia表。向mclayton致敬,指出跟踪原始字符串中的 indices 与
.Substring()
调用相结合足以提取块-不需要string builder。System.Collections.Generic.List
1`示例收集列表中的所有块。输出(注解):
注意,除了第一个块之外的所有块都具有 * 少于 * 4个字符(字节计数限制,不包括NUL),这是由于包含多字节为UTF-8字符和/或由于 * 下一个 * 字符是这样的字符,因此不适合当前块。