【问题标题】:SQL - Remove all HTML tags in a stringSQL - 删除字符串中的所有 HTML 标记
【发布时间】:2016-12-16 12:57:44
【问题描述】:

在我的数据集中,我有一个存储用 HTML 标记的文本的字段。一般格式如下:

<html><head></head><body><p>My text.</p></body></html>

我可以尝试通过执行以下操作来解决问题:

REPLACE(REPLACE(Table.HtmlData, '<html><head></head><body><p>', ''), '</p></body></html>')

但是,这不是一个严格的规则,因为一些条目违反了 W3C 标准,并且不包含 &lt;head&gt; 等标签。更糟糕的是,可能缺少结束标记。所以我需要为每个可能存在的开始和结束标签包含REPLACE 函数。

REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(
    Table.HtmlData,
    '<html>', ''),
    '</html>', ''),
    '<head>', ''),
    '</head>', ''),
    '<body>', ''),
    '</body>', ''),
    '<p>', ''),
    '</p>', '')

我想知道是否有比使用多个嵌套的REPLACE 函数更好的方法来实现这一点。不幸的是,我在这个环境中唯一可用的语言是 SQL 和 Visual Basic(不是 .NET)。

【问题讨论】:

  • 正如您亲眼所见,对于您所面临的问题,核心 SQL Server 字符串函数充其量是笨拙的,最坏的情况是丑陋的。 IMO 使用 Java 或 .NET 之类的东西可以更轻松地执行此操作,您可以在其中利用 XML 解析器的强大功能。是否有机会使用不同的工具来解决这个问题?
  • 嵌套REPLACE 与循环(性能方面)非常相似。我将创建一个二维数组,其中包含一对 HTML 标签,并遍历试图替换标签的字符串。优点是您可以在不更改代码的情况下修改(丰富)数组。
  • 我完全同意蒂姆的观点。此外,如果您的 html 中有 ramenk、javascript 或样式块,会发生什么?您会将它们视为内容的一部分,我怀疑这就是您想要的......
  • 我很想实现它,但这是进入 SSRS 报告,所以我最好的选择是 Visual Basic...颤抖。至少可以保证不会有 JavaScript 或 CSS。

标签: html sql sql-server xml tsql


【解决方案1】:

首先创建一个用户定义的函数,像这样剥离 HTML:

CREATE FUNCTION [dbo].[udf_StripHTML] (@HTMLText VARCHAR(MAX))
RETURNS VARCHAR(MAX)
AS
     BEGIN
         DECLARE @Start INT;
         DECLARE @End INT;
         DECLARE @Length INT;
         SET @Start = CHARINDEX('<', @HTMLText);
         SET @End = CHARINDEX('>', @HTMLText, CHARINDEX('<', @HTMLText));
         SET @Length = (@End - @Start) + 1;
         WHILE @Start > 0
               AND @End > 0
               AND @Length > 0
             BEGIN
                 SET @HTMLText = STUFF(@HTMLText, @Start, @Length, '');
                 SET @Start = CHARINDEX('<', @HTMLText);
                 SET @End = CHARINDEX('>', @HTMLText, CHARINDEX('<', @HTMLText));
                 SET @Length = (@End - @Start) + 1;
             END;
         RETURN LTRIM(RTRIM(@HTMLText));
     END;
GO

当您尝试选择它时:

SELECT dbo.udf_StripHTML([column]) FROM SOMETABLE

这应该会导致您避免使用多个嵌套的替换语句。

信用和更多信息:http://blog.sqlauthority.com/2007/06/16/sql-server-udf-user-defined-function-to-strip-html-parse-html-no-regular-expression/

【讨论】:

  • 这可以更容易地完成,无需 WHILE 循环,只需使用模板表替换(顺便说一句更容易维护)
【解决方案2】:

这只是一个例子。您可以在脚本中使用它来 rmeove 任何 html 标签:

 DECLARE @VALUE VARCHAR(MAX),@start INT,@end int,@remove varchar(max)
SET @VALUE='<html itemscope itemtype="http://schema.org/QAPage">
<head>

<title>sql - Converting INT to DATE then using GETDATE on conversion? - Stack Overflow</title>
<html>
</html>
'

set @start=charindex('<',@value)
while @start>0
begin
set @end=charindex('>',@VALUE)

set @remove=substring(@VALUE,@start,@end)
set @value=replace(@value,@remove,'')
set @start=charindex('<',@value)
end
print @value

【讨论】:

    【解决方案3】:

    这是最简单的方法。

    DECLARE @str VARCHAR(299)
    
    SELECT @str = '<html><head></head><body><p>My text.</p></body></html>'
    
    SELECT cast(@str AS XML).query('.').value('.', 'varchar(200)')
    

    【讨论】:

    • 绝对是最简单的,但是我如何让它与未配对的标签一起工作?
    【解决方案4】:

    如果 HTML 格式正确,则无需使用替换来解析 XML。
    只需将其转换或转换为 XML 类型并获取值。

    这是一个从所有标签输出文本的示例:

    declare @htmlData nvarchar(100) = '<html>
    <head>
    </head>
    <body>
       <p>My text.</p>
       <p>My other text.</p>
    </body>
    </html>';
    
    select convert(XML,@htmlData,1).value('.', 'nvarchar(max)');
    
    select cast(@htmlData as XML).value('.', 'nvarchar(max)');
    

    请注意,强制转换和转换之间的空白输出存在差异。

    要仅从特定节点获取内容,使用XQuery 语法。 (XQuery 基于 XPath 语法)

    例如:

    select cast(@htmlData as XML).value('(//body/p/node())[1]', 'nvarchar(max)');
    
    select convert(XML,@htmlData,1).value('(//body/p/node())[1]', 'nvarchar(max)');
    

    结果:My text.

    当然,这仍然假定 XML 有效。
    例如,如果缺少结束标记,则会引发 XML parsing 错误。

    如果 HTML 的 XML 格式不正确,则可以使用 PATINDEX & SUBSTRING 来获取第一个 p 标记。然后将其转换为 XML 类型以获取值。

    select cast(SUBSTRING(@htmlData,patindex('%<p>%',@htmlData),patindex('%</p>%',@htmlData) - patindex('%<p>%',@htmlData)+4) as xml).value('.','nvarchar(max)');
    

    或者通过一种时髦的递归方式:

    declare @xmlData nvarchar(100);
    WITH Lines(n, x, y) AS (
      SELECT 1, 1, CHARINDEX(char(13), @htmlData)
      UNION ALL
      SELECT n+1, y+1, CHARINDEX(char(13), @htmlData, y+1) FROM Lines
      WHERE y > 0
    )
    SELECT @xmlData = concat(@xmlData,SUBSTRING(@htmlData,x,IIF(y>0,y-x,8)))
    FROM Lines
    where PATINDEX('%<p>%</p>%', SUBSTRING(@htmlData,x,IIF(y>0,y-x,10))) > 0
    order by n;
    
    select 
    @xmlData as xmlData, 
    convert(XML,@xmlData,1).value('(/p/node())[1]', 'nvarchar(max)') as FirstP;
    

    【讨论】:

    • 这太好了,我不知道你可以打电话给.value 并使用XPATH。但不幸的是,我确实需要它来处理不匹配的标签。
    • 是的,如果 tsql 有一个标准方法可以将糟糕的 HTML 转换为 XML 类型而不会失败,那就太棒了。如果您是生成 HTML 的人,问题就不大了,因为这样您就可以确保 HTML 从一开始就格式正确。
    • 不幸的是,我无法控制从我们的 ERP 系统生成的 HTML 的状况。用户在多行文本字段中键入内容,它会自动格式化为基本 HTML。有时很难预测会发生什么。即使是有效的 HTML 也有自闭合标签,可以这样写:&lt;br&gt;,这将是无效的 XML。
    • 添加了一个替代方案,它只会在演员表之前提取第一个 p 标签。
    【解决方案5】:

    您提到 XML 并不总是有效,但它总是包含

    标记吗?

    在这种情况下,以下将起作用:

    SUBSTRING(Table.HtmlData, 
        CHARINDEX('<p>', Table.HtmlData) + 1, 
        CHARINDEX('</p>', Table.HtmlData) - CHARINDEX('<p>', Table.HtmlData) + 1)
    

    为了在 HTML 中查找

    的所有位置,这里已经有一篇很好的帖子:https://dba.stackexchange.com/questions/41961/how-to-find-all-positions-of-a-string-within-another-string

    或者,我建议使用 Visual Basic,正如您所提到的,这也是一种选择。

    【讨论】:

    • 是的,据我在各种表格中看到的,它似乎总是包含&lt;p&gt; 标签。但是,如果我遇到这种情况,您所写的内容会受到限制:&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;p&gt;My text.&lt;/p&gt;&lt;p&gt;Another paragraph&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
    【解决方案6】:
    DECLARE @x XML = '<html><head></head><body><p>My text.</p></body></html>'
    
    SELECT t.c.value('.', 'NVARCHAR(MAX)')
    FROM @x.nodes('*') t(c)
    

    更新 - 对于带有未闭合标签的字符串:

    DECLARE @x NVARCHAR(MAX) = '<html><head></head><body><p>My text.<br>More text.</p></body></html>'
    
    SELECT x.value('.', 'NVARCHAR(MAX)')
    FROM (
        SELECT x = CAST(REPLACE(REPLACE(@x, '>', '/>'), '</', '<') AS XML)
    ) r
    

    【讨论】:

    • 感谢@Devart 这解决了问题,无需创建函数并处理未配对的标签。很好的解决方案。
    • 如果 html 包含 cmets 则不起作用
    • 还需要替换html cmets:REPLACE(REPLACE(REPLACE(@x, '>', '/>'), '', '' , '-->')
    【解决方案7】:

    另一种解决方案,只是为了演示在一个语句中替换一个表的多个值的技巧(易于维护!!!):

    --在此处添加任何替换模板:

    CREATE TABLE ReplaceTags (HTML VARCHAR(100));
    INSERT INTO ReplaceTags VALUES
     ('<html>'),('<head>'),('<body>'),('<p>'),('<br>')
    ,('</html>'),('</head>'),('</body>'),('</p>'),('</br>');
    GO
    

    --这个函数将执行“技巧”

    CREATE FUNCTION dbo.DoReplace(@Content VARCHAR(MAX))
    RETURNS VARCHAR(MAX)
    AS
    BEGIN
        SELECT @Content=REPLACE(@Content,HTML,'')
        FROM ReplaceTags;
    
        RETURN @Content;
    END
    GO
    

    --我在你的问题和 cmets 中找到的所有示例

    DECLARE @content TABLE(Content VARCHAR(MAX));
    INSERT INTO @content VALUES
     ('<html><head></head><body><p>My text.</p></body></html>')
    ,('<html><head></head><body><p>My text.<br>More text.</p></body></html>')
    ,('<html><head></head><body><p>My text.<br>More text.</p></body></html>')
    ,('<html><head></head><body><p>My text.</p></html>');
    

    --这是实际的查询

    SELECT dbo.DoReplace(Content) FROM @content;
    GO
    

    --清理

    DROP FUNCTION dbo.DoReplace;
    DROP TABLE ReplaceTags;
    

    更新

    如果您向模板表添加替换值,您甚至可以使用不同的值作为替换,例如用实际换行符替换 &lt;br&gt;...

    【讨论】:

      【解决方案8】:

      SQL Server 2017+

      如果你有一个字符串拆分器功能,你可以从几乎任何文本(格式是否正确)中去除 HTML 标记:

      select string_agg(c.String, null) within group (order by o.Ordinal)
      from dbo.SplitString(@Input, N'<') o
          cross apply dbo.SplitString(o.String, N'>') c
      where o.Ordinal = 1
          or c.Ordinal = 2;
      

      这将与您的拆分器功能一样高效。因此,它通常应该胜过任何基于循环的解决方案。

      基于替换的解决方案无法处理具有属性的 cmets 或元素,这使得它们对我来说实际上毫无用处。

      这是我的 split 和 strip 函数的版本:

      create or alter function dbo.SplitString (
            @String nvarchar(max)
          , @Delimiter nvarchar(4000)
      )
      returns table with schemabinding
      as
          return
          select [key] + 1 as Ordinal, value as String
          from openjson(replace(json_modify(N'[]', N'append $', @String), string_escape(@Delimiter, N'json'), N'","'))
      
      create or alter function dbo.StripHtml (
            @Input nvarchar(max)
      )
      returns nvarchar(max)
      as
      begin
          return (
              select string_agg(c.String, null) within group (order by o.Ordinal)
              from dbo.SplitString(@Input, N'<') o
                  cross apply dbo.SplitString(o.String, N'>') c
              where o.Ordinal = 1
                  or c.Ordinal = 2
          )
      end
      

      【讨论】:

        猜你喜欢
        • 2012-03-14
        • 2010-09-19
        • 2017-09-06
        • 2022-06-23
        相关资源
        最近更新 更多