如何替换字符串中的所有特殊字符

Posted

技术标签:

【中文标题】如何替换字符串中的所有特殊字符【英文标题】:How to replace all special characters in string 【发布时间】:2019-08-31 14:34:42 【问题描述】:

我有一个包含以下列的表格:

dbo.SomeInfo
  - Id
  - Name
  - InfoCode

现在我需要将上表的 InfoCode 更新为

Update  dbo.SomeInfo
Set InfoCode= REPLACE(Replace(RTRIM(LOWER(Name)),' ','-'),':','')

这会将所有空格替换为 - 并将名称小写

当我检查信息代码时,我看到有一些特殊字符的名称,例如

Cathe Friedrich''s Low Impact
coffeyfit-cardio-box-&-burn
Jillian Michaels: Cardio

然后我手动为此编写更新 sql

 Update dbo.SomeInfo
 SET InfoCode= 'cathe-friedrichs-low-impact'
 where Name ='Cathe Friedrich''s Low Impact '

现在,这个解决方案对我来说是不现实的。我检查了以下与 Regex 相关的链接及其周围的其他链接。

UPDATE and REPLACE part of a string https://www.codeproject.com/Questions/456246/replace-special-characters-in-sql

但没有一个达到要求。

我需要的是如果有任何其他字符 [a-z0-9] 替换它 - & 也不应该是连续的 - 在 InfoCode 中

上面的Update sql已经将InfoCode的一些值设置为the-dancer's-workout®----starter-package

一些名称的价值为

Sleek Technique™
The Dancer's-workout®

如何编写可以处理所有此类特殊字符的更新 sql?

【问题讨论】:

【参考方案1】:

使用NGrams8K,您可以将字符串拆分为字符,然后只保留某些字符,而不是替换每个不可接受的字符:

SELECT (SELECT '' + CASE WHEN N.token COLLATE Latin1_General_BIN LIKE '[A-z0-9]'THEN token ELSE '-' END
        FROM dbo.NGrams8k(V.S,1) N
        ORDER BY position
        FOR XML PATH(''))
FROM (VALUES('Sleek Technique™'),('The Dancer''s-workout®'))V(S);

我在这里使用COLLATE,因为在我的实例中我的默认排序规则'™' 被忽略,因此我使用二进制排序规则。您可能希望使用COLLATE 将字符串切换回子查询之外的原始排序规则。

【讨论】:

【参考方案2】:

这种方法是完全可内联的:

首先我们需要一个带有一些测试数据的模型表

DECLARe @SomeInfo TABLE (Id INT IDENTITY, InfoCode VARCHAR(100));
INSERT INTO @SomeInfo (InfoCode) VALUES
 ('Cathe Friedrich''s Low Impact')
,('coffeyfit-cardio-box-&-burn')
,('Jillian Michaels: Cardio')
,('Sleek Technique™')
,('The Dancer''s-workout®');

--这是查询

WITH cte AS
(
    SELECT 1 AS position
          ,si.Id
          ,LOWER(si.InfoCode) AS SourceText
          ,SUBSTRING(LOWER(si.InfoCode),1,1) AS OneChar
    FROM @SomeInfo si

    UNION ALL

    SELECT cte.position +1
          ,cte.Id
          ,cte.SourceText
          ,SUBSTRING(LOWER(cte.SourceText),cte.position+1,1) AS OneChar
    FROM cte
    WHERE position < DATALENGTH(SourceText)
)
,Cleaned AS
(
    SELECT cte.Id
          ,(
            SELECT CASE WHEN ASCII(cte2.OneChar) BETWEEN 65 AND 90 --A-Z
                          OR ASCII(cte2.OneChar) BETWEEN 97 AND 122--a-z
                          OR ASCII(cte2.OneChar) BETWEEN 48 AND 57 --0-9
                          --You can easily add more ranges
                        THEN cte2.OneChar ELSE '-' 
                          --You can easily nest another CASE to deal with special characters like the single quote in your examples... 
                   END 
            FROM cte AS cte2
            WHERE cte2.Id=cte.Id
            ORDER BY cte2.position
            FOR XML PATH('')
           ) AS normalised
    FROM cte
    GROUP BY cte.Id
)
,NoDoubleHyphens AS
(
    SELECT REPLACE(REPLACE(REPLACE(normalised,'-','<>'),'><',''),'<>','-') AS normalised2
    FROM Cleaned
)
SELECT CASE WHEN RIGHT(normalised2,1)='-' THEN SUBSTRING(normalised2,1,LEN(normalised2)-1) ELSE normalised2 END AS FinalResult 
FROM NoDoubleHyphens;

第一个 CTE 将递归地(嗯,而是迭代地)遍历字符串,逐个字符并返回一个非常精简的集合,每个字符一行。

然后第二个 CTE 将 GROUP Ids。这允许相关子查询,其中使用 ASCII 范围执行实际检查。 FOR XML PATH('') 用于重新连接字符串。对于 SQL-Server 2017+,我建议改用 STRING_AGG()

第三个 CTE 将使用一个众所周知的技巧来消除一个角色的多次出现。取任何两个永远不会出现在你的字符串中的字符,我使用&lt;&gt;。像a--b---c 这样的字符串将返回为a&lt;&gt;&lt;&gt;b&lt;&gt;&lt;&gt;&lt;&gt;c。在将&gt;&lt; 替换为空之后,我们得到a&lt;&gt;b&lt;&gt;c。嗯,就是这样……

最后的SELECT 将去掉一个尾随连字符。如果需要,您可以添加类似的逻辑来摆脱前导连字符。在 v2017+ 中,TRIM('-') 让这一切变得更容易......

结果

cathe-friedrich-s-low-impact
coffeyfit-cardio-box-burn
jillian-michaels-cardio
sleek-technique
the-dancer-s-workout

【讨论】:

【参考方案3】:

您可以为类似的东西创建一个用户定义的函数。

然后在更新中使用UDF。

CREATE FUNCTION [dbo].LowerDashString (@str varchar(255))
RETURNS varchar(255)
AS
BEGIN
    DECLARE @result varchar(255);
    DECLARE @chr varchar(1);
    DECLARE @pos int;
    SET @result = '';
    SET @pos = 1;

    -- lowercase the input and remove the single-quotes
    SET @str = REPLACE(LOWER(@str),'''','');

    -- loop through the characters 
    -- while replacing anything that's not a letter to a dash
    WHILE @pos <= LEN(@str)
    BEGIN

      SET @chr = SUBSTRING(@str, @pos, 1)

      IF @chr LIKE '[a-z]' SET @result += @chr;
      ELSE SET @result += '-';

      SET @pos += 1;
    END;

    -- SET @result = TRIM('-' FROM @result); -- SqlServer 2017 and beyond

    -- multiple dashes to one dash
    WHILE @result LIKE '%--%' SET @result = REPLACE(@result,'--','-');

    RETURN @result;
END;
GO

使用函数的示例 sn-p:

-- using a table variable for demonstration purposes
declare @SomeInfo table (Id int primary key identity(1,1) not null, InfoCode varchar(100) not null);

-- sample data
insert into @SomeInfo (InfoCode) values
('Cathe Friedrich''s Low Impact'),
('coffeyfit-cardio-box-&-burn'),
('Jillian Michaels: Cardio'),
('Sleek Technique™'),
('The Dancer''s-workout®');

update @SomeInfo
set InfoCode = dbo.LowerDashString(InfoCode)
where (InfoCode LIKE '%[^A-Z-]%' OR InfoCode != LOWER(InfoCode));

select * 
from @SomeInfo;

结果:

Id  InfoCode
--  -----------------------------
1   cathe-friedrichs-low-impact
2   coffeyfit-cardio-box-burn
3   jillian-michaels-cardio
4   sleek-technique-
5   the-dancers-workout-

【讨论】:

以上是关于如何替换字符串中的所有特殊字符的主要内容,如果未能解决你的问题,请参考以下文章

如何替换字符串中的特殊字符?

如何通过替换 SQL 中的特殊字符来旋转给定的字符串?

如何删除/替换字符串列中的一系列特殊字符? [关闭]

替换字符串Python中的特殊字符

如何替换 hive 列中的特殊字符?

c#中 如何解决xml格式的字符串中特殊字符