Встроенный способ декодирования HTML-объектов (т. Е. & Quot; или & # 39;)

11

Недавно я столкнулся с проблемой декодирования HTML-объектов. У меня есть следующие две строки ( обратите внимание, как используются два метода кодирования, именованные и пронумерованные ).

The old "how to fold xml" question
Babel doesn't wrap results in verbatim

И мне нужно преобразовать их в

The old "how to fold xml" question
Babel doesn't wrap results in verbatim

Осматривая, я нашел этот старый вопрос о SO (что я сейчас и делаю), но я отказываюсь верить, что Emacs не имеет встроенного способа сделать это. У нас есть несколько веб-браузеров, по крайней мере два из которых, я знаю, являются встроенными, не говоря уже о почтовых клиентах и ​​читателях каналов.

Разве нет встроенного способа декодирования html-сущностей?
Я ищу функцию, которая берет строку из первого примера и возвращает строку из второго примера.

Malabarba
источник
Если что-то есть, я уверен, что это должно быть в коде nxml, так как он способен анализировать DTD и может проверять сущности в документе.
Васамаса
libxml-parse-html-regionделает это, конечно, но он может делать больше, чем вы хотите, в том смысле, что он также анализирует HTML-теги… (И я полагаю, что не все Emacs построены с поддержкой LibXML).
Джон О.

Ответы:

7

Emacs включает в себя синтаксический анализатор XML с чистым Elisp xml.el, чья xml-parse-stringфункция выполняет свою работу, хотя это выглядит как недокументированная внутренняя функция. Я не уверен, есть ли какие-либо объекты только для HTML, которые не будут должным образом обработаны, рассматривая строку как фрагмент XML.

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

(defun decode-entities (html)
  (with-temp-buffer
    (save-excursion (insert html))
    (xml-parse-string)))

(decode-entities "The old "how to fold xml" question")
;; => "The old \"how to fold xml\" question"

(decode-entities "doesn't")
;; => "doesn't"

(decode-entities "string with trailing tag: <tag/>")
;; => "string with trailing tag: "

В Emacs с поддержкой LibXML, другой слегка хакерский способ - написать обертку libxml-html-parse-region. Так как синтаксический анализатор LibXML предполагает, что его аргумент является полным документом HTML, функция-обертка должна извлекать проанализированные символьные данные из возвращенной структуры документа, используя pcase. Попытка декодировать строку, содержащую какие-либо теги HTML, приведет к ошибке:

(defun decode-entities/libxml (html)
  (with-temp-buffer
    (insert html)
    (let ((document
           (libxml-parse-html-region (point-min) (point-max))))
      (pcase document
        (`(html nil
                (body nil
                      (p nil
                         ,(and (pred stringp)
                               content))))
          content)
        (_ (error "Unexpected parse result: %S" document))))))

Результаты:

(decode-entities/libxml "The old &quot;how to fold xml&quot; question")
     ; => "The old \"how to fold xml\" question"
(decode-entities/libxml "doesn&#39;t") ; => "doesn't"

(decode-entities/libxml "<html>")              ; produces an error

Кажется немного отсталым декодировать фрагмент документа, анализируя его как законченный документ, только чтобы сразу удалить лишние теги. С другой стороны, использование LibXML должно быть быстрым и давать точные результаты.

Джон О.
источник
Извините, я не видел ваше редактирование XML. Выглядит потрясающе
Малабарба
Спасибо - я отредактировал ответ, чтобы xml.elсначала поставить более простое решение.
Джон О.
@Malabarba Обратите внимание , что lisp/xml.elвсегда включали в себя функцию xml-substitute-special, которая выполняет ту же сущность декодирования , как Джон О. - хdecode-entities . Однако он не пропускает конечные теги.
Василий