A regexp_replace():
with tests(example) as (values
('ab02'),('ab1'),('A'),('1'),('A1'),('123'),('ABC'),('abc0'),('a123'),('abcd0123'),('1a'),('a1a'),('1a1') )
select example,
regexp_replace(
example,
'^(\D{0,4})(\d{0,4})$',
'\1' || repeat('0',4-length(example)) || '\2' )
from tests;
example | regexp_replace
----------+----------------
ab02 | ab02
ab1 | ab01
A | A000
1 | 0001
A1 | A001
123 | 0123
ABC | ABC0
abc0 | abc0
a123 | a123
abcd0123 | abcd0123 --caught, repeat('0',-4) is same as repeat('0',0), so nothing
1a | 1a --doesn't start with non-digits
a1a | a1a --doesn't end with digits
1a1 | 1a1 --doesn't start with non-digits
- catches non-digits with a
\D at the start of the string ^
- catches digits with a
\d at the end $
- specifies that it's looking for 0 to 4 occurences of each
{0,4}
- referencing each hit enclosed in consecutive parentheses
() with a backreference \1 and \2.
- filling the space between them with a
repeat() up to the total length of 4.
It's good to consider additional test cases.