Экранирование символа Unicode «POPCORN» в объект HTML

У меня есть строка с эмодзи в ней

I love ????

Мне нужно избежать этого смайлика попкорна с его html-объектом, поэтому я получаю

I love 🍿

Я пишу свой код на Java, и я пробовал разные библиотеки StringEscapeUtils, но не заработал. Пожалуйста, помогите мне понять, что я могу использовать для экранирования специальных символов, таких как Попкорн.

Для справки:

Информация о символах Unicode

Unicode 8.0 (июнь 2015 г.)


person Murtnowski    schedule 17.08.2019    source источник
comment
Если принимающая система ожидает документ HTML с кодировкой документа US-ASCII, почему бы просто не сериализовать весь документ как таковой? Зачем фокусироваться на конкретных персонажах?   -  person Tom Blodget    schedule 18.08.2019


Ответы (4)


Я бы использовал CharSequence::codePoints чтобы получить IntStream кодовых точек и сопоставить их со строками, а затем собрать их, объединив в одну строку:

public String escape(final String s) {
    return s.codePoints()
        .mapToObj(codePoint -> codePoint > 127 ?
            "&#x" + Integer.toHexString(codePoint) + ";" :
             new String(Character.toChars(codePoint)))
    .collect(Collectors.joining());
}

Для указанного ввода это производит:

I love 🍿
person David Conrad    schedule 17.08.2019

Это немного хакерски, потому что я не верю, что для этого есть готовая библиотека; предполагая, что вы не можете просто использовать UTF-8 (или UTF-16) на своей HTML-странице (которая должна отображать ???? как есть), вы можете использовать Character.codePointAt(CharSequence, int) и Character.offsetByCodePoints(CharSequence, int, int)1 для выполнения преобразования, если заданный символ находится за пределами обычного диапазона ASCII. Что-то типа,

String str = "I love ????";
StringBuilder sb = new StringBuilder();
for (int i = 0; i < str.length(); i++) {
    char ch = str.charAt(i);
    if (ch > 127) {
        sb.append(String.format("&#x%x;", Character.codePointAt(str, i)));
        i += Character.offsetByCodePoints(str, i, 1) - 1;
    } else {
        sb.append(ch);
    }
}
System.out.println(sb);

который выводит (по запросу)

I love &#x1f37f;

1Отредактировано на основе полезные комментарии от Андреаса.

person Elliott Frisch    schedule 17.08.2019
comment
На самом деле я не отображаю это на html-странице. Я передаю его в другую систему, и мое внимание сосредоточено на том, чтобы поведение оставалось таким же, как у устаревшей системы. - person Murtnowski; 17.08.2019
comment
Вы должны кодировать все выше 127, а не 255, чтобы результат состоял только из символов ASCII. - person Andreas; 17.08.2019
comment
Character.codePointCount(str, i, i + 1) всегда возвращает 1. Я полагаю, вы имели в виду i = Character.offsetByCodePoints(str, i, 1) - 1; с -1 в конце, необходимым для смещения i++ в цикле for. --- Чтобы увидеть проблему, вставьте, например, ň в строке, а следующий за ним символ будет пропущен. - person Andreas; 17.08.2019
comment
Я бы предпочел использовать str.codePoints() для получения потока и обработки кодовых точек таким образом. Использование codePointCount и offsetByCodePoints слишком низкоуровневое, утомительное и легко ошибиться. - person David Conrad; 17.08.2019

Вы можете использовать библиотеку unbescape: unbescape: мощные, быстрые и простые операции escape/unescape для Java .

Пример

Добавьте зависимость в файл pom.xml:

<dependency>
    <groupId>org.unbescape</groupId>
    <artifactId>unbescape</artifactId>
    <version>1.1.6.RELEASE</version>
</dependency>

Использование:

import org.unbescape.html.HtmlEscape;
import org.unbescape.html.HtmlEscapeLevel;
import org.unbescape.html.HtmlEscapeType;

<…>

final String inputString = "\uD83C\uDF7F";
final String escapedString = HtmlEscape.escapeHtml(
    inputString,
    HtmlEscapeType.HEXADECIMAL_REFERENCES,
    HtmlEscapeLevel.LEVEL_2_ALL_NON_ASCII_PLUS_MARKUP_SIGNIFICANT
);

// Here `escapedString` has the value: `&#x1f37f;`.

Для вашего варианта использования, вероятно, следует использовать либо HtmlEscapeType.HTML4_NAMED_REFERENCES_DEFAULT_TO_HEXA, либо HtmlEscapeType.HTML5_NAMED_REFERENCES_DEFAULT_TO_HEXA вместо HtmlEscapeType.HEXADECIMAL_REFERENCES.

person Sergey Vyacheslavovich Brunov    schedule 17.08.2019

Обычно работает библиотека emoji4j. Он имеет простой метод htmlify для кодирования HTML.

Например:

String text = "I love ????";

EmojiUtils.htmlify(text); //returns "I love &#127871"

EmojiUtils.hexHtmlify(text); //returns "I love &#x1f37f"
person user11809641    schedule 17.08.2019
comment
Это на самом деле не отвечает на вопрос. Учитывая строку, содержащую эмодзи и другие символы, это не дает никакого способа избежать этой строки. - person David Conrad; 19.08.2019
comment
@DavidConrad Спасибо, что указали на это! Я отредактировал свой ответ, чтобы он использовал метод библиотеки для преобразования смайликов в HTML. - person user11809641; 22.08.2019