带有Lua的Unescape数字XML实体

问题描述:

对于消除数字HTML/XML实体的好实现
和替换它们与ASCII等效?带有Lua的Unescape数字XML实体

表示为一个单元测试:

local orig = "It's the "end" &ok;
" 
local fixd = unescape(orig) -- Implement this 
assert(fixd == "It's the \"end\" &ok;\n") 

这里有一个简单的实现,也负责处理核心命名XML实体:

function unescape(str) 
    str = string.gsub(str, '&lt;', '<') 
    str = string.gsub(str, '&gt;', '>') 
    str = string.gsub(str, '&quot;', '"') 
    str = string.gsub(str, '&apos;', "'") 
    str = string.gsub(str, '&#(%d+);', function(n) return string.char(n) end) 
    str = string.gsub(str, '&#x(%d+);', function(n) return string.char(tonumber(n,16)) end) 
    str = string.gsub(str, '&amp;', '&') -- Be sure to do this after all others 
    return str 
end 

print(unescape("&#34;Hello&quot; &apos;World&#39;")) --> "Hello" 'World' 

但是请注意,这个失败的一种病理性的情况:一个数字&符实体后跟文字amp;

print(unescape("Ampersand entity is &#38;amp;")) --> Ampersand entity is & 
-- The result should actually be      Ampersand entity is &amp; 

我们可以通过处理一次所有的实体解决这个边缘情况,但代码得到一个好一点的丑陋:

function unescape(str) 
    local map={ ["lt"]="<", ["gt"]=">", ["amp"]="&", ["quot"]='"', ["apos"]="'" } 
    str = string.gsub(str, '(&(#?x?)([%d%a]+);)', function(orig,n,s) 
    return (n=='' and map[s]) 
      or (n=="#x" and tonumber(s,16)) and string.char(tonumber(s,16)) 
      or (n=="#" and tonumber(s)) and string.char(s) 
      or orig 
    end) 
    return str 
end 

print(unescape("Ampersand entity is &#38;amp;")) --> Ampersand entity is &amp; 

最后,我们可以解开它多一点速度:

local gsub, char = string.gsub, string.char 
local entityMap = {["lt"]="<",["gt"]=">",["amp"]="&",["quot"]='"',["apos"]="'"} 
local entitySwap = function(orig,n,s) 
    return (n=='' and entityMap[s]) 
     or (n=="#" and tonumber(s)) and string.char(s) 
     or (n=="#x" and tonumber(s,16)) and string.char(tonumber(s,16)) 
     or orig 
end 
function unescape(str) 
    return (gsub(str, '(&(#?x?)([%d%a]+);)', entitySwap)) 
end 
+0

应检查'n ==可 “”'的'entityMap'情况下(或者你可能会不小心匹配'' mp;) – daurnimator 2016-11-10 15:27:49

对于少数程序员在下载法语html内容时可能需要避免重音,这里是上述功能的更广泛的版本。

local function unescape(str) 
    str = string.gsub(str, '&nbsp;', ' ') 
    str = string.gsub(str, '&iexcl;', '¡') 
    str = string.gsub(str, '&cent;', '¢') 
    str = string.gsub(str, '&pound;', '£') 
    str = string.gsub(str, '&curren;', '¤') 
    str = string.gsub(str, '&yen;', '¥') 
    str = string.gsub(str, '&brvbar;', '¦') 
    str = string.gsub(str, '&sect;', '§') 
    str = string.gsub(str, '&uml;', '¨') 
    str = string.gsub(str, '&copy;', '©') 
    str = string.gsub(str, '&ordf;', 'ª') 
    str = string.gsub(str, '&laquo;', '«') 
    str = string.gsub(str, '&not;', '¬') 
    str = string.gsub(str, '&shy;', '­') 
    str = string.gsub(str, '&reg;', '®') 
    str = string.gsub(str, '&macr;', '¯') 
    str = string.gsub(str, '&deg;', '°') 
    str = string.gsub(str, '&plusmn;', '±') 
    str = string.gsub(str, '&sup2;', '²') 
    str = string.gsub(str, '&sup3;', '³') 
    str = string.gsub(str, '&acute;', '´') 
    str = string.gsub(str, '&micro;', 'µ') 
    str = string.gsub(str, '&para;', '¶') 
    str = string.gsub(str, '&middot;', '·') 
    str = string.gsub(str, '&cedil;', '¸') 
    str = string.gsub(str, '&sup1;', '¹') 
    str = string.gsub(str, '&ordm;', 'º') 
    str = string.gsub(str, '&raquo;', '»') 
    str = string.gsub(str, '&frac14;', '¼') 
    str = string.gsub(str, '&frac12;', '½') 
    str = string.gsub(str, '&frac34;', '¾') 
    str = string.gsub(str, '&iquest;', '¿') 
    str = string.gsub(str, '&Agrave;', 'À') 
    str = string.gsub(str, '&Aacute;', 'Á') 
    str = string.gsub(str, '&Acirc;', 'Â') 
    str = string.gsub(str, '&Atilde;', 'Ã') 
    str = string.gsub(str, '&Auml;', 'Ä') 
    str = string.gsub(str, '&Aring;', 'Å') 
    str = string.gsub(str, '&AElig;', 'Æ') 
    str = string.gsub(str, '&Ccedil;', 'Ç') 
    str = string.gsub(str, '&Egrave;', 'È') 
    str = string.gsub(str, '&Eacute;', 'É') 
    str = string.gsub(str, '&Ecirc;', 'Ê') 
    str = string.gsub(str, '&Euml;', 'Ë') 
    str = string.gsub(str, '&Igrave;', 'Ì') 
    str = string.gsub(str, '&Iacute;', 'Í') 
    str = string.gsub(str, '&Icirc;', 'Î') 
    str = string.gsub(str, '&Iuml;', 'Ï') 
    str = string.gsub(str, '&ETH;', 'Ð') 
    str = string.gsub(str, '&Ntilde;', 'Ñ') 
    str = string.gsub(str, '&Ograve;', 'Ò') 
    str = string.gsub(str, '&Oacute;', 'Ó') 
    str = string.gsub(str, '&Ocirc;', 'Ô') 
    str = string.gsub(str, '&Otilde;', 'Õ') 
    str = string.gsub(str, '&Ouml;', 'Ö') 
    str = string.gsub(str, '&times;', '×') 
    str = string.gsub(str, '&Oslash;', 'Ø') 
    str = string.gsub(str, '&Ugrave;', 'Ù') 
    str = string.gsub(str, '&Uacute;', 'Ú') 
    str = string.gsub(str, '&Ucirc;', 'Û') 
    str = string.gsub(str, '&Uuml;', 'Ü') 
    str = string.gsub(str, '&Yacute;', 'Ý') 
    str = string.gsub(str, '&THORN;', 'Þ') 
    str = string.gsub(str, '&szlig;', 'ß') 
    str = string.gsub(str, '&agrave;', 'à') 
    str = string.gsub(str, '&aacute;', 'á') 
    str = string.gsub(str, '&acirc;', 'â') 
    str = string.gsub(str, '&atilde;', 'ã') 
    str = string.gsub(str, '&auml;', 'ä') 
    str = string.gsub(str, '&aring;', 'å') 
    str = string.gsub(str, '&aelig;', 'æ') 
    str = string.gsub(str, '&ccedil;', 'ç') 
    str = string.gsub(str, '&egrave;', 'è') 
    str = string.gsub(str, '&eacute;', 'é') 
    str = string.gsub(str, '&ecirc;', 'ê') 
    str = string.gsub(str, '&euml;', 'ë') 
    str = string.gsub(str, '&igrave;', 'ì') 
    str = string.gsub(str, '&iacute;', 'í') 
    str = string.gsub(str, '&icirc;', 'î') 
    str = string.gsub(str, '&iuml;', 'ï') 
    str = string.gsub(str, '&eth;', 'ð') 
    str = string.gsub(str, '&ntilde;', 'ñ') 
    str = string.gsub(str, '&ograve;', 'ò') 
    str = string.gsub(str, '&oacute;', 'ó') 
    str = string.gsub(str, '&ocirc;', 'ô') 
    str = string.gsub(str, '&otilde;', 'õ') 
    str = string.gsub(str, '&ouml;', 'ö') 
    str = string.gsub(str, '&divide;', '÷') 
    str = string.gsub(str, '&oslash;', 'ø') 
    str = string.gsub(str, '&ugrave;', 'ù') 
    str = string.gsub(str, '&uacute;', 'ú') 
    str = string.gsub(str, '&ucirc;', 'û') 
    str = string.gsub(str, '&uuml;', 'ü') 
    str = string.gsub(str, '&yacute;', 'ý') 
    str = string.gsub(str, '&thorn;', 'þ') 
    str = string.gsub(str, '&yuml;', 'ÿ') 
    str = string.gsub(str, '&euro;', '€') 
    str = string.gsub(str, '&#(%d+);', function(n) return string.char(n) end) 
    str = string.gsub(str, '&#x(%d+);', function(n) return string.char(tonumber(n,16)) end) 
    str = string.gsub(str, '&amp;', '&') -- Be sure to do this after all others 
    return str 
end 
+0

注意,技术上这个名单是适当的HTML,不是XML。但是谢谢你的加入! – Phrogz 2016-07-19 13:43:18

+0

'κ'意味着'к'(俄语字母),但它失败 – val 2017-06-09 17:10:09