Manera incorporada de decodificar entidades HTML (es decir, & quot; o & # 39;)

11

Recientemente me encontré con el problema de decodificar entidades html. Tengo las siguientes dos cadenas (tenga en cuenta cómo se utilizan dos métodos de codificación, nombrados y numerados ).

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

Y necesito convertirlos a

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

Al buscar, encontré esta vieja pregunta sobre SO (que es lo que estoy haciendo por el momento), pero me niego a creer que Emacs no tenga una forma integrada de hacerlo. Tenemos varios navegadores web, al menos dos de los cuales sé que están integrados, sin mencionar los clientes de correo y los lectores de feeds.

¿No hay una forma integrada de decodificar entidades html?
Estoy buscando una función que tome una cadena del primer ejemplo y devuelva una cadena del segundo ejemplo.

Malabarba
fuente
Si hay algo, apuesto a que debe estar en el código nxml, ya que puede analizar DTD y puede validar entidades en el documento.
wasamasa
libxml-parse-html-regionhace esto, por supuesto, pero puede hacer más de lo que desea, ya que analiza las etiquetas HTML también ... (Y supongo que no todos los Emacs están construidos con soporte LibXML).
Jon O.

Respuestas:

7

Emacs incluye un analizador XML puro Elisp xml.el, cuya xml-parse-stringfunción hace el trabajo, aunque parece un poco como una función interna no documentada. No estoy seguro de si hay entidades de solo HTML que no se manejarán correctamente al tratar la cadena como un fragmento XML.

Esta función de contenedor simplemente omitirá las etiquetas finales de la cadena de entrada, aunque podría hacerlo más estricto:

(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: "

En Emacs con soporte para LibXML, otra forma un poco hackeada sería escribir un contenedor libxml-html-parse-region. Dado que el analizador LibXML supone que su argumento es un documento HTML completo, la función de contenedor debe extraer los datos de caracteres analizados de la estructura del documento devuelto, utilizando pcase. Intentar decodificar una cadena que contenga etiquetas HTML producirá un error:

(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))))))

Resultados:

(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

Parece un poco hacia atrás decodificar un fragmento de documento analizándolo como un documento completo, solo para quitar inmediatamente las etiquetas circundantes. Por otro lado, usar LibXML debe ser rápido y dar resultados precisos.

Jon O.
fuente
Lo siento, no había visto tu edición xml. Parece increíble.
Malabarba
Gracias. Edité la respuesta para poner xml.elprimero la solución más simple .
Jon O.
@Malabarba Tenga en cuenta que lisp/xml.elsiempre ha incluido la función xml-substitute-special, que realiza la misma decodificación de entidades que Jon O.'sdecode-entities . Sin embargo, no omite las etiquetas finales.
Basil