检查一个字符串是否为HTML格式

147

我有一个字符串,想要检查它是否为HTML格式。我使用正则表达式进行匹配,但是结果不正确。

我验证了我的正则表达式,在这里是有效的。

var htmlRegex = new RegExp("<([A-Za-z][A-Za-z0-9]*)\b[^>]*>(.*?)</\1>");
return htmlRegex.test(testString);

这是 jsfiddle 的链接,但正则表达式在那里没有运行。http://jsfiddle.net/wFWtc/

在我的电脑上,代码能够正常运行,但结果为 false 而不是 true。 我错过了什么?


7
使用 HTML 解析器解析 HTML。如果您还没有阅读过,请阅读此链接(https://dev59.com/X3I-5IYBdhLWcg3wq6do#1732454)。 - Frédéric Hamidi
3
问题不断出现,应该有一个堆栈机器人,可以自动为每个包含 HTML 和正则表达式的问题设置评论。 - Bartlomiej Lewandowski
3
这有点取决于你想要检查的复杂程度。你可以检查字符串中是否包含至少一个<和至少一个>,并将其称为HTML,或者你可以检查它是否具有正确的HTML语法,并严格有效,也可以在两者之间任意选择。对于最简单的情况,不需要使用HTML解析器。 - JJJ
3
为什么要检查一个字符串是否为HTML? - nhahtdh
2
@user1240679:有效的标记格式?什么样的有效性?严格来说,您需要DTD来描述它。在宽松的意义上,您可能希望检查标记是否正确匹配。以上两种情况都不适合使用正则表达式。 - nhahtdh
显示剩余4条评论
20个回答

415

检查字符串是否为HTML的更好的正则表达式是:

/^/

例如:

/^/.test('') // true
/^/.test('foo bar baz') //true
/^/.test('<p>fizz buzz</p>') //true

事实上,它非常好,对于传递给它的每个字符串都会返回true,这是因为每个字符串都是HTML。即使格式不正确或无效,它仍然是HTML。

如果你想要的是判断是否存在HTML元素,而不仅仅是任何文本内容,你可以使用类似以下的代码:

/<\/?[a-z][\s\S]*>/i.test()

它无法帮助您以任何方式解析HTML,但肯定会将字符串标记为包含HTML元素。


110
说实话,我很惊讶我的嘲讽没有收到更多的踩。 - zzzzBov
11
@clenemt,你认为a < b && a > c是HTML吗? - zzzzBov
2
@oriadam,这个上下文是用于检测元素的情况。如果您使用 a < b && a > c,浏览器将适当地将 >< 字符转换为 &gt;&lt; 实体。相反,如果您使用 a<b && a>c,浏览器将解释标记为 a<b && a>c</b>,因为缺少空格意味着 <b 打开了一个 <b> 元素。这里有一个我所说的快速演示 - zzzzBov
24
这可能是我在这个网站上看到的得票最高的“喷子回答”了。 ;) - aandis
5
我向你downvote了,遗憾的是,我喜欢用尖酸刻薄的态度来表达。这里有两个不同的问题,看起来好像都被回答了,但实际上它们是完全不同的。第一个问题是:“这段文本中是否包含任何HTML标记?”→ “它是否包含<[a-zA-Z]最终跟着一个>”,就是这样。第二个问题是:这个字符串是一个HTML文档吗?它是否以 <!DOCTYPE html> 开头?因为: <!DOCTYPE html><title>Yes, really.</title><p>This is everything you need for a fully valid, complete HTML document. - amcgregor
显示剩余14条评论

107

方法 #1。这是一个简单的函数,用于测试字符串是否包含HTML数据:

function isHTML(str) {
  var a = document.createElement('div');
  a.innerHTML = str;

  for (var c = a.childNodes, i = c.length; i--; ) {
    if (c[i].nodeType == 1) return true; 
  }

  return false;
}

这个想法是允许浏览器DOM解析器决定提供的字符串是否类似于HTML。正如您所看到的,它只是检查ELEMENT_NODE(1号nodeType)。

我进行了几次测试,看起来它有效:

isHTML('<a>this is a string</a>') // true
isHTML('this is a string')        // false
isHTML('this is a <b>string</b>') // true

这个解决方案能够正确地检测HTML字符串,但是它有一个副作用,即在解析innerHTML时,img/vide/etc.标签将开始下载资源。

方法2。另一种方法使用DOMParser,并且没有加载资源的副作用:

function isHTML(str) {
  var doc = new DOMParser().parseFromString(str, "text/html");
  return Array.from(doc.body.childNodes).some(node => node.nodeType === 1);
}

注意:
1. Array.from 是 ES2015 的方法,可以用 [].slice.call(doc.body.childNodes) 来替换。
2. some 调用中的箭头函数可以用普通的匿名函数来替换。


4
这是一个很棒的想法。然而,该函数无法检测闭合标签(例如 `isHTML("</a>") --> false)。 - Lewis
12
很棒的解决方案!唯一的负面影响是,如果您的HTML包含任何静态资源,例如图像src属性.. innerHTML将强制浏览器开始获取这些资源。 :( - Jose Browne
2
@kuus 是的,即使不是追加也可以使用 DOMParser 解决方案。 - dfsq
1
好主意,但是采纳的答案对性能来说不是更好吗?特别是当你有大量字符串(双关语)或者需要经常使用这个测试时。 - DerpyNerd
1
更不用说使用这种方法时,XSS似乎不会执行,因为它没有添加到DOM中! - David Kroukamp
显示剩余5条评论

23

这是我偶尔使用的一个简短的代码:

var isHTML = RegExp.prototype.test.bind(/(<([^>]+)>)/i);

基本上,对于包含一个<加上SOMETHING再加上>的字符串,它将返回true

这里的SOMETHING指的是除了空字符串之外的任何东西。

虽然不是非常好,但它只有一行代码。

用法

isHTML('Testing');               // false
isHTML('<p>Testing</p>');        // true
isHTML('<img src="hello.jpg">'); // true
isHTML('My < weird > string');   // true (caution!!!)
isHTML('<>');                    // false
isHTML('< >');                   // true (caution!!!)
isHTML('2 < 5 && 5 > 3');        // true (caution!!!)

正如你所看到的,它还远非完美,但在某些情况下可能对你有用。


1
这是一个可爱的一行代码...从未考虑过.test.bind - John Doherty
有人能解释一下这行代码吗? - mkvakin

17

稍微进行一些验证:

/<(?=.*? .*?\/ ?>|br|hr|input|!--|wbr)[a-z]+.*?>|<([a-z]+).*?<\/\1>/i.test(htmlStringHere) 

这个搜索会查找空标签(一些预定义的)和以/结尾的XHTML空标签,并且由于这些空标签,它可以作为HTML进行验证。或者它会捕获标签名称并尝试在字符串中寻找它的闭合标签,以作为HTML进行验证。

演示说明:http://regex101.com/r/cX0eP2

更新:

完整验证结果为:

/<(br|basefont|hr|input|source|frame|param|area|meta|!--|col|link|option|base|img|wbr|!DOCTYPE).*?>|<(a|abbr|acronym|address|applet|article|aside|audio|b|bdi|bdo|big|blockquote|body|button|canvas|caption|center|cite|code|colgroup|command|datalist|dd|del|details|dfn|dialog|dir|div|dl|dt|em|embed|fieldset|figcaption|figure|font|footer|form|frameset|head|header|hgroup|h1|h2|h3|h4|h5|h6|html|i|iframe|ins|kbd|keygen|label|legend|li|map|mark|menu|meter|nav|noframes|noscript|object|ol|optgroup|output|p|pre|progress|q|rp|rt|ruby|s|samp|script|section|select|small|span|strike|strong|style|sub|summary|sup|table|tbody|td|textarea|tfoot|th|thead|time|title|tr|track|tt|u|ul|var|video).*?<\/\2>/i.test(htmlStringHere) 

这个代码进行了正确的验证,因为它包含了所有HTML标签,空标签先出现,其余标签需要闭合。

这里有一个说明演示:http://regex101.com/r/pE1mT5


1
只是一个提醒,底部的正则表达式确实可以工作,但它无法检测未闭合的HTML标签,例如"'<strong>hello world"。尽管这是损坏的HTML,因此应该被视为字符串,但出于实际目的,您的应用程序可能也希望检测到这些。 - user967451
1
HTML是为了适应用户代理而设计的,"无效"的标签并不是无效的,它们只是未知的,并且被允许存在。同样,"无效"的属性也不是无效的......当我们开始涉及到"Web组件"和JSX这样的技术时,这一点尤为重要,因为它们将HTML和更丰富的组件描述混合在一起,通常生成阴影DOM。复制这个文件,执行document.querySelector('strange')——它会正常工作。 - amcgregor
总结一下:由于规范的编写方式,尝试“验证”HTML标记实际上是徒劳无功的。在给出的样本HTML文档中,有一个“无效”的元素,那里是一个100%完整的HTML文档 - 这是自1997年以来的情况,作为另一个例子。 - amcgregor

13

zzzzBov的回答很好,但它没有考虑到杂散的闭合标签,例如:

/<[a-z][\s\S]*>/i.test('foo </b> bar'); // false

一个同时捕获闭合标签的版本可能是这样的:

/<[a-z/][\s\S]*>/i.test('foo </b> bar'); // true

建议您提出修改意见,而不是将此作为评论发布。 - Zlatin Zlatev
1
我认为你指的是<[a-z/][\s\S]*>-请注意第一组中的斜杠。 - Ryan Guill

8
这里的所有答案都过于宽泛,只是寻找<后面紧跟着>。没有完美的方法来检测字符串是否为HTML,但我们可以做得更好。下面我们将寻找结束标签,这样会更加严格和准确:
import re
re_is_html = re.compile(r"(?:</[^<]+>)|(?:<[^<]+/>)")

以下是它的运行效果:

# Correctly identified as not HTML:
print re_is_html.search("Hello, World")
print re_is_html.search("This is less than <, this is greater than >.")
print re_is_html.search(" a < 3 && b > 3")
print re_is_html.search("<<Important Text>>")
print re_is_html.search("<a>")

# Correctly identified as HTML
print re_is_html.search("<a>Foo</a>")
print re_is_html.search("<input type='submit' value='Ok' />")
print re_is_html.search("<br/>")

# We don't handle, but could with more tweaking:
print re_is_html.search("<br>")
print re_is_html.search("Foo &amp; bar")
print re_is_html.search("<input type='submit' value='Ok'>")

4

如果您从字符串文字创建正则表达式,则需要转义任何反斜杠:

var htmlRegex = new RegExp("<([A-Za-z][A-Za-z0-9]*)\\b[^>]*>(.*?)</\\1>");
// extra backslash added here ---------------------^ and here -----^

如果您使用正则表达式字面量,则不需要这样做,但是您需要转义正斜杠:
var htmlRegex = /<([A-Za-z][A-Za-z0-9]*)\b[^>]*>(.*?)<\/\1>/;
// forward slash escaped here ------------------------^

你的 jsfiddle 没有工作是因为你在另一个 onload 处理程序中分配了一个 onload 处理程序 - 在左侧的 Frameworks & Extensions 面板中设置的默认值是将 JS 包装在 onload 中。将其更改为 nowrap 选项,并修复字符串文字转义,它就可以“工作”(在评论中指出的所有约束条件内):http://jsfiddle.net/wFWtc/4/ 据我所知,JavaScript 正则表达式没有反向引用。因此,你的表达式的这一部分:
</\1>

这在JS中不起作用(但在其他一些语言中可能会起作用)。


这里是:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/RegExp - nhahtdh
好的,这将测试其中一个标签是否正常,但对其余部分没有任何影响。不确定OP想要什么样的“有效性”。 - nhahtdh
1
关于 <br> <hr> <input...> @user1240679,您有什么想问的吗? - CSᵠ

4

使用jQuery:

function isHTML(str) {
  return /^<.*?>$/.test(str) && !!$(str)[0];
}

2
isHTML("<foo>"); // 返回 true isHTML("div"); // 如果页面上有 div,则返回 true - ACK_stoverflow
@yekta - 你在说什么?这个应该检查字符串是否是HTML。据我所知,电子邮件不是HTML标签... isHTML('foo@bar.com')-> false // 正确的 - gtournie
1
一个字符串可以是任何东西,如果你知道它是HTML标签,那么为什么要首先检查它是否是HTML呢?我不太明白你的观点。@不是选择器的有效语法。因此,当你将它传递给jQuery选择器时,它会抛出异常(即从!!$(str)[0]中的$("you@example.com"))。我特别指的是!!$(str)[0]部分。你刚刚编辑了你的答案,但现在你正在检查HTML是否存在,而jQuery还没有做任何事情。 - yekta
我认为作者想要检查的不仅仅是一个字符串,这才是关键。他想要的是一个能够检查字符串是否为有效HTML标签的函数,而不仅仅是HTML(否则这就有点愚蠢了)。 在阅读了@ACK_stoverflow的评论后,我更新了我的答案,但我相信一个简单的正则表达式就可以解决问题。 - gtournie

3
虽然这是一个旧的帖子,但我想分享一下我针对自己需求编写的解决方案:
function isHtml(input) {
    return /<[a-z]+\d?(\s+[\w-]+=("[^"]*"|'[^']*'))*\s*\/?>|&#?\w+;/i.test(input);
}

我希望这篇文章可以涵盖我在这个主题中发现的大部分棘手情况。已经使用 document.body.innerTextdocument.body.innerHTML 在此页面上进行测试。

希望对某人有所帮助。:)


似乎过于具体或者试图更加明确地“验证”HTML。 <input disabled>可能会有问题,因为没有带引号的属性是完全可以接受的,使得该表达式不正确。 HTML是一个宽容的,而非严格的过程,我的答案提供了一个更加功能完整(如果急于求成)的匹配模式。测试失败也要看,而不仅仅是成功。 - amcgregor
对我来说完美地工作了,谢谢! - Spencer Bigum

3

/<\/?[^>]*>/.test(str) 只检测字符串是否包含HTML标签,可能是XML。


27小于42,而96大于42。这不是HTML。 - amcgregor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接