SQL - 删除字符串中的所有HTML标记

时间:2016-08-10 08:44:57

标签: html sql sql-server xml tsql

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

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

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

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

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

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

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

9 个答案:

答案 0 :(得分:8)

如果HTML格式正确,那么就不需要使用replace来解析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&amp; 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;

答案 1 :(得分:8)

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

答案 2 :(得分:3)

首先创建一个用户定义的函数,将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/

答案 3 :(得分:1)

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

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

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

- 此功能将执行&#34;技巧&#34;

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

    RETURN @Content;
END
GO

- 我在您的问题和评论中找到的所有示例

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;

更新

如果您在模板表中添加替换值,您甚至可以使用不同的值作为替换,例如将<br>替换为实际的换行符...

答案 4 :(得分:0)

这只是一个例子。您可以在脚本中使用它来删除任何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

答案 5 :(得分:0)

这是最简单的方法。

public class DisplayItem : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private ImageSource icon;

    public ImageSource Icon
    {
        get
        {
            if (icon == null)
            {
                icon = ... // load here
            }
            return icon;
        }
        private set
        {
            icon = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Icon"));
        }
    }
}

答案 6 :(得分:0)

你提到XML并不总是有效,但是它总是包含&lt; p&gt;和&lt; / p&gt;标签

在这种情况下,以下方法可行:

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

用于找到&lt; p&gt;的所有位置。在HTML中,这里已经发布了一篇好文章:https://dba.stackexchange.com/questions/41961/how-to-find-all-positions-of-a-string-within-another-string

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

答案 7 :(得分:0)

SSMS 2017具有一个内置功能,可以查找和替换为正则表达式,但是我还没有弄清楚如何使用查询来实现。

此处的某些答案使用XML parsing,但是此方法仅适用于简单的html字符串并返回错误:XML parsing: line 5, character 6, end tag does not match start tag具有复杂的html字符串,就像我们从帮助台系统中获得的一样。

'<!--html--><div>
<div>A sentence.</div>
<br>
<div>Thank you!</div>
</div>'

我最终使用了递归CTE来完成工作。这是我的功能。

ALTER FUNCTION [dbo].[udf_removeHtmlTags] 
(
    @html NVARCHAR(MAX)
)  
    RETURNS NVARCHAR(MAX)
AS  
BEGIN 
    DECLARE @returnHtml NVARCHAR(MAX)

    ;with cte(id, html) as (
        select 1, STUFF(@html, CHARINDEX('<', @html), CHARINDEX('>', @html)-CHARINDEX('<', @html)+1, '')
        union all
        select id + 1, STUFF(html, CHARINDEX('<', html), CHARINDEX('>', html)-CHARINDEX('<', html)+1, '')
        from cte
        where html like '%<%>%'
    )
    select top 1 @returnHtml = html 
    from cte
    order by id desc

    RETURN @returnHtml
END

答案 8 :(得分:0)

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;

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

基于替换的解决方案无法处理具有属性的注释或元素,这使它们对我几乎没有用。

这是我的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