搜索不可见的字符

时间:2016-12-24 12:05:00

标签: sql sql-server tsql

当我想获取非空或空字符串的记录时,我正在排除SQL Server输出中的一些奇怪现象:

SELECT myString 
FROM myTable 
WHERE myString IS NOT NULL OR myString != '' 

除了显然适合的记录(返回到SSMS网格的字符串值)之外,我还看到网格单元格为空白的记录。当我选择单元格并尝试复制时,我的剪贴板管理器(clipmate.com)会抱怨数据无效。

如果我将查询输出到文件而不是网格,然后通过十六进制字符模式进行检查,果然 - 有些字符我不希望(更不想要)在那里。< / p>

我的数据库归类值的整理显示为:SQL_Latin1_General_CP1_CI_AS

我如何消除任何/所有不可见的角色?

4 个答案:

答案 0 :(得分:0)

可以尝试这个,将删除任何不可打印的字符。

    CREATE FUNCTION [dbo].[RemoveNonPrintableChars]
    (
        @p_string  varchar(max)
    )
    RETURNS varchar(max)
    AS
    BEGIN
       declare @l_pos int = 1
       declare @l_str varchar(max) = ''
       while (@l_pos <= len(@p_string))
       begin

          if (ascii(substring(@p_string,@l_pos,1)) >=32)
          begin
             set @l_str=@l_str+substring(@p_string,@l_pos,1)
          end
          set @l_pos = @l_pos+1
       end
       return @l_str

    END

declare 
@l_str varchar(max) = 'andrew'--select char(7)
select dbo.[RemoveNonPrintableChars] (@l_str)
set @l_str = 'andrew'+char(7)-- add NP character
select dbo.[RemoveNonPrintableChars] (@l_str)

答案 1 :(得分:0)

如果您偶然无法使用UDF。

你可能会注意到我不是在这里剥离控制字符,我用空格替换它们,以免连接字符串。

Declare @YourTable table (SomeField varchar(50))
Insert Into @YourTable values
('Michael'+char(13)+char(10)+'LastName')

Select A.*
      ,B.Value
 From  @YourTable A
 Cross Apply (
              Select Value = replace(replace((
                 Select ''+C
                  From (
                         Select N,C=case when ASCII(Substring(A.SomeField,N ,1))>31 then Substring(A.SomeField,N ,1) else '{--space--}' end
                          From ( Select Top (Len(A.SomeField)) N=Row_Number() Over (Order By Number) From master..spt_values ) N
                       ) C Order by N
                  For XML Path('') ) ,'{--space--}',' '),'  ',' ')
             ) B

返回

SomeField   Value
Michael     Michael LastName
LastName    
  

修改

但是,如果您确实希望UDF考虑以下非线性方法

CREATE FUNCTION [dbo].[udf-Str-Strip-Control](@S varchar(max))
Returns varchar(max)
Begin
    ;with  cte1(N) As (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N)),
           cte2(C) As (Select Top (32) Char(Row_Number() over (Order By (Select NULL))-1) From cte1 a,cte1 b)
    Select @S = Replace(@S,C,' ')
     From  cte2

    Return LTrim(RTrim(Replace(Replace(@S,'   ',' '),'  ',' ')))
End
--Select [dbo].[udf-Str-Strip-Control]('Michael'+char(13)+char(10)+'LastName')  --Returns: Michael LastName

答案 2 :(得分:0)

我更喜欢John的答案,如果您想控制是否包含某些控制字符,可能会修改它们。这是我过去用来清理一些字符串的函数。

create function dbo.fnCleanVarchar (
  @StringParameter varchar(max)
, @CleanStyle tinyint = 1
  ) returns varchar(max) as 
begin;
  if @StringParameter is null 
    return null;
  if @CleanStyle > 3 set @CleanStyle = 1;
  declare @StringReturn varchar(max);
  declare @StringLength int;
  declare @CharacterCode int;
  declare @CharacterCodePosition int;
  set @StringReturn = '';
  set @StringLength = len(@StringParameter);
  set @CharacterCodePosition = 1;

  while @CharacterCodePosition <= @StringLength
    begin
        set @CharacterCode = ascii(substring(@stringParameter , @CharacterCodePosition , 1))
        -- Removes Unprintable Characters 0-8,12,14-31 
        -- If Style = 1, Remove Unprintable Characters except Tab (9), New Line (10), Carraige Return (13)
        -- If Style = 2, Remove Unprintable Characters except character 9 (Tab)
        -- If Style = 3, Remove Unprintable Characters and character 9 (Tab)
        set @StringReturn = @StringReturn + case
          when @CharacterCode >31 
            then char(@CharacterCode)
          when @Style = 3 
            then ''
          when @Style = 2 and @CharacterCode = 9 
            then char(9)
          when @Style = 1 and @CharacterCode in ( 9 , 10 , 13 )
            then char(@CharacterCode)
          else ''
          end;
        set @CharacterCodePosition = @CharacterCodePosition + 1
    end;
  if len(@StringReturn) = 0 
    return null;
  return @StringReturn
end;

答案 3 :(得分:0)

检查不可见字段与查找不可见字符直接相关,因此请考虑以下两个注意事项:

注意1:SQL Server will auto-trimming spaces条款中N' ' = N''为真,并且字符的任何连续字符串;
空字符是一个等于N''的字符。

注意2:有65536个Unicode字符,您可以使用如下查询查看它们:

WITH CTE(i, c) AS (
    SELECT 0, NCHAR(0) COLLATE SQL_Latin1_General_CP1_CI_AS --I add COLLATE to express your collation but I think it is optional
    UNION ALL
    SELECT i+1, NCHAR(i+1) COLLATE SQL_Latin1_General_CP1_CI_AS
    FROM CTE
    WHERE i < 65535
)
SELECT *
FROM CTE
OPTION ( MaxRecursion 0 );

其中一些不可见为空,如NCHAR(0), NCHAR(12288), ...
其中一些不可见不为空,如NCHAR(1), ...
其中一些可见,如NCHAR(502), ... !!!!。

  

因此,如果您的字段是nvarchar字符串,则过滤不可见字符会遇到很大问题,对于varchar字符串,您会遇到问题但更少不止于此。

附注:您可以使用COALESCE(myString, '') != ''代替您的;)。

摘要:
对于一个小针,不要制造锤机! 当这种行为对你来说并不那么重要时,你的项目就不会尝试改变或处理或创建你自己的相等字符串函数;)。

检查字符串是否可见(返回1)或不是(返回0)的示例函数可以是这样的:

CREATE FUNCTION IsVisible ( @string varchar(max) )
RETURNS bit
AS
BEGIN
    DECLARE @pString varchar(max) = @string;

    WITH InvisibleChars AS (
        SELECT c COLLATE SQL_Latin1_General_CP1_CI_AS AS c
        FROM (VALUES (CHAR(0)),  (CHAR(1)),  (CHAR(9)),  (CHAR(10)), 
                     (CHAR(11)), (CHAR(12)), (CHAR(13)), (CHAR(28)), 
                     (CHAR(29)), (CHAR(30)), (CHAR(31)), (CHAR(32)), 
                     (CHAR(160)) -- Above characters are non-visibles
             )  t(c)
     )
    SELECT @pString = REPLACE(@pString, c, '')
    FROM InvisibleChars;

    RETURN CASE WHEN @pString = '' THEN 0 ELSE 1 END;
END
GO