Reputation: 1
Can I use some function call on REGEXP_REPLACE
back-reference value?
For example I want to call chr()
or any other function on back-reference value, but this
SELECT REGEXP_REPLACE('a 98 c 100', '(\d+)', ASCII('\1')) FROM dual;
just returns ASCII value of '\':
'a 92 c 92'
I want that the last parameter (replacement string) to be evaluated first and then to replace string. So result would be:
'a b c d'
Upvotes: 0
Views: 512
Reputation: 142713
I'm not that smart to do it using one regular expression, but - step-by-step, something like this might help. It splits the source string into rows, checks whether part of it is a number and - if so - selects CHR
of it. Finally, everything is aggregated back to a single string.
SQL> with test (col) as
2 (select 'a 98 c 100' from dual),
3 inter as
4 (select level lvl,
5 regexp_substr(col, '[^ ]+', 1, level) c_val
6 from test
7 connect by level <= regexp_count(col, ' ') + 1
8 ),
9 inter_2 as
10 (select lvl,
11 case when regexp_like(c_val, '^\d+$') then chr(c_val)
12 else c_val
13 end c_val_2
14 from inter
15 )
16 select listagg(c_val_2, ' ') within group (order by lvl) result
17 from inter_2;
RESULT
--------------------
a b c d
SQL>
It can be shortened for one step (I intentionally left it as is so that you could execute one query at a time and check the result, to make things clearer):
SQL> with test (col) as
2 (select 'a 98 c 100' from dual),
3 inter as
4 (select level lvl,
5 case when regexp_like(regexp_substr(col, '[^ ]+', 1, level), '^\d+$')
6 then chr(regexp_substr(col, '[^ ]+', 1, level))
7 else regexp_substr(col, '[^ ]+', 1, level)
8 end c_val
9 from test
10 connect by level <= regexp_count(col, ' ') + 1
11 )
12 select listagg(c_val, ' ') within group (order by lvl) result
13 from inter;
RESULT
--------------------
a b c d
SQL>
[EDIT: what if input looks differently?]
That is somewhat simpler. Using REGEXP_SUBSTR
, extract digits: ..., 1, 1
returns the first one, ... 1, 2
the second one. Pure REPLACE
then replaces numbers with their CHR
values.
SQL> with test (col) as
2 (select 'a98c100e' from dual)
3 select
4 replace(replace(col, regexp_substr(col, '\d+', 1, 1), chr(regexp_substr(col, '\d+', 1, 1))),
5 regexp_substr(col, '\d+', 1, 2), chr(regexp_substr(col, '\d+', 1, 2))) result
6 from test;
RESULT
--------------------
abcde
SQL>
Upvotes: 1
Reputation: 191275
Just for fun really, you could do the tokenization, conversion of numbers to characters, and aggregation using XPath:
select *
from xmltable(
'string-join(
for $t in tokenize($s, " ")
return if ($t castable as xs:integer) then codepoints-to-string(xs:integer($t)) else $t,
" ")'
passing 'a 98 c 100' as "s"
);
Result Sequence
--------------------------------------------------------------------------------
a b c d
The initial string value is passed in as $s
; tokenize()
splits that up using a space as the delimiter; each $t
that generates is evaluated to see if it's an integer, and if it is then it's converted to the equivalent character via codepoints-to-string
, otherwise it's left alone; then all the tokens are recombined with string-join()
.
If the original has runs of multiple spaces those will collapse to a single space (as they will with Littlefoot's regex).
Upvotes: 1