正则表达式在尊重CDATA时转义HTML&符号

时间:2009-01-20 19:52:15

标签: ruby-on-rails ruby regex

我编写了一个内容管理系统,该系统使用服务器端正则表达式在页面响应中将&符号转发到客户端浏览器之前。正则表达式注意到已经被转义或是HTML实体的一部分的&符号。例如,以下内容:

a & b, c & d, © 2009

改为:

a & b, c & d, © 2009

(只修改了第一个&。)这是正则表达式,它是从Rails助手中获取和修改的:

html.gsub(/&(?!([a-zA-Z][a-zA-Z0-9]*|(#\d+));)/) { |special| ERB::Util::HTML_ESCAPE[special] }

虽然这很有效但确实有问题。正则表达式不知道可能围绕未转义的&符号的任何<![CDATA[]]>。这对于嵌入式JavaScript保持不变是必要的。例如,这个:

<script type="text/javascript">
  // <![CDATA[
  if (a && b) doSomething();
  // ]]>
</script>
遗憾的是,

被渲染为:

<script type="text/javascript">
  // <![CDATA[
  if (a &amp;&amp; b) doSomething();
  // ]]>
</script>

当然JavaScript引擎无法理解。

我的问题是:有没有办法修改正则表达式,使其完全像现在这样做,除了它保持CDATA部分内的文本不变?

由于正则表达式开头并不那么简单,这个问题可能更容易回答:是否可以编写一个正则表达式,将所有字母更改为句点,除了<之间的字母'和'>'?例如,可以将"some <words> are < safe! >"更改为".... <words> ... < safe! >"

5 个答案:

答案 0 :(得分:7)

你问了! :d

/&(?!(?:[a-zA-Z][a-zA-Z0-9]*|#\d+);)
 (?!(?>(?:(?!<!\[CDATA\[|\]\]>).)*)\]\]>)/xm

第一行是你原来的正则表达式。如果前面有一个CDATA结束序列(]]>),则前瞻匹配,除非此处和那之间有一个开放序列(<!CDATA[)。假设文档格式最小,那应该意味着当前位置在CDATA部分内。

哎呀,我有那个向后:通过使用正向前瞻,我只在CDATA部分匹配“裸”&符号。我将其改为负向前瞻,所以现在它正常工作。

顺便说一句,这个正则表达式在Ruby模式下在RegexBuddy中运行,但不在the rubular site。我怀疑Rubular使用旧版本的Ruby,并且支持不太强大的正则表达式;任何人都可以确认吗? (你可能已经猜到了,我不是Ruby程序员。)

编辑:Rubular的问题在于我使用's'作为修饰符(表示点匹配 - 所有内容),但Ruby使用'm'表示。

答案 1 :(得分:3)

不要为此使用正则表达式。这是一个可怕而可怕的想法。相反,只需HTML编码您输出的任何可能包含字符的内容。像这样:

require 'cgi'
print CGI.escape("All of this is HTML encoded!")

答案 2 :(得分:1)

那很有效!在Rubular我必须将选项从/xs更改为/m(并且我删除了正如您在上面显示的正则表达式的两个部分分隔的空格。)

您可以看到此正则表达式以及http://www.rubular.com/regexes/5855处的示例字符串。

如果Rubular永久链接不是永久性的,那么这就是我为正则表达式输入的内容:

/&(?!(?:[a-zA-Z][a-zA-Z0-9]*|#\d+);)(?!(?>(?:(?!<!\[CDATA\[|\]\]>).)*)\]\]>)/m

这是测试字符串:

<p>a & b</p>
<p>c &amp; d</p>
<script type="text/javascript">
  // <![CDATA[
  if (a && b) doSomething('a & b &amp; c');
  // ]]>
</script>
<p>a & b</p>
<p>c &amp; d</p>

只有两个&符号匹配 - 顶部为a & b,底部为a & b。 “&”符号已经&amp;转义,并且<![CDATA[]]>之间的所有&符号(转义与否)都将保持不变。

所以,我的最终代码现在是这样的:

html.gsub(/&(?!(?:[a-zA-Z][a-zA-Z0-9]*|#\d+);)(?!(?>(?:(?!<!\[CDATA\[|\]\]>).)*)\]\]>)/m, '&amp;')

非常感谢艾伦。这正是我所需要的。

答案 3 :(得分:0)

我在这里做了类似的事情:
Best way to encode text data for XML

幸运的是,就我而言,CDATA不是问题。

问题在于你必须要小心,表达不是贪婪,否则你最终会得到这样的东西:

.... <words> are < safe! >

答案 4 :(得分:0)

我严重怀疑你想要完成的是你可以单独使用正则表达式做的事情。 Regexp在正确处理嵌套方面非常糟糕。

使用XML解析器而不是转发CDATA内容可能会更好。