0

语境。

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

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

问题。

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

此外,这是保证和永久的,还是当前 Unicode 的未指定结果并且可能会更改?

4

1 回答 1

1

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

我认为,至少在理论上,这可能会在 Unicode 的未来版本中发生变化。但是在规范化为 NFC 时,有一个关于字符串扩展的稳定性策略(另见Can Unicode NFC normalization increase a string of a length?),所以我认为这不太可能改变:

规范映射(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)
  );
}

于 2018-10-19T12:06:10.750 回答