如何在 Prolog 中对 html 属性值进行转义?

问题描述 投票:0回答:4

我在库(sgml)中找到谓词 xml_quote_attribute/2 SWI-Prolog 的。该谓词适用于第一个参数 作为输入,第二个参数作为输出:

?- xml_quote_attribute('<abc>', X).
X = '&lt;abc&gt;'.

但我不知道如何进行反向转换。 例如,以下查询不起作用:

?- xml_quote_attribute(X, '&lt;abc&gt;').
ERROR: Arguments are not sufficiently instantiated

还有另一个谓词可以完成这项工作吗?

再见

html prolog swi-prolog
4个回答
3
投票

这就是 Ruud 的解决方案,使用 DCG 符号+推回列表/半上下文符号。

:- use_module(library(dcg/basics)).

html_unescape --> sgml_entity, !, html_unescape.
html_unescape, [C] --> [C], !, html_unescape.
html_unescape --> [].

sgml_entity, [C] --> "&#", integer(C), ";".
sgml_entity, "<" --> "&lt;".
sgml_entity, ">" --> "&gt;".
sgml_entity, "&" --> "&amp;".

使用 DCG 使代码更具可读性。它还消除了一些多余的回溯,Cookie Monster 指出这是使用

append/3
的结果。


1
投票

这是简单的解决方案,使用字符代码列表。它很可能不会为您提供最佳性能,但对于不是很长的字符串,它可能没问题。

html_unescape("", "") :- !.

html_unescape(Escaped, Unescaped) :-
    append("&", _, Escaped),
    !,
    append(E1, E2, Escaped),
    sgml_entity(E1, U1),
    !,
    html_unescape(E2, U2),
    append(U1, U2, Unescaped).

html_unescape(Escaped, Unescaped) :-
    append([C], E2, Escaped),
    html_unescape(E2, U2),
    append([C], U2, Unescaped).

sgml_entity(Escaped, [C]) :-
    append(["&#", L, ";"], Escaped),
    catch(number_codes(C, L), error(syntax_error(_), _), fail),
    !.

sgml_entity("&lt;", "<").
sgml_entity("&gt;", ">").
sgml_entity("&amp;", "&").

您必须自己填写 SGML 实体列表。

输出示例:

?- html_unescape("&lt;a&gt; &#26361;&#25805;", L), format('~s', [L]).
<a> 曹操
L = [60, 97, 62, 32, 26361, 25805].

1
投票

如果你不介意链接外部模块,那么你可以在C中进行非常高效的实现。

html_unescape.pl:

:- module(html_unescape, [ html_unescape/2 ]).
:- use_foreign_library(foreign('./html_unescape.so')).

html_unescape.c:

#include <stdio.h>
#include <string.h>
#include <SWI-Prolog.h>

static int to_utf8(char **unesc, unsigned ccode)
{
    int ok = 1;
    if (ccode < 0x80)
    {
        *(*unesc)++ = ccode;
    }
    else if (ccode < 0x800)
    {
        *(*unesc)++ = 192 + ccode / 64;
        *(*unesc)++ = 128 + ccode % 64;
    }
    else if (ccode - 0xd800u < 0x800)
    {
        ok = 0;
    }
    else if (ccode < 0x10000)
    {
        *(*unesc)++ = 224 + ccode / 4096;
        *(*unesc)++ = 128 + ccode / 64 % 64;
        *(*unesc)++ = 128 + ccode % 64;
    }
    else if (ccode < 0x110000)
    {
        *(*unesc)++ = 240 + ccode / 262144;
        *(*unesc)++ = 128 + ccode / 4096 % 64;
        *(*unesc)++ = 128 + ccode / 64 % 64;
        *(*unesc)++ = 128 + ccode % 64;
    }
    else
    {
        ok = 0;
    }
    return ok;
}

static int numeric_entity(char **esc, char **unesc)
{
    int consumed;
    unsigned ccode;
    int ok = (sscanf(*esc, "&#%u;%n", &ccode, &consumed) > 0 ||
              sscanf(*esc, "&#x%x;%n", &ccode, &consumed) > 0) &&
             consumed > 0 &&
             to_utf8(unesc, ccode);
    if (ok)
    {
        *esc += consumed;
    }
    return ok;
}

static int symbolic_entity(char **esc, char **unesc, char *name, int ccode)
{
    int ok = strncmp(*esc, name, strlen(name)) == 0 &&
             to_utf8(unesc, ccode);
    if (ok)
    {
        *esc += strlen(name);
    }
    return ok;
}

static foreign_t pl_html_unescape(term_t escaped, term_t unescaped)
{
    char *esc;
    if (!PL_get_chars(escaped, &esc, CVT_ATOM | REP_UTF8))
    {
        PL_fail;
    }
    else if (strchr(esc, '&') == NULL)
    {
        return PL_unify(escaped, unescaped);
    }
    else
    {
        char buffer[strlen(esc) + 1];
        char *unesc = buffer;
        while (*esc != '\0')
        {
            if (*esc != '&' || !(numeric_entity(&esc, &unesc) ||
                                 symbolic_entity(&esc, &unesc, "&lt;", '<') ||
                                 symbolic_entity(&esc, &unesc, "&gt;", '>') ||
                                 symbolic_entity(&esc, &unesc, "&amp;", '&')))
                                    // TODO: more entities...
            {
                *unesc++ = *esc++;
            }
        }
        return PL_unify_chars(unescaped, PL_ATOM | REP_UTF8, unesc - buffer, buffer);
    }
}

install_t install_html_unescape()
{
    PL_register_foreign("html_unescape", 2, pl_html_unescape, 0);
}

以下语句将从 html_unescape.c 构建一个共享库 html_unescape.so。在 Ubuntu 14.04 上测试; Windows 上可能会有所不同。

swipl-ld -shared -o html_unescape html_unescape.c

启动SWI-Prolog:

swipl html_unescape.pl

输出示例:

?- html_unescape('&lt;a&gt; &#26361;&#25805;', S).
S = '<a> 曹操'.

特别感谢 SWI-Prolog 文档和源代码,以及 将 unicode 代码点转换为 UTF8 的 C 库?


0
投票

不希望成为最终答案,因为它没有给出 SWI-Prolog 的解决方案。对于基于 Java 的解释器来说,这个问题 XML 转义不是 J2SE 的一部分,至少不是简单的 表单(不知道如何使用 Xerxes 等)。

一个可能的途径是从 StringEscapeUtils ( * ) 接口 阿帕奇共享。但话又说回来,这对于 Android既然有一个类TextUtil。所以我们推出了自己的 ( * * ) 转换很少。其工作原理如下:

?- text_escape('<abc>', X).
X = '&lt;abc&gt;'
?- text_escape(X, '&lt;abc&gt;').
X = '<abc>'

注意 Java 方法 codePointAt() 和 charCount() 的使用 分别是Java源代码中的appendCodePoint()。所以它 还可以转义和取消转义基本以上的代码点 平面,即在 >0xFFFF 的范围内(当前未实现, 留作练习)。

另一方面,Apache 库(至少 2.6 版)是 不知道代理对,并将在每个代理对中放置两个小数实体 代码点改为 1。

再见

( * ) Java:类 StringEscapeUtils 源
http://grepcode.com/file/repo1.maven.org/maven2/commons-lang/commons-lang/2.6/org/apache/commons/lang/Entities.java#Entities.escape%28java.io.Writer ,java.lang.String%29

© www.soinside.com 2019 - 2024. All rights reserved.