'PATINDEX pattern to replace character that is *not* first character
I'm trying to paritally 'leet-ify' a word, by taking a single character from a pre-defined set of replacable characters, and replacing it with a number/special character that resembles the letter being replaced. I don't want to replace the first character though.
Earlier I get a word that I've ensured has at least one letter after the first that is within the set of replaceable letters, and place it in the @Word variable. I then use the following to replace one letter. (Using 'loveable' as an example)
DECLARE @Word varchar(8) = 'lovable';
DECLARE @ReplaceChar varchar(1);
SET @ReplaceChar = SUBSTRING(@Word,PATINDEX('[a-z][abeilost]',@Word)+1,1);
SET @ReplaceChar =
(SELECT CASE @ReplaceChar
WHEN 'a' THEN '@'
WHEN 'b' THEN '8'
WHEN 'e' THEN '3'
WHEN 'i' THEN '!'
WHEN 'l' THEN '1'
WHEN 'o' THEN '0'
WHEN 's' THEN '$'
WHEN 't' THEN '+'
END
);
SET @Word = STUFF(@Word,PATINDEX('[a-z][abeilost]',@Word)+1,1,@ReplaceChar);
As I understand it, PATINDEX should be finding the starting location of the first any-letter-followed-by-a-matching-letter string, and SUBSTRING/STUFF explicitly adds 1 to that number before collecting or replacing, so I should never get 'lovable' changed to '1oveable'... But that's what I'm getting. What am I missing?
To clarify expected results:
| Input | Output |
|---|---|
| loveable | l0veable |
| give | g1ve |
| shelter | sh3lter |
| grams | gr@ms |
| phrygian | phryg!an |
Solution 1:[1]
After our talk in the comments above I'd suggest this approach:
DECLARE @Word varchar(8) = 'truth';
DECLARE @toBeReplaced VARCHAR(10)='abeilost';
DECLARE @replaceWith VARCHAR(10)='@83!10$+';
DECLARE @position INT=PATINDEX(CONCAT('%[',@toBeReplaced,']%'),SUBSTRING(@word,2,8000))+1;
SELECT STUFF(@word,@position,1,TRANSLATE(SUBSTRING(@word,@position,1),@toBeReplaced,@replaceWith));
The idea in short:
- We define your translate parameters.
- We find the position using
PATINDEX()behind the first character. - Now we can use
STUFF()to replace exactly one character at the given position by its translation.
For the next time: It would help a lot if you'd provided some samples with the expected result.
UPDATE
Using this at a tabular result, you can avoid the declared variable and do this inline:
DECLARE @WordTable TABLE(SomeText varchar(8));
INSERT INTO @WordTable VALUES('truth'),('loveable');
DECLARE @toBeReplaced VARCHAR(10)='abeilost';
DECLARE @replaceWith VARCHAR(10)='@83!10$+';
--the new query
SELECT STUFF(wt.SomeText,pos,1,TRANSLATE(SUBSTRING(wt.SomeText,pos,1),@toBeReplaced,@replaceWith))
FROM @WordTable wt
CROSS APPLY(SELECT PATINDEX(CONCAT('%[',@toBeReplaced,']%'),SUBSTRING(wt.SomeText,2,8000))+1) A(pos);
Solution 2:[2]
SELECT PATINDEX('[a-z][abeilost]', @Word)
Is returning 0. According to the documentation 0 means not found. The reason it is not being found it that your search criteria is for 2 characters only, the first being a single character which matches [a-z], and the second being a single character which matches [abeilost]. If you want to allow more characters you need to extend the search expression e.g.
SELECT PATINDEX('[a-z][abeilost]%', @Word)
Although I expect that isn't doing what you want as I imagine you want to repeat the specified characters any number of times which isn't possible with PATINDEX.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Shnugo |
| Solution 2 | Dale K |
