【问题标题】:What is the longest UTF8 representation of an NFC-form string of a given length?给定长度的 NFC 形式字符串的最长 UTF8 表示形式是什么?
【发布时间】:2018-10-19 20:06:10
【问题描述】:

上下文。

我正在将 C 写入 iCal (RFC 5545) 规范。它指定分隔行的最大长度为 75 个八位字节,不包括分隔符。稳健性原则和 W3C 字符模型都倾向于我将以 UTF8 编码的输入字符串规范化为 NFC 形式(请参阅Unicode Normalization Forms)。

在读取输入行时,我想读取静态分配的缓冲区。但是,即使 NFC 格式小于 75,一条线的 UTF8 表示也可能超过 75 个八位字节。所以这个缓冲区需要大于 75 个八位字节。我的问题是有多少。

问题。

NFC 格式最多为 75 个八位字节的 UTF8 字符串的最大八位字节长度是多少? (奖励积分:其 NFC 形式最多为 N 个八位字节。)

此外,这是保证和永久的,还是当前 Unicode 的未指定结果并可能发生变化?

【问题讨论】:

  • 为什么不直接使用动态分配的缓冲区呢?执行 NFC 转换,如果结果大于您当前的缓冲区大小,则将缓冲区重新分配给更大的大小。
  • @RemyLebeau 是的,我可能会这样做。这个问题仍然是相关的,因为答案会在重新分配之前通知初始缓冲区“猜测”大小。
  • 所需的大小实际上取决于在字符串中编码的特定代码点。单个代码点在 UTF-8 中编码为 1..4 个字节,因此 UTF-8 中的 75 个八位字节行最多可以包含 18..75 个代码点之间的任何位置,具体取决于特定内容。最好的情况是该行仅包含 ASCII 码点 U+0000..U+007F,然后每个码点 1 个八位字节(最多 75 个码点)。更糟糕的情况是,如果该行仅包含代码点 U+10000..U+10FFFF,则每个代码点 4 个八位字节(最多 18 个代码点)。

标签: utf-8 unicode-normalization canonicalization canonical-form


【解决方案1】:

这里有一些 Javascript 代码,它试图找到 Unicode 代码点,其 UTF-8 表示在转换为 NFD 并返回 NFC 时收缩最大。似乎没有代码点缩小超过三倍。据我了解Unicode规范化算法,只有单个代码点必须以这种方式检查。

我认为,至少在理论上,这可能会在 Unicode 的未来版本中发生变化。但是有一个stability policy 涉及在规范化为 NFC 时字符串的扩展(另请参阅Can Unicode NFC normalization increase the length of a string?),所以我认为这不太可能改变:

规范映射(Decomposition_Mapping 属性值)始终受到限制,因此当归一化为 NFC 时,任何字符串都不会扩展到超过 3 倍的长度(以代码单位衡量)。

因此,分配比最大行长度大三倍的初始缓冲区似乎是一个合理的选择。

var maxRatio = 2;
var codePoints = [];

for (var i=0; i<0x110000; i++) {
  // Exclude surrogates
  if (i >= 0xD800 && i <= 0xDFFF) continue;
  var nfd = String.fromCodePoint(i).normalize('NFD');
  var nfc = nfd.normalize('NFC');
  var nfdu8 = unescape(encodeURIComponent(nfd));
  var nfcu8 = unescape(encodeURIComponent(nfc));
  var ratio = nfdu8.length / nfcu8.length;
  if (ratio > maxRatio) {
    maxRatio = ratio;
    codePoints = [ i ];
  }
  else if (ratio == maxRatio) {
    codePoints.push(i);
  }
}

console.log(`Max ratio: ${maxRatio}`);

for (codePoint of codePoints) {
  // Exclude Hangul syllables
  if (codePoint >= 0xAC00 && codePoint <= 0xD7AF) continue;
  var nfd = String.fromCodePoint(codePoint).normalize('NFD');
  var nfc = nfd.normalize('NFC');
  console.log(
    codePoint.toString(16).toUpperCase(),
    encodeURIComponent(nfd),
    encodeURIComponent(nfc)
  );
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-04-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-24
    • 1970-01-01
    相关资源
    最近更新 更多