将HTML标记转义为HTML实体的最快方法?

时间:2011-03-31 11:28:27

标签: javascript html regex performance string

我正在编写一个Chrome扩展程序,其中涉及执行以下作业的批次:通过转换<来清理可能包含HTML标记的字符串,分别为>&&lt;&gt;&amp;

(换句话说,和PHP的htmlspecialchars(str, ENT_NOQUOTES)相同 - 我认为没有必要转换双引号字符。)

这是迄今为止我发现的最快的功能:

function safe_tags(str) {
    return str.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;') ;
}

但是当我必须一次性运行几千个字符串时,仍然存在很大的滞后。

有人可以改进吗?它主要用于10到150个字符之间的字符串,如果这有所不同的话。

(我有一个想法是不打扰编码大于号 - 这会有什么真正的危险吗?)

12 个答案:

答案 0 :(得分:86)

这是你可以做到这一点的一种方式:

var escape = document.createElement('textarea');
function escapeHTML(html) {
    escape.textContent = html;
    return escape.innerHTML;
}

function unescapeHTML(html) {
    escape.innerHTML = html;
    return escape.textContent;
}

Here's a demo.

答案 1 :(得分:71)

您可以尝试传递回调函数来执行替换:

var tagsToReplace = {
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;'
};

function replaceTag(tag) {
    return tagsToReplace[tag] || tag;
}

function safe_tags_replace(str) {
    return str.replace(/[&<>]/g, replaceTag);
}

这是一个性能测试:http://jsperf.com/encode-html-entities与重复调用replace函数,并使用Dmitrij提出的DOM方法进行比较。

你的方式似乎更快......

为什么你需要它呢?

答案 2 :(得分:27)

Martijn的方法作为原型函数:

String.prototype.escape = function() {
    var tagsToReplace = {
        '&': '&amp;',
        '<': '&lt;',
        '>': '&gt;'
    };
    return this.replace(/[&<>]/g, function(tag) {
        return tagsToReplace[tag] || tag;
    });
};

var a = "<abc>";
var b = a.escape(); // "&lt;abc&gt;"

答案 3 :(得分:9)

最快的方法是:

function escapeHTML(html) {
    return document.createElement('div').appendChild(document.createTextNode(html)).parentNode.innerHTML;
}

此方法比基于'替换'的方法快两倍,请参阅http://jsperf.com/htmlencoderegex/35

来源:https://stackoverflow.com/a/17546215/698168

答案 4 :(得分:8)

AngularJS源代码的版本也在angular-sanitize.js内。

var SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
    // Match everything outside of normal chars and " (quote character)
    NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g;
/**
 * Escapes all potentially dangerous characters, so that the
 * resulting string can be safely inserted into attribute or
 * element text.
 * @param value
 * @returns {string} escaped text
 */
function encodeEntities(value) {
  return value.
    replace(/&/g, '&amp;').
    replace(SURROGATE_PAIR_REGEXP, function(value) {
      var hi = value.charCodeAt(0);
      var low = value.charCodeAt(1);
      return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';';
    }).
    replace(NON_ALPHANUMERIC_REGEXP, function(value) {
      return '&#' + value.charCodeAt(0) + ';';
    }).
    replace(/</g, '&lt;').
    replace(/>/g, '&gt;');
}

答案 5 :(得分:7)

一体化脚本:

// HTML entities Encode/Decode

function htmlspecialchars(str) {
    var map = {
        "&": "&amp;",
        "<": "&lt;",
        ">": "&gt;",
        "\"": "&quot;",
        "'": "&#39;" // ' -> &apos; for XML only
    };
    return str.replace(/[&<>"']/g, function(m) { return map[m]; });
}
function htmlspecialchars_decode(str) {
    var map = {
        "&amp;": "&",
        "&lt;": "<",
        "&gt;": ">",
        "&quot;": "\"",
        "&#39;": "'"
    };
    return str.replace(/(&amp;|&lt;|&gt;|&quot;|&#39;)/g, function(m) { return map[m]; });
}
function htmlentities(str) {
    var textarea = document.createElement("textarea");
    textarea.innerHTML = str;
    return textarea.innerHTML;
}
function htmlentities_decode(str) {
    var textarea = document.createElement("textarea");
    textarea.innerHTML = str;
    return textarea.value;
}

http://pastebin.com/JGCVs0Ts

答案 6 :(得分:2)

更快或更短的解决方案是:

escaped = new Option(html).innerHTML

这与JavaScript的一些怪异痕迹有关,其中Option元素保留了一个自动进行这种转义的构造函数。

贷记https://github.com/jasonmoo/t.js/blob/master/t.js

答案 7 :(得分:1)

function encode(r) {
  return r.replace(/[\x26\x0A\x3c\x3e\x22\x27]/g, function(r) {
	return "&#" + r.charCodeAt(0) + ";";
  });
}

test.value=encode('How to encode\nonly html tags &<>\'" nice & fast!');

/*
 \x26 is &ampersand (it has to be first),
 \x0A is newline,
 \x22 is ",
 \x27 is ',
 \x3c is <,
 \x3e is >
*/
<textarea id=test rows=11 cols=55>www.WHAK.com</textarea>

答案 8 :(得分:0)

Martijn的方法作为单一功能处理&#34; 标记(在javascript中使用):

function escapeHTML(html) {
    var fn=function(tag) {
        var charsToReplace = {
            '&': '&amp;',
            '<': '&lt;',
            '>': '&gt;',
            '"': '&#34;'
        };
        return charsToReplace[tag] || tag;
    }
    return html.replace(/[&<>"]/g, fn);
}

答案 9 :(得分:0)

我不太确定速度,但是如果您追求简单性,我建议您使用lodash /下划线escape函数。

答案 10 :(得分:0)

我将XMLSerializer添加到堆中。它提供了最快的结果,而无需使用任何对象缓存(不在序列化器上,也没有在Text节点上)。

function serializeTextNode(text) {
  return new XMLSerializer().serializeToString(document.createTextNode(text));
}

额外的好处是它支持序列化的属性与文本节点不同:

function serializeAttributeValue(value) {
  const attr = document.createAttribute('a');
  attr.value = value;
  return new XMLSerializer().serializeToString(attr);
}

通过查看text nodesattribute values的规范,您可以查看其实际替换的内容。完整的文档具有更多的节点类型,但是概念是相同的。

关于性能,如果不缓存,它是最快的。如果确实允许缓存,则在带有子Text节点的HTMLElement上调用innerHTML最快。正则表达式将是最慢的(如其他评论所证明)。当然,XMLSerializer在其他浏览器上可能会更快,但是在我的(有限的)测试中,innerHTML是最快的。


最快的单行:

new XMLSerializer().serializeToString(document.createTextNode(text));

最快的缓存:

const cachedElementParent = document.createElement('div');
const cachedChildTextNode = document.createTextNode('');
cachedElementParent.appendChild(cachedChildTextNode);

function serializeTextNode(text) {
  cachedChildTextNode.nodeValue = text;
  return cachedElementParent.innerHTML;
}

https://jsperf.com/htmlentityencode/1

答案 11 :(得分:-2)

节目有点晚,但使用encodeURIComponent()decodeURIComponent()有什么问题?