我有一个带名字的varchar记录列(列)varchar2(30)。如何使用列出的条件屏蔽名称中的字符。
e.g。
Tristram Vladimir Chan <---Original name
1234567890123456789012 <---ruler (character count)
.....XXXXX.....XXXXX.....<--- masking criteria: copy every 5 characters, mask next 5 characters to X
TristXXX XladimXX XXan <--- expected results
使这个棘手的是对空间“”角色的特殊处理......
最快的方法是什么?可以使用的任何库,程序或快捷方式?非常感谢!!!!
答案 0 :(得分:2)
要替换除空白之外的所有内容,您将使用regexp_replace。如果名称具有最大长度(我假设),则该语句易于编写。如果没有,那么你需要一个循环,你可以用存储的函数或一些技巧来实现。以下是最多30个字母的名称声明:
select substr(full_name,1,5) || regexp_replace(substr(full_name,6,5), '[^ ]', 'X') ||
substr(full_name,11,5) || regexp_replace(substr(full_name,16,5), '[^ ]', 'X') ||
substr(full_name,21,5) || regexp_replace(substr(full_name,26,5), '[^ ]', 'X') as masked_name
from
(
select 'Tristram Vladimir Chan ' as full_name from dual
)
答案 1 :(得分:1)
EDIT1:抱歉,我忘记了空间管理:这是 a 解决方案:
SET serveroutput ON format wraped;
DECLARE
l_text varchar2(64) := 'Tristram Vladimir Chan';
l_mask VARCHAR2(64) := '.....XXXX..XX.....X.XX';
l_res VARCHAR2(64);
l_text_length INTEGER := length(l_text);
l_mask_length INTEGER := length(l_mask);
l_pos INTEGER := 1;
l_p INTEGER := -1;
l_x INTEGER := -1;
l_b INTEGER := instr(l_text, ' ');
l_lastb INTEGER := 0;
l_cpt INTEGER := 1;
BEGIN
-- in case text length doesn't match mask length
if l_mask_length > l_text_length then
l_mask := substr(l_mask, 1, l_text_length);
elsif l_text_length > l_mask_length then
l_mask := rpad(l_mask, l_text_length, '.');
end if;
l_mask_length := l_text_length;
-- loop for each sequence of characters in the mask
while l_pos <= l_mask_length loop
l_p := instr(l_mask, '.'); -- first '.' position
l_x := instr(l_mask, 'X'); -- first 'X' position
-- if "." or "X' has not been found
if l_p = 0 or l_x = 0 then
if l_p = 0 then -- no "." found, write 'X' until the end
l_res := rpad(l_res, l_text_length, 'X');
elsif l_x = 0 then -- no "X" found, write text until the end
l_res := l_res || substr(l_text, l_pos, l_text_length);
end if;
l_pos := l_mask_length+1;
else
if l_p = 1 then -- '.' found, write text until the next 'X'
l_res := l_res || substr(l_text, l_pos, l_x-1);
l_mask := substr(l_mask, l_x);
l_pos := l_pos + l_x-1;
elsif l_x = 1 then -- 'X' found, write 'X' until the next '.'
l_res := rpad(nvl(l_res,'X'), nvl(length(l_res),0)+l_p-1, 'X');
l_mask := substr(l_mask, l_p);
l_pos := l_pos + l_p-1;
end if;
end if;
-- if a ' ' is found in the original text, replace it in the result
if l_pos > l_b and l_b > l_lastb then
l_b := instr(l_text, ' ', 1, l_cpt);
l_res := substr(l_res, 1, l_b-1) || ' ' || substr(l_res,l_b+1,length(l_res));
l_lastb := l_b;
l_cpt := l_cpt + 1;
end if;
end loop;
dbms_output.put_line(l_res);
END;
结果:TristXXX VlXXimir XhXX
有可能改进它,我只是“程序性地”写它。 与其他解决方案相比的优势在于,您可以在这里更改掩码。
EDIT2 :我重写了代码,它简单得多......:
SET serveroutput ON format wraped;
DECLARE
l_text varchar2(64) := 'Tristram Vladimir Chan';
l_mask VARCHAR2(64) := '.....XX..XX....XXXX...';
l_res VARCHAR2(64);
l_text_length INTEGER := length(l_text);
l_mask_length INTEGER := length(l_mask);
l_pos INTEGER := 1;
BEGIN
-- in case text length doesn't match mask length
if l_mask_length > l_text_length then
l_mask := substr(l_mask, 1, l_text_length);
elsif l_text_length > l_mask_length then
l_mask := rpad(l_mask, l_text_length, '.');
end if;
l_mask_length := l_text_length;
-- loop and build the result string
while l_pos <= l_mask_length loop
if substr(l_mask, l_pos, 1) = 'X' then
l_res := l_res || 'X';
else
l_res := l_res || substr(l_text, l_pos, 1);
end if;
if substr(l_text, l_pos, 1) = ' ' then
l_res := substr(l_res, 1, l_pos-1) || ' ';
end if;
l_pos := l_pos + 1;
end loop;
dbms_output.put_line(l_res);
END;
结果:TristXXm XXadimXX Xhan
答案 2 :(得分:1)
我的尝试就在这里:
SELECT listagg(dd ,'') within group(ORDER BY rn)
from(
SELECT SPLIT,
rn, case when mod(rn,10) <=5 and mod(rn,10) >0 then split WHEN split = ' ' THEN ' ' else 'X' end dd
FROM
(SELECT regexp_substr('Tristram Vladimir Chan', '.',LEVEL)split,
ROWNUM rn
FROM dual
CONNECT BY LEVEL <= LENGTH('Tristram Vladimir Chan')
));