Wbudowany sposób dekodowania jednostek HTML (tj. „Lub” # 39;)

11

Ostatnio natknąłem się na problem dekodowania jednostek HTML. Mam następujące dwa ciągi ( zwróć uwagę, jak używane są dwie metody kodowania, nazwane i numerowane ).

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

I muszę je przekonwertować

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

Rozglądając się, znalazłem to stare pytanie na SO (co obecnie robię), ale nie chcę wierzyć, że Emacs nie ma wbudowanego sposobu, aby to zrobić. Mamy kilka przeglądarek internetowych, z których przynajmniej dwie są wbudowane, nie wspominając o klientach pocztowych i czytnikach kanałów.

Czy nie ma wbudowanego sposobu dekodowania jednostek HTML?
Szukam funkcji, która pobiera ciąg z pierwszego przykładu i zwraca ciąg z drugiego przykładu.

Malabarba
źródło
Jeśli coś jest, założę się, że musi być w kodzie nxml, ponieważ jest w stanie parsować DTD i może sprawdzać poprawność jednostek w dokumencie.
wasamasa,
libxml-parse-html-regionrobi to oczywiście, ale może zrobić więcej, niż chcesz, ponieważ analizuje również tagi HTML… (I chyba nie wszystkie Emacsy są zbudowane z obsługą LibXML, jak sądzę).
Jon O.

Odpowiedzi:

7

Emacs zawiera analizator składający się wyłącznie z Elisp XML xml.el, którego xml-parse-stringfunkcja wykonuje to zadanie, choć wygląda trochę jak nieudokumentowana funkcja wewnętrzna. Nie jestem pewien, czy istnieją jakieś jednostki tylko HTML, które nie będą poprawnie obsługiwane przez traktowanie łańcucha jako fragmentu XML.

Ta funkcja otoki po prostu pominie wszystkie końcowe tagi w ciągu wejściowym, chociaż możesz to zaostrzyć:

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

W Emacsie z obsługą LibXML, innym nieco hackerskim sposobem byłoby napisanie otoki libxml-html-parse-region. Ponieważ parser LibXML zakłada, że ​​jego argument jest kompletnym dokumentem HTML, funkcja opakowania musi wyodrębnić przeanalizowane dane znakowe ze struktury zwróconego dokumentu, używając pcase. Próba odkodowania ciągu zawierającego dowolne tagi HTML spowoduje błąd:

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

Wyniki:

(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

Odkodowanie fragmentu dokumentu przez parsowanie go jako kompletnego dokumentu wydaje się trochę wstecz, tylko po to, aby natychmiast usunąć otaczające znaczniki. Z drugiej strony, używanie LibXML powinno być szybkie i dawać dokładne wyniki.

Jon O.
źródło
Przepraszam, nie widziałem twojej edycji xml. Wyglada świetnie.
Malabarba
Dzięki - zredagowałem odpowiedź, aby na początku umieścić prostsze xml.elrozwiązanie.
Jon O.
@Malabarba Uwaga, lisp/xml.elktóra zawsze zawierała funkcję xml-substitute-special, która wykonuje to samo dekodowanie bytu, co Jon O.'sdecode-entities . Nie pomija jednak tagów końcowych.
Basil
2

web-mode.elrobi to z web-mode-dom-entities-replace.

fxbois
źródło