Недавно я отлаживал очень странный случай с одним из моих проектов Node.js. Он распечатывает мой вывод в формате (с модулем as-table) в красиво выглядящий макет таблицы ASCII:

Для большей наглядности я попытался заменить это заголовок «время» символом 🕑 из Unicode…

Ого. Очевидно, что-то случилось. Довольно быстро я обнаружил, что вычисленная длина символа оказалась равной 2!
Изначально я подозревал ошибку в модуле, который неправильно определял истинную оптическую длину строки (это printable-characters, который выполняет работу, BTW). Какого черта? Похоже, вы не можете полагаться на str.length, чтобы получить его визуализированную длину: вам нужно сначала удалить символы нулевой ширины и escape-последовательности ANSI, иначе ваша строка может оказаться шире, чем есть на самом деле!
Итак, я подумал, что 🕑.length могло быть ошибочно вычислено как 2, и этот просчет привел к наблюдаемому нарушению компоновки таблицы. Это происходит потому, что символ «часы» занимает несколько кодовых точек Unicode или что-то в этом роде, а встроенный .length просто недостаточно умен, чтобы управлять им. Вот подробное объяснение проблемы, и я настоятельно рекомендую его прочитать:
… Но потом, внезапно, я понял еще одну вещь. Фактическая видимая ширина 🕑 может действительно быть «2» из VSCode:

Но не совсем ... Как видите, это не точно ни 2, ни 1 (и это нарушает согласованность). Но как это отображается в различных консольных терминалах? Проверьте это в интегрированном терминале VSCode:

… И встроенное приложение терминала из MacOS:

Кажется, что популярные терминалы отображают все строго в моноширинном формате, и сам пользователь должен вручную поставить пустое место после каждого из этих новых причудливых глифов Unicode, чтобы освободить для них место в моноширинной сетке:

Таким образом, наше первоначальное предположение должно быть верным, и мы должны исправить printable-characters, чтобы он мог понимать эти символы. Приведенная выше статья очень помогла мне с этой задачей. Таким образом, все важные биты, необходимые для правильного макета ASCII, теперь работают как шарм, не нарушая символы нулевой ширины, Emojis и escape-коды ANSI:
// determining the real length of a string
strlen ('💩') === 1
// getting first N visible symbols
first ('💩23456789', 3) === '💩23'
// filling with whitespace
blank ('\t💩foo') === '\t '
Ура! Теперь as-table способен красиво отображать эти причудливые символы:

об авторе
Я являюсь членом команды разработчиков кросс-языковой библиотеки с открытым исходным кодом CCXT Library (доступной для JavaScript, Python или PHP). Библиотека используется для подключения и торговли с более чем 85 биржами криптовалют по всему миру.