给定长度的NFC形式的字符串的最长UTF8表示是什么?

时间:2018-10-10 03:17:05

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

上下文。

我正在按照iCal(RFC 5545)规范编写C。它指定分隔行的最大长度为75个八位位组(不包括分隔符)。健壮性原理和W3C字符模型都使我能够将以UTF8编码的输入字符串规范化为NFC形式(请参见Unicode Normalization Forms)。

当读取输入行时,我想读取一个静态分配的缓冲区。但是,即使一行的NFC格式小于75,它的UTF8表示也可能超过75个八位位组。因此,此缓冲区将需要大于75个八位位组。我的问题是多少。

问题。

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

此外,这是否可以保证并且是永久的,或者是当前Unicode的不确定结果并可能更改?

1 个答案:

答案 0 :(得分:0)

这里有一些Javascript代码,试图找到在转换为NFD并返回NFC时其UTF-8表示收缩最多的Unicode代码点。似乎没有任何代码点减少超过三分之一。据我了解Unicode规范化算法,只需要以这种方式检查单个代码点。

我认为,至少在理论上,这可能会在将来的Unicode版本中改变。但是在规范化为NFC时,有一个stability policy关于字符串的扩展(另请参见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)
  );
}