Never write your own code when you can just use a built-in capability of the Oracle database or the PL/SQL language.
In my case, I needed to take a CSV input - a record or line of text containing comma-separated values enclosed with
double quotes - and turn it into a set of discrete field values.
Most of the way through writing a string parser with INSTR, SUBSTR, REPLACE, TRANSLATE, etc. I realized I could do it
all in one pass by calling DBMS_OUTPUT.COMMA_TO_TABLE, as follows:
FUNCTION comma_to_table(iv_raw IN VARCHAR2) RETURN dbms_utility.lname_array IS
ltab_lname dbms_utility.lname_array;
ln_len BINARY_INTEGER;
BEGIN
dbms_utility.comma_to_table(list => iv_raw
,tablen => ln_len
,tab => ltab_lname);
FOR i IN 1 .. ln_len LOOP
dbms_output.put_line('element ' || i || ' is ' || ltab_lname(i));
END LOOP;
RETURN ltab_lname;
END;
Next, I need to grab a particular field from the input, say at position 'n':
FUNCTION get_field
(
iv_raw IN VARCHAR2
,in_pos IN NUMBER
-- ,iv_delim IN VARCHAR2 DEFAULT gcv_delim
-- ,iv_encl IN VARCHAR2 DEFAULT gcv_encl -- enclosure not supported yet
) RETURN VARCHAR2 IS
ltab_lname dbms_utility.lname_array;
lv_retval VARCHAR2(32760);
BEGIN
ltab_lname := comma_to_table(iv_raw);
lv_retval := ltab_lname(in_pos);
RETURN lv_retval ;
END ;
Just to make you laugh, here is the incomplete and somewhat bug-riddled code I was writing before I came to my senses. There are
all sorts of cases that this won't handle well.
-- FUNCTION get_field
-- (
-- iv_raw IN VARCHAR2
-- ,in_pos IN NUMBER
-- ,iv_delim IN VARCHAR2 DEFAULT gcv_delim
-- -- ,iv_encl IN VARCHAR2 DEFAULT gcv_encl -- enclosure not supported yet
-- ) RETURN VARCHAR2 IS
-- ln_start PLS_INTEGER := 0;
-- ln_end PLS_INTEGER := 0;
-- ln_length PLS_INTEGER := 0;
-- lv_retval VARCHAR2(32760);
-- ln_nth PLS_INTEGER := in_pos;
-- ln_fieldcount PLS_INTEGER := 0;
-- BEGIN
-- -- field count cannot exceed delimeter count + 1
-- -- count delimiters by comparing lengths before and after
-- -- removing the delimeter, adjusting for the length of the
-- -- delimeter itself. Field count = delimeter count +1.
-- ln_fieldcount := ((length(iv_raw) -
-- length(translate(iv_raw
-- ,'a' || iv_delim
-- ,'a'))
-- )/length(iv_delim)) + 1;
-- IF ln_fieldcount < 2 THEN
-- lv_retval := iv_raw;
-- ELSIF ln_fieldcount < ln_nth THEN
-- lv_retval := NULL ;
-- ELSE
-- IF ln_nth = 1 THEN
-- ln_start := 1;
-- ELSE
-- ln_start := instr(iv_raw
-- ,iv_delim
-- ,1
-- ,ln_nth-1) + 1;
-- END IF;
-- IF ln_nth = ln_fieldcount THEN
-- ln_end := length(iv_raw);
-- ELSE
-- ln_end := instr(iv_raw
-- ,iv_delim
-- ,1
-- ,ln_nth ) - 1;
-- END IF ;
-- ln_length := (ln_end - ln_start) +1 ;
-- lv_retval := substr(iv_raw,ln_start,ln_length);
-- END IF ; -- ln_fieldcount < 2
-- RETURN (lv_retval) ;
-- END;






