[Оригинал] Делимся опытом: сколько вещей может включать в себя маленький смайлик?

Java

предисловие

Я также поделился большим опытом наступания на ямы в своей работе:

  1. Думая об онлайн-проблеме: как кластер реестра Eureka обеспечивает загрузку клиентских запросов и аварийное переключение?
  2. [Оригинал] Обмен опытом: кровавый случай, вызванный длиной содержания (почти ....)

Сегодня поделюсь реальным случаем на работе:

На странице списка оценки продукта отображаются сведения об оценке каждого пользователя.В целях защиты конфиденциальности пользователей требуется отображать только первую и последнюю цифры псевдонима пользователя, а остальные заменять на *.

Например, ввод: 🐳🐳🐠, вывод: 🐳***🐠

Это кажется пресным требованием, и мне все равно. Сервер хранит информацию о комментариях пользователя вdb, интерфейс списка обзоров предназначен для отображения обзорной информации о продукте в базе данных, а псевдоним рецензента достаточен при особой обработке.

но! !Тестовые одноклассники обнаружили, что никнейм пользователя содержитemoji表情При возникновении проблемы данные резки будутЗнак вопроса показывает! !

Пример кода для моделирования выглядит следующим образом:

字符串截取.png

вывод:

输出.png

Увидев этот результат, я действительно сбит с толку, это совсем не тот результат, которого я хочу! ! !

黑人问号.png

Эти три рыбы можно считать преткновением для меня, можно ли только сказать на тестсмайлики слишком особенныене обрабатывать? потомкокетливыйПерепутать?

Долго думая об этом, я все же решил столкнуться с этой проблемой и решить ее!(Ведь я еще тот маленький умный призрак, который не боится трудностей 🤔)

问题不大.png

PS: Эта статья во многом навеяна тем, что коллега по компании поделился юникодом. Здесь я отдаю должное моему учителю! В следующем содержании будет поэтапно проанализировано создание этой проблемы и окончательное решение.

Концептуальные знания

Чтобы решить эти проблемы, вы должны заложить некоторые базовые знания. Вам не терпится увидеть решение. Вы можете использовать пример кода в конце статьи.

utf8mb4

Как правило, мы используем этот формат кодировки по умолчанию при создании таблиц в базе данных:

utf8mb4编码.png

Я думаю, что все знакомы с этим форматом кодирования, когда мы хотим сохранитьemojiданные в базу данных, то формат базы данных должен быть указан какutf8mb4В противном случае хранилище сообщит об ошибке. Так во многих компанияхdb规范, кодировка базы данных по умолчанию должна бытьutf8mb4

emoji保存报错.png

Но вы когда-нибудь задумывались, почему?utf8ни за чтоutf8mb4Просто сделай это? что здесьизогнутая дорога?

Это включает в себяunicodeАктуальные знания, о которых мы упомянем ниже, все будут продолжать читать.

существуетmysql 5.5До,utf8поддерживается только кодировка1-3байт, изmysql 5.5Старт, может поддерживать 4 байтаUTFкодированиеutf8mb4Персонаж может иметь больше всего4 байта, поэтому он может поддерживать больше наборов символов.

emoji长度.png

Эта таблица содержит всеemojiи соответствующийunicodeкод, но и соответствующийutf-8Реализация кодировки.

Это также можно увидеть с фигурыemojiдля выражений лицаutf-8Занимать при выражении4 байта, поэтому база данных используетutf8не может быть сохраненemojiПричина выражения.

Точно так же мы можем такжеjavaпосмотри в кодеemojiЗанимает несколько байт в длину:

emoji长度.png

мы также можем видетьString.getBytes(), по умолчаниюutf-8Закодировано:

String.getBytes编码格式.png

ASCII-код

Представлено вышеutf8mb4упоминается время от времениunicode, прежде чем представить его, мы также должны упомянуть нашего старого друга:ASCIIкод

ASCII (Американский стандартный код для обмена информацией) — это компьютерная система кодирования, основанная на латинском алфавите. Он в основном используется для отображения современного английского языка.

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

ASCII码.png

Но это может отображаться только для представления латиницы, чего явно недостаточно.

Unicode

Очевидно, что развитие компьютеров поддерживает не только английский как язык,ASCIIОграничение состоит в том, что он может отображать только26个Базовый латинский алфавит, арабские цифры и британская пунктуация, поэтому его можно использовать только для отображения современного американского варианта английского языка.

Тогда, если есть набор символов, который содержит все текст в мире, в каждом регионе текста в этом наборе символов есть уникальное двоичное представление, так что не будет искажена проблемой. такUnicodeОно тоже возникло.

концепция

Юникод, также известный как Универсальный код, Международный код, Юникод и Единый код на китайском языке, является отраслевым стандартом в области компьютерных наук. Он упорядочивает и кодирует большинство систем письма в мире, облегчая компьютерам представление и обработку слов.

плоский

UnicodeПервый, кто призналASCIIЗанять0-127Легитимность целочисленных ресурсов после повторного использования128-65535Имея так много целочисленных ресурсов, мы можем присвоить целое число каждому символу различных языков мира.

после,UnicodeОткрытие Альянса65536Количество целых чисел недостаточно для выделения, поэтому мы просто присваиваем следующие по одному.16 65536число65536-1114111Заняты целые ресурсы, а затем более занятые16 65536Сегменты называются16 самолетов, плюс оригинал0-65535 Квартира,UnicodeВсего17 самолетов.比如第 1 平面就是65536-131072. Конечно, пока только7 самолетоввыйти.

Unicode平面.png

Самолет 0,ДаUnicode中的一个编码区段。 кодирование изU+0000кU+FFFF, символы этого плана мы используем чаще всего.

65535Персонажи, назначенные после этого, в основномemojiВыражения типа 😺 Да128570 (\uD83D\uDE3A)

Вот рекомендуемый онлайн-сайт для преобразования кода:Длинные волосы, это тоже больно.com/C encode.htm…

在线utf16转换.png

Это представляет диапазон

UnicodeДиапазон представления:U+0000 ~ U+10FFFF

  • Это примерно: U+0000~U+110000 (плюс 1), что составляет 17 FFFF (65535)
  • Почти 17 * 6w, есть около 100 кодовых точек, которые можно использовать для сопоставления символов.
  • Точное значение — 1114 112, что составляет почти 112 кодовых точек.
  • Последняя версия Unicode содержит 136 690 символов, что далеко не 100 Вт.
  • Unicode официально заявил, что текущие кодовые точки достаточны и не будут расширяться в будущем.

Метод реализации

Unicodeреализуется иначе, чем кодирование. один персонажUnicodeКодирование является детерминированным. Однако в реальном процессе передачи, поскольку конструкции различных системных платформ не обязательно согласованы, и в целях экономии места,UnicodeКодировка реализована по-разному.Unicodeреализация называетсяUnicodeФормат преобразования (формат преобразования Unicode, называемый UTF).

за то, чтоUnicode收录的字符其编码是唯一且确定的。 ноUnicodeреализации (для транспортировки, хранения, обработки или обратной совместимости) различаются по нескольким параметрам, наиболее популярными из которых являютсяUTF-8,UTF-16,UCS2,UCS4/UTF-32подождите, есть подразделенияРазмерразница.

о насJava, отcharЗанять2 байтасделать вывод, что с помощьюUTF-16код для хранения

Хорошая статья рекомендуется для различных проблем с кодированием:Углубленный анализ проблем китайской кодировки в Java

Определите, следует ли включать китайский язык

Наверное понял вышеUnicodeзначение и использованиеИтак, каков практический эффект от знания этого материала?

Давайте рассмотрим небольшое требование, например:Как определить, что строка содержит китайский язык?

Я думаю, что все сталкивались с таким спросом.Вообщем, мы пойдем на Baidu Yitong, и мы должны быть в состоянии найти регулярное выражение, чтобы судить, содержит ли оно китайский язык, а затем решить проблему с радостью.

Так уж получилось, что в нашей системе есть такое штатное суждение, которое инкапсулируется коллегами из группы архитектуры, давайте посмотрим на него вместе:

是否含有中文.png

Очевидно, здесь черезUnicodeЕсть ли проблема в оценке интервала?

Интервал здесь используетсяЕдиные иероглифы Китая, Японии и Кореи, но это версия 1993 года, которая содержит большую часть нашего общеупотребительного китайского языка, в общей сложности20902После просмотра дополненной версии было добавлено много слов, поэтому вполне возможно, что метод суждения, который мы используем сейчас, определенно пропустит слова, добавленные позже:

中日韩统一表意文字.png

Используем прибавку 2000 г.Зона расширения единой иероглифы Китай-Япония-Корея AДавайте проверим это на примере:

中日韩统一表意文字扩展区A.png

Здесь добавлено много неупотребительных слов, а я их даже не знаю.Давайте воспользуемся данными во второй строке для проверки:

验证是否包含中文.png

Вы удивлены, увидев это? и кричать, что вы написалиbug, Ха-ха.

写Bug.png

На самом деле это не означает, что наше обычное суждениеbug, необходимо проверить, достаточно ли точны наши потребности, чтобы распознать все редкие слова. Судя по привычкам пользователя, вероятность ввести эти необычные слова не очень высока, поэтому с обратной связью от мелких партнеров проблем не возникает.

Решить проблему перехвата эмодзи

Ближе к дому ведь нам еще предстоит решить поднятую в начале проблему, как правильно перехватить содержимоеemojiНить? отсюда изUTF-16Кодирование, чтобы начать с.

UTF-16

UTF-16 конкретно определяет, как символы Unicode могут быть доступны на компьютерах. UTF-16 использует два байта для представления формата преобразования Unicode.Это метод представления с фиксированной длиной.Независимо от того, какой символ может быть представлен двумя байтами, два байта составляют 16 бит, поэтому он называется UTF-16. UTF-16 очень удобен для представления символов.Каждые два байта представляют один символ, что значительно упрощает работу при работе со строками.Это также очень важная причина, по которой Java использует UTF-16 в качестве формата хранения символов в памяти.

Кодовые точки в базовой многоязычной плоскости (диапазон кодовых точек от U+0000 до U+FFFF)UTF-16В кодировке используется 1 символ, и его значение такое же, какUnicodeравны (преобразовывать не нужно), это наш обычный китайский иероглиф, например, кодовая точка во вспомогательной плоскости (диапазон кодовых точек U+10000-U+10FFFF) находится вUTF-16кодируется как пара16bitКодовый блок (т.е. 32 бит, 4 байта), называетсясуррогатная пара. Первый из двух символов, составляющих суррогатную пару, называетсясвинцовые суррогатыДиапазон0xD800-0xDBFF, последний называетсяследовые суррогатыДиапазон0xDC00-0xDFFF

surrogate

упомянутый вышеsurrogate,surrogateявляется значение агентства, понятие происходит не отJavaязык, но изUnicodeОдин из методов кодированияUTF-16. Подробнее см.:UTF-16

короче,JavaИнформация о символах в рамках использования языкаUTF-16кодирование. так какcharЭтот тип16-bitиз. это может иметь65536ценность, то есть65536Каждое число может представлять 1 символ. но,Unicodeсодержит гораздо больше символов, чем65536Кусок.那么编号大于65536Да, пользуюсь до сих пор16-bitКодирование, что делать? тогдаUnicodeТо, что придумала группа по установлению стандартов, заключалось в следующем:65536номер, снять2048, оговорив, что они「Surrogates」, пусть они будут группой из двух для представления чисел больше, чем65536Эти персонажи.

Точнее, числоU+D800кU+DBFFуказывается как「High Surrogates」,общий1024Кусок. НетU+DC00 кU+DFFFуказывается как「Low Surrogates」,Слишком1024Кусок. Когда они появляются в комбинации, они могут выражать больше1048576вид персонажей.

Причина эмодзи перехвата исключения

Выше приведены некоторые концептуальные знания.Если действительно легко запутаться, давайте оглянемся назад и начнем с кода:

昵称.png

мы можем поставитьemojiвыделяются следующим образом:

🐳 ->\uD83D\uDC33

🐳 ->\uD83D\uDC33

🐠 ->\uD83D\uDC20

emojiопределенно больше, чем65536, так что здесь мы используем「High Surrogates」и「Low Surrogates」представлены в комбинациях два на два.

вышеизложеннымUTF-16Знание кодирования может сделать вывод, что нашиemojiВозьмите смайликcharПричина искажения символов послеUTF-16Кодирование суррогатных пар во вспомогательной плоскости, и если мы разделим суррогатные пары при перехвате, возникнет нештатная проблема.

Для этого случая мы можем пройтиCharacterКласс Статический методisHighSurrogateиisLowSurrogateсудить, одинemojiКомбинациявысокий + низкий, поэтому для суррогатной пары во вспомогательной плоскости ее можно полностью убрать или оставить.

isHighSurrogateИсходный код метода выглядит следующим образом:

public static final char MIN_HIGH_SURROGATE = '\uD800';

public static final char MAX_HIGH_SURROGATE = '\uDBFF';

public static boolean isHighSurrogate(char ch) {
    return ch >= MIN_HIGH_SURROGATE && ch < (MAX_HIGH_SURROGATE + 1);
}

Это суждение на самом деле выше「High Surrogates」, мы можем преобразовать его в:

U+D800 <= ch <= U+DBFF

Так же,isLowSurrogateМетод определяется таким же образом:

U+DC00 <= ch <= U+DFFF

проблема решена

Или сначала запустите код, чтобы увидеть эффект:

问题解决.png

Конкретный код реализации выглядит следующим образом:

public static void main(String[] args) {
    // 用户昵称为:🐳🐳🐠,正常结果应该为:🐳***🐠
    String context = "\uD83D\uDC33\uD83D\uDC33\uD83D\uDC20";
    int realNameLength = realStringLength(context);
    String namePrefix = subString(context, 1, 0);
    String nameSuffix = subString(context, realNameLength - 1, 1);
    context = String.format("%s%s%s", namePrefix, "***", nameSuffix);
    System.out.println(context);
}

/**
 * 包含emoji表情的subString方法
 *
 * @param str 原有的str
 * @param len str长度
 * @param type type = 0 代表prefix,其他代表suffix
 */
private static String subString(String str, int len, int type) {
    if (len < 0) {
        return str;
    }

    int count = 0;
    for (int i = 0; i < str.length(); i++) {
        if (count == len) {
            // type = 0 代表prefix,其他代表suffix
            if (type == 0) {
                return str.substring(0, i);
            }
            return str.substring(i);
        }

        char c = str.charAt(i);
        if (Character.isHighSurrogate(c) || Character.isLowSurrogate(c)) {
            i++;
        }
        count++;
    }

    return str;
}


/**
 * 包含emoji表情的字符串实际长度
 *
 * @param str 原有str
 * @return str实际长度
 */
private static int realStringLength(String str) {
    int count = 0;
    for (int i = 0; i < str.length(); i++) {
        char c = str.charAt(i);
        if (Character.isHighSurrogate(c) || Character.isLowSurrogate(c)) {
            i++;
        }
        count++;
    }

    return count;
}

Пасхальное яйцо: забери свой смайлик

emojiгораздо больше, чем это,unicodeОн также может поддерживатьemojiПожертвовать, конечно этоemojiбудет назван в честь донора. Существуют следующиеСписок пожертвований:

捐助列表.png

捐助列表2.png

Я видел, что первый был пожертвован elastic.co, и, нажав на ссылку, можно перейти прямо на их официальный сайт. Еще один во втором списке пожертвований был пожертвован моим коллегой, ха-ха, очень интересно.

Если вы хотите пожертвовать себя, вы также можете перейти непосредственно кСайт пожертвований эмоджиПерейдите к заполнению личной информации, всего шестеренок три, после пожертвования в этом списке будут отображаться определенные вамиemojiИнформация такая классная 😎:

一枝花.png

Суммировать

крошечныйemojiЭто действительно бесконечные знания. Из-за проблем с пространством я опустил здесь много вещей, таких какUTF-8иUTF-16Две формы кодирования и не более глубокое объяснение, там будет включать много контента.

Я надеюсь, что эта статья сделаетБросание кирпичей и привлечение нефритаРоль вдохновлять друзей на совместное изучение новых тайн.

Ссылаться на

  1. Википедия Юникод:Это. Wikipedia.org/wiki/u Нико…
  2. Википедия Unicode Character Plane Map:Это. Wikipedia.org/wiki/u Нико…
  3. Не стоит недооценивать крошечные смайлики:nuggets.capable/post/684490…
  4. Разговор о кодировках характера: Unicode, UTF-8 и CHAR []:mess.horse/post/insert AC…
  5. Проблема искажения выражения эмодзи, вызванная усечением символов:супер серия для взрослых.GitHub.IO/2018/06/19/…
  6. Список пожертвований смайликов:Woohoo.Unicode.org/consortium/…