Какое самое длинное UTF8-представление строки NFC-формы заданной длины? - PullRequest
0 голосов
/ 10 октября 2018

Контекст.

Я пишу C в спецификации iCal (RFC 5545).Он определяет максимальную длину строки с разделителями, равную 75 октетам, исключая разделитель.И принцип надежности, и модель символов W3C склоняют меня к канонизации входных строк, закодированных в форме UTF8, в форму NFC (см. Формы нормализации Unicode ).

При чтении строк ввода я хотел бычитать в статически выделенный буфер.Но представление строки в UTF8 может быть больше 75 октетов, даже если ее форма NFC меньше 75. Таким образом, этот буфер должен быть больше 75 октетов.Мой вопрос: сколько.

Вопрос.

Какова максимальная длина в октетах строки UTF8, чья форма NFC составляет не более 75 октетов?(Бонусные баллы: чья форма NFC составляет не более N октетов.)

Кроме того, является ли это гарантированным и постоянным или это неуказанное последствие текущего Unicode и может быть изменено?

1 Ответ

0 голосов
/ 19 октября 2018

Вот некоторый код Javascript, который пытается найти кодовую точку Unicode, чье представление UTF-8 сжимается больше всего при преобразовании в NFD и обратно в NFC.Кажется, что ни одна кодовая точка не уменьшается более чем в три раза.Насколько я понимаю алгоритм нормализации Unicode, таким способом нужно проверять только отдельные кодовые точки.

Я думаю, что, по крайней мере теоретически, это может измениться в будущих версиях Unicode.Но существует политика стабильности относительно расширения строк при нормализации в NFC (см. Также Может ли нормализация Unicode NFC увеличить длину строки? ), поэтому я думаю,маловероятно, что это когда-либо изменится:

Канонические отображения (значения свойств 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)
  );
}
...