Friday, February 10, 2006

Fun with strings

Let's imagine there's a string of alpha-numeric and numeric characters and we need to delimit the string in order to separate both types of characters, e.g. to prepare values for normalization. The following scalar function (minus a few bugs) inserts a parameterised delimiter (a single space as the default) between individual series of apha-numeric and numeric data:
create function dbo.fnGet_DelimitedString_byNumChar
 (
 @inStr  nvarchar(max)
 ,@delimiter nvarchar(16)  = null
 )
returns nvarchar(max)
as
begin
 declare @result   nvarchar(max)
 declare @curChar  int
 declare @numLoops  int
 declare @lookForNum  bit
 declare @lookForPattern  nvarchar(16)
 declare @lookForNumber  nvarchar(16)
 declare @lookForNotNumber nvarchar(16)

 if (@delimiter is null)
  begin
   set @delimiter = N' '
  end

 set @lookForNumber = N'%[0-9]%'
 set @lookForNotNumber = N'%[^0-9]%'

 set @numLoops = 1

 if (patindex(@lookForNumber, left(@inStr, 1)) = 0)
  begin
   set @lookForNum = 0
   set @lookForPattern = @lookForNotNumber
  end
 else
  begin
   set @lookForNum = 1
   set @lookForPattern = @lookForNumber
  end

 set @curChar = patindex(@lookForPattern, @inStr)
 set @result = substring(@inStr, 0, @curChar)

 while (@curChar != 0)
  begin
   if (@lookForNum = 1)
    begin
     set @inStr = substring(@inStr, @curChar, datalength(@inStr))

     set @result
       = @result
       + case
        when right(@result, datalength(@delimiter) / 2) = @delimiter
         or left(@inStr, datalength(@delimiter) / 2) = @delimiter
         or @numLoops = 1
         then N''
        else @delimiter
        end
       + substring(@inStr, 0, patindex(@lookForNotNumber, @inStr))

     set @lookForNum = 0
     set @lookForPattern = @lookForNotNumber
    end
   else
    begin
     set @inStr = substring(@inStr, @curChar, datalength(@inStr))

     set @result
       = @result
       + case
        when right(@result, datalength(@delimiter) / 2) = @delimiter
         or left(@inStr, datalength(@delimiter) / 2) = @delimiter
         or @numLoops = 1
         then N''
        else @delimiter
        end
       + substring(@inStr, 0, patindex(@lookForNumber, @inStr))

     set @lookForNum = 1
     set @lookForPattern = @lookForNumber
    end

   set @numLoops = @numLoops + 1
   set @curChar = patindex(@lookForPattern, @inStr)
  end

 set @result = @result + @inStr

 return @result
end
go
A few examples of use:
select dbo.fnGet_DelimitedString_byNumChar('512btc', '-')
union all
select dbo.fnGet_DelimitedString_byNumChar('FX788H', null)
union all
select dbo.fnGet_DelimitedString_byNumChar('S XX12', null)
union all
select dbo.fnGet_DelimitedString_byNumChar('S XX12OH', null)
Silly, isn't it...? ;) But add this function by Dejan Sarka to the picture, and it makes a bit more sense. ML p.s. Thanks to Razvan Socol for catching the bugs which have now been crushed.

No comments: