【问题标题】: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 相关的链接及其周围的其他链接。

但没有一个达到要求。

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

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

一些名称的价值为

Sleek Technique™
The Dancer's-workout®

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

【问题讨论】:

    标签: sql sql-server string tsql


    【解决方案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-
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2023-03-12
          • 1970-01-01
          • 2011-05-16
          • 2012-11-06
          • 1970-01-01
          • 1970-01-01
          • 2014-12-07
          相关资源
          最近更新 更多