Oracle REGEXP_REPLACE 字符串从 nn 位置开始替换 'n' 次

Oracle REGEXP_REPLACE string to replace 'n' times starting with nn position

我想替换'|'和 '_'。替换应该从第nn个字符开始,替换n次。例如

ABC|1234|mno|p|q|r|456|XYZ|QRS|TUV ====> ABC|1234|mno|p_q_r|456|XYZ|QRS|TUV

在上面的例子中 nn=14 和 n=3

到目前为止,我已经试过了,但没有得到预期的结果

SELECT REGEXP_REPLACE('ABC|1234|mno|p|q|r|456|XYZ', '[|]', '_',14) rep_str FROM DUAL

您可以使用普通 substr/instr 来完成,但需要仔细处理边缘情况。提取您需要的部分并更换其中的所有管道。然后把所有东西都放在一起。

with
--
function replace_n(
  str in varchar2,
  start_ in number,
  count_ in number
)
return varchar2
as
begin
  return
    /*Starting part unchanged*/
    substr(str, 1, start_)
    /*Replacement: locate n'th - 1 occurrence of pipe*/
    || translate(
      substr(str, start_ + 1, instr(str, '|', start_, count_-1) - start_)
      , '|'
      , '_'
    )
    /*Remaining string*/
    || substr(str, instr(str, '|', start_, count_ - 1) + 1)
  ;
end;
--
a(a) as (
  select
  'ABC|1234|mno|p|q|r|456|XYZ|QRS|TUV'
  from dual
)

select replace_n(a, 14, 3) as res
from a
| RES                                |
| :--------------------------------- |
| ABC|1234|mno|p_q_r|456|XYZ|QRS|TUV |

db<>fiddle here

UPD:或者如果您要从位置 nnn:

开始替换大小为 n 的子字符串
with
--
function replace_n(
  str in varchar2,
  start_ in number,
  count_ in number
)
return varchar2
as
begin
  return
    /*Starting part unchanged*/
    substr(str, 1, start_)
    /*Replacement: extract substring on size n*/
    || translate(
      substr(str, start_ + 1, instr(str, '|', start_, count_-1) - start_)
      , '|'
      , '_'
    )
    /*Remaining string*/
    || substr(str, instr(str, '|', start_, count_ - 1) + 1)
  ;
end;
--

db<>fiddle here

在您的简单示例中,更容易指定两个事件:

  regexp_replace(
      str
     , '\|([^|]+)'
     ||'\|([^|]+)' -- 2 times just to make it more readable
     ||'(.*)'      --  others
     ,'__'
     ,14
  )

带有测试数据的完整示例:DBFiddle

with t as (
select
  'ABC|1234|mno|p|q|r|456|XYZ|QRS|TUV' str
 ,'ABC|1234|mno|p_q_r|456|XYZ|QRS|TUV' chk 
from dual
)
select
  str,chk,
  regexp_replace(
      str
     , '\|([^|]+)'
     ||'\|([^|]+)' -- 2 times just to make it more readable
     ||'(.*)'      --  others
     ,'__'
     ,14
  ) as str2
from t
/

或者如果你让它更可定制并更容易指定替换数量,你可以使用简单的内联 pl/sql 函数和这样的循环:DBFiddle

with function regexp_replaces(
    source_char  varchar2
   ,pattern      varchar2
   ,replace_char varchar2
   ,position     int
   ,cnt          int
  ) return varchar2
as
  res varchar2(4000):=source_char;
begin
  for i in 1..cnt loop
    res:=regexp_replace(res,pattern,replace_char,position,1);
  end loop;
  return res;
end;
select
  str,chk,
  regexp_replaces(str,'\|','_',14,2) as str2
from t;