我在库(sgml)中找到谓词 xml_quote_attribute/2 SWI-Prolog 的。该谓词适用于第一个参数 作为输入,第二个参数作为输出:
?- xml_quote_attribute('<abc>', X).
X = '<abc>'.
但我不知道如何进行反向转换。 例如,以下查询不起作用:
?- xml_quote_attribute(X, '<abc>').
ERROR: Arguments are not sufficiently instantiated
还有另一个谓词可以完成这项工作吗?
再见
这就是 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, "<" --> "<".
sgml_entity, ">" --> ">".
sgml_entity, "&" --> "&".
使用 DCG 使代码更具可读性。它还消除了一些多余的回溯,Cookie Monster 指出这是使用
append/3
的结果。
这是简单的解决方案,使用字符代码列表。它很可能不会为您提供最佳性能,但对于不是很长的字符串,它可能没问题。
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("<", "<").
sgml_entity(">", ">").
sgml_entity("&", "&").
您必须自己填写 SGML 实体列表。
输出示例:
?- html_unescape("<a> 曹操", L), format('~s', [L]).
<a> 曹操
L = [60, 97, 62, 32, 26361, 25805].
如果你不介意链接外部模块,那么你可以在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, "<", '<') ||
symbolic_entity(&esc, &unesc, ">", '>') ||
symbolic_entity(&esc, &unesc, "&", '&')))
// 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('<a> 曹操', S).
S = '<a> 曹操'.
特别感谢 SWI-Prolog 文档和源代码,以及 将 unicode 代码点转换为 UTF8 的 C 库?
不希望成为最终答案,因为它没有给出 SWI-Prolog 的解决方案。对于基于 Java 的解释器来说,这个问题 XML 转义不是 J2SE 的一部分,至少不是简单的 表单(不知道如何使用 Xerxes 等)。
一个可能的途径是从 StringEscapeUtils ( * ) 接口 阿帕奇共享。但话又说回来,这对于 Android既然有一个类TextUtil。所以我们推出了自己的 ( * * ) 转换很少。其工作原理如下:
?- text_escape('<abc>', X).
X = '<abc>'
?- text_escape(X, '<abc>').
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