This seems simple to me, but I can't get my brain around it.
I want to take a string, check for spaces, ignore the first space,
but remove all subsequent spaces. For example:
MyString := 'Alexander The Great';
Output would be 'Alexander TheGreat'
Much thanks in advance! (Using Turbo Pascal 7.0 for DOS)
I usually use Java so I don't know if this is the best way to do what you ask but at least it seems to work...
program nospaces(output);
var
MyString : string;
ResultStr: string;
count: integer;
i: integer;
Temp: string;
n: string;
begin
ResultStr:='';
MyString := 'Alexander The Great';
writeln(MyString);
count := 0;
for i := 1 to length(MyString) do
begin
Temp := copy(MyString, i, 1);
if Temp = ' ' then
begin
If count=0 then
begin
count := count + 1;
ResultStr := ResultStr + Temp;
end;
end
else
begin
ResultStr := ResultStr + Temp;
end
end;
writeln(ResultStr);
readln(n);
end.
what have I done? I cicle on the characters of the String. If the character that I found isn't a space I add that to the resulting String. If the character is a 'space' and it is the first (it's the first because count=0) I add 1 to count and add the character to the resulting string. Then if the character is a space again I'll have the count=1 that make me continue ignoring this space.
Thank you Mauros for your help, although I figured it out this morning before checking back here. This is the answer, for anyone else who might run into this in the future:
Crush the Name if it has more than one space in it
For example: "Alexander The Great" Becomes "Alexander TheGreat",
"John" stays as "John", "Doc Holiday" stays as "Doc Holiday"
"Alexander The Super Great" becomes "Alexander TheSuperGreat" and
so on and so forth.
FirstSpacePosition := POS(' ',LT.Name);
s := Copy(LT.Name,1,FirstSpacePosition);
s2 := Copy(LT.Name,FirstSpacePosition,Length(LT.Name));
s := StripAllSpaces(s);
s2 := StripAllSpaces(s2);
Insert(' ',s,(Length(s)+1));
LT.Name := s+s2;
StripTrailingBlanks2(LT.Name);
StripLeadingBlanks(LT.Name);
And the StripAllSpaces Function looked like this:
FUNCTION StripAllSpaces(s3:STRING):STRING;
BEGIN
WHILE POS(' ',s3)>0 DO Delete(s3,Pos(' ',s3),1);
StripAllSpaces:=s3;
END;{StripAllSpaces}
And The StripLeadingBlanks / StripTrailingBlanks Functions look like this:
PROCEDURE StripTrailingBlanks2(var Strg: string);
BEGIN
while Strg[Length(Strg)] = ' ' do
Delete(Strg, Length(Strg), 1);
END; { END StripTrailingBlanks }
PROCEDURE StripLeadingBlanks(var Strg: string);
BEGIN
While (Length(Strg) > 0) and (Strg[1] = ' ') do
Delete(Strg, 1, 1);
END; { END StripLeadingBlanks }
Related
I'm trying to run an interface in ODI 11g. When I call the procedure I get this error :
ODI-1228: Task START_JC (Procedure) fails on the target ORACLE connection OJC.
Caused By: java.sql.SQLException: ORA-06502: PL/SQL: numeric or value error: NULL index table key value
ORA-06512: at "OJC.JC_MASTER", line 129
ORA-06512: at "OJC.JC_MASTER", line 689
ORA-06512: at line 9
the sql code
PROCEDURE string_to_aa_parameter_type (
p_string VARCHAR2,
p_out_aa_parameter_values IN OUT aa_parameter_type
)
AS
v_start INTEGER := 1;
v_pos INTEGER := 0;
v_counter INTEGER := 0;
v_temp_parameter_name VARCHAR2 (4000);
v_temp_parameter_value VARCHAR2 (4000);
BEGIN
IF p_string IS NULL
THEN
RETURN;
END IF;
-- determine first chuck of string
v_pos := INSTR (p_string, '=', v_start);
-- while there are chunks left, loop
WHILE (v_pos != 0)
LOOP
v_counter := v_counter + 1;
-- create array
IF MOD (v_counter, 2) = 1
THEN
v_temp_parameter_name :=
SUBSTR (p_string, v_start, v_pos - v_start);
v_start := v_pos + 1;
v_pos := INSTR (p_string, ';', v_start);
ELSE
v_temp_parameter_value :=
SUBSTR (p_string, v_start, v_pos - v_start);
p_out_aa_parameter_values (trim(v_temp_parameter_name)) :=
trim(v_temp_parameter_value);
v_start := v_pos + 1;
v_pos := INSTR (p_string, '=', v_start);
END IF;
END LOOP;
-- IN THE FOLLOWING LINE I GET THE ERROR
v_temp_parameter_value := SUBSTR (p_string, v_start);
p_out_aa_parameter_values (trim(v_temp_parameter_name)) :=
trim(v_temp_parameter_value);
END;
Can someone help me in figuring out that the problem is ?
You'll get that error if p_string is a not-null value which doesn't contain an equals sign at all, or with any semicolon-delimited part that starts with an equals sign. It's thrown by the line after the one you indicated (or the equivalent line inside the loop, if p_string has a final semicolon).
If there is no equals sign at all then
v_pos := INSTR (p_string, '=', v_start);
gives zero, which means you don't go through the loop at all; which means when you get to that final assignment v_temp_parameter_name has never been set.
If there is a key/value pair with no key, say p_string is 'x=y;=z' you do go into the loop, and with that example the first key/value pair is added to the array; but then v_start and v_pos end up as the same value (5 in this case, both pointing to the second =). The the next time round the loop:
v_temp_parameter_name :=
SUBSTR (p_string, v_start, v_pos - v_start);
evaluates to SUBSTR(p_string, 5, 0) (where the third argument is zero because those two variables are the same), which is always going to be an empty string, or null.
There is no actual error yet, so it evaluates v_pos again, and either gets zero or non-zero, depending on whether there is a terminating semicolon.
If it's non-zero then it goes round the loop again; if it's zero it drops out. Either way it has a last stab at getting the matching value - it doesn't matter if that is set to anything or not. When it tries to add the element to the array, though, the name is still null, and you get that error, from whichever of the two array assignments it hits.
You could do additional testing and handling inside the procedure to spot and discard null keys, but
i didn't write the procedure,i have to run it . It is supposed to be syntax correct
So you need to figure out why the Java code is passing a value which the procedure can't handle - i.e. why it is sending incomplete key/value pairs.
I wrote the stored procedure below for the purposes of reading a comma delimited file that is moved onto the server ("DIR" folder), and when script is executed, it essentially parses the file (.csv), and assigns the data to its respective variables (xJOB_ID, xCTRL_ID, xACCT_SEC, xCREATEDON_DATE) such that I can insert the data into a table. I am using Oracle SQL Developer version 4.0.0.13 in a Windows 7 environment. Fortunately, after banging my head on the table a couple of times the code works and I have not had any issues running the script.
Example format of the file:
1111, 2, T, 10/10/2000
2222, 12345, U, 10/10/2001
5555, 123, S, 10/10/1999
MY QUESTION:
I found a little difficulty using the SUBSTRING & INSTRING functions to parse the data and wanted to know how can I improve the script so that in the event some debugging was needed, it can be easily resolved for someone that did not write the stored procedure. Please let me know if that makes sense. I gave you the entire script so that you can understand what I was trying to accomplish and so that I can improve the code for debugging purposes.
create or replace PROCEDURE SP_INSERT_INTO_TABLE(xFILE_NAME IN VARCHAR2)
IS
--UTL_FILE is an oracle package that allows you to read and write operating system files.
TEXT_DATA UTL_FILE.FILE_TYPE;
v_ROW_LENGTH NUMBER := 1024;
v_TEXTSTRING VARCHAR2(4000);
cLINE VARCHAR2(100);
xJOB_ID NUMBER;
xCTRL_ID NUMBER;
xACCT_SEC VARCHAR2(1);
xCREATEDON_DATE DATE;
xCOUNT NUMBER := 0;
BEGIN
BEGIN
--Streams in the file data and assigns it to TEXT_DATA variable.
TEXT_DATA := UTL_FILE.FOPEN('DIR', xFILE_NAME, 'R', v_ROW_LENGTH);
END;
--Begin LOOP to get each line and assign to cLINE to extract, assign to each variable, and insert into the table
LOOP
BEGIN
--Gets each string/line up to the line terminator
UTL_FILE.GET_LINE(TEXT_DATA, v_TEXTSTRING);
EXCEPTION
WHEN NO_DATA_FOUND THEN
EXIT;
END;
--Each line is assigned to the variable cLINE.
cLINE := v_TEXTSTRING;
--Begin to parse data using SUBSTRING and INSTRING functions
BEGIN
--Extracts string from cLINE position 1 up to the first occurrence, converts it to a number, and assigns it to the variable.
xJOB_ID := TO_NUMBER(SUBSTR(cLINE, 1,INSTR(cLINE, ',', 1, 1)-1));
--Extracts string from cLINE between the 1st and 2nd occurrence, converts it to a number, and assigns it to the variable.
xCTRL_ID := TO_NUMBER(SUBSTR(cLINE, INSTR(cLINE, ',', 1, 1)+1, INSTR(cLINE, ',', 1,2)-INSTR(cLINE, ',', 1,1)-1));
--Extracts string from cLINE between the 2nd and 3rd occurrence and assigns it to the variable.
xACCT_SEC := SUBSTR(cLINE, INSTR(cLINE, ',', 1, 2) +1, INSTR(cLINE, ',', 1,3)-INSTR(cLINE, ',', 1,2) -1);
--Extracts string from cLINE after the last occurrence, converts it to a date, and assigns it the variable.
xCREATEDON_DATE := TO_DATE(SUBSTR(cLINE, INSTR(cLINE, ',', 1, 3)+1), 'MM/DD/YYYY');
INSERT INTO TABLE(JOB_ID, CTRL_ID, ACCT_SEC, CREATEDON_DATE)
VALUES(xJOB_ID, xCTRL_ID, xACCT_SEC, xCREATEDON_DATE);
COMMIT;
--Counter to count the amount of inserts
xCOUNT := xCOUNT + 1;
EXCEPTION
--Exception to handle the conversion of a string to a NUMBER or value is longer than the declared length of the variable.
WHEN VALUE_ERROR THEN
NULL;
END;
END LOOP;
DBMS_OUTPUT.PUT_LINE('RECORDS INSERTED: ' || xCOUNT);
UTL_FILE.FCLOSE(TEXT_DATA);
END;
You could use REGEXP_SUBSTR instead, since that just requires one function call, rather than a series of SUBSTR and INSTR calls, eg:
(courtesy of Gary_W):
declare
v_str varchar2(20) := 'a,,bcd';
v_substr1 varchar2(10);
v_substr2 varchar2(10);
v_substr3 varchar2(10);
begin
v_substr1 := regexp_substr(v_str, '([^,]*)(,|$)', 1, 1, NULL, 1);
v_substr2 := regexp_substr(v_str, '([^,]*)(,|$)', 1, 2, NULL, 1);
v_substr3 := regexp_substr(v_str, '([^,]*)(,|$)', 1, 3, NULL, 1);
dbms_output.put_line(v_substr1||':'||v_substr2||':'||v_substr3);
end;
/
a::bcd
The above will work with strings that have null portions, as demonstrated. The search pattern has been split into two groups (aka subexpressions): [^,]* and ,|$.
The first group says: any character that isn't a comma ([^,]) that appears 0 or more times (*).
The second group says: comma or the end of the line.
So the whole pattern is looking for a set of any characters excluding the comma that may or may not exist which is followed by either a comma or the end of the line.
The last parameter in the regexp_substr is indicating that we want to pick the 1st subexpression from the search pattern to display - if we didn't include this, then you'd end up with the commas being displayed as part of the string being returned.
If you're absolutely sure that none of the elements of the string will ever be null, then the following will work:
declare
v_str varchar2(20) := 'a,123,bcd';
v_substr1 varchar2(10);
v_substr2 varchar2(10);
v_substr3 varchar2(10);
begin
v_substr1 := regexp_substr(v_str, '[^,]+', 1, 1);
v_substr2 := regexp_substr(v_str, '[^,]+', 1, 2);
v_substr3 := regexp_substr(v_str, '[^,]+', 1, 3);
dbms_output.put_line(v_substr1||':'||v_substr2||':'||v_substr3);
end;
/
a:123:bcd
This is just looking for the specified occurrence of a string of characters that aren't a comma, which is slighly easier to grok (imho!) than the search pattern used in the previous example, but is a lot less robust.
I am writing a function which would build a fix a length empty string with value in a fix length string. for e.g.
I have a string of length 1000. When I pass string 'ABC' of length 5 with position 50, the function puts 'ABC' at 50th position of that fixed length string and pads 2 char with ' '.
I have managed to write it using regexp_replace. However I need it which doesnt use regexp_replace as it seems very slow in processing.
This function will be called in batch processing to build message string to be passed to other interface.
create or replace function insert_string(i_value varchar2, i_length number, i_position number) return varchar2
is
fix_string varchar2(1000) := ' ';
begin
fix_string := rpad(fix_string,1000,' ');
fix_string := regexp_replace(fix_string,'.{'||i_length ||'}',rpad(i_value,i_length,' '),i_position,1);
return fix_string;
end;
Something like this:
CREATE OR REPLACE FUNCTION insert_string(i_value VARCHAR2, i_length NUMBER, i_position NUMBER) RETURN VARCHAR2 IS
BEGIN
RETURN RPAD(LPAD(i_value, i_position + LENGTH(i_value) - 1), i_length);
END;
I dont know if this is what youve been trying to do/say but in case it is, ive added another parameter which will contain the source string. Without the string inside the function, its hard to manipulate values.
CREATE OR REPLACE FUNCTION INSERT_STRING(source_string VARCHAR2,
input_string VARCHAR2,
start_position NUMBER,
input_len NUMBER)
RETURN VARCHAR2 AS
TYPE char_arr IS TABLE OF CHAR(1) INDEX BY pls_integer;
TYPE char_arr2 IS TABLE OF CHAR(1);
source_array char_arr2:= char_arr2();
input_array char_arr;
X NUMBER:=1;
new_string VARCHAR2(2000);
BEGIN
FOR i IN 1..LENGTH(source_string) -- converts the source string to array
LOOP
source_array.extend;
source_array(i) := substr( source_string, i, 1 );
END LOOP;
-------------------------------------------------------------------
FOR j IN 1 .. input_len -- converts the input string to array with size input_len
LOOP
input_array(j) := substr( input_string, j, 1 );
END LOOP;
-------------------------------------------------------------------
--dbms_output.put_line(input_array(1));
FOR k IN 1..LENGTH(source_string) -- loop to insert the input_string to the source string
LOOP
IF k >= start_position THEN
source_array.extend;
source_array(k) := input_array(X) ;
x := x+1;
IF x > input_array.count THEN
exit;
end if;
END IF;
END LOOP;
FOR m IN 1 .. LENGTH(source_string) --loop to convert array back to string
LOOP
IF source_array(m) is null THEN
source_array(m) := ' ';
END IF;
new_string := new_string||source_array(m);
END LOOP;
RETURN new_string;
END;
Sample:
select insert_string('**********', 'ABC', 3, 5) from dual;
Output:
INSERT_STRING('**********','ABC',3,5)
**ABC ***
take note.
Source string should not be empty.
the size of the source string should be greater than the size of the input_string else the excess character from the input will not be inserted in the source string
There's the simplified version of my code who keep raise me ORA-06502:
declare
p_filter varchar2(300) := '2012';
p_value varchar2(300) := '12345.000';
w_new_value number(13,3) := null ;
w_count number(4) := null ;
BEGIN
SELECT count(*)
INTO w_count
FROM dual
where p_filter = p_filter;
--- more filters
if w_count != 0 then
w_new_value := p_value / w_count;
else
w_new_value := p_value;
end if;
-- do something
end;
/
Someone can give me a help?
DataBase Details
nls_language = italian
nls_territory = italy
nls_currency = �
nls_iso_currency = italy
nls_numeric_characters = ,.
nls_calendar = gregorian
nls_date_format = dd-mon-rr
nls_date_language = italian
nls_characterset = we8iso8859p15
nls_sort = west_european
nls_time_format = hh24:mi:ssxff
nls_timestamp_format = dd-mon-rr hh24:mi:ssxff
nls_time_tz_format = hh24:mi:ssxff tzr
nls_timestamp_tz_format = dd-mon-rr hh24:mi:ssxff tzr
nls_dual_currency = �
nls_nchar_characterset = al16utf16
nls_comp = binary
nls_length_semantics = byte
nls_nchar_conv_excp = false
First, this is always going return a value of 1.
SELECT count(*)
INTO w_count
FROM dual
It doesn't matter what the qualifier is.
Lastly, I just ran your simplified code example in Oracle 11R2 and it didn't throw an exception.
I added the following statement in place of your "do something" comment:
dbms_output.put_line('w_new_value: ' || w_new_value || '. w_count: ' || w_count);
The result was:
w_new_value: 12345. w_count: 1
So, I think you've simplified your example into oblivion. You need to provide something that actually shows the error.
Good luck.
I found myself the ansewer and i think is useful for other know.
The real problem of the script for my DB is the language.
The italian "version" of Oracle accept , instead of the . for translate the VARCHAR2 into NUMBER unlike the most of other country.
For make the code running well the solution is
w_new_value := replace(p_value,'.',',') / w_count;
This trick finally allows the DB use my VARCHAR2 param like a NUMBER
I have a long string ("sara james joseph NAUMAN") which is displayed using Crystal Reports in VS2008(c#) using a stored procedure.
I want to convert it to title case ("Sara Williams Joseph Nauman")
I also want to remove spaces if there are more than two between the words.
I want to know how to do the conversion in a stored procedure, Crystal Reports formula, or .cs file.
There is a function called ProperCase(str) that will fix the casing.
`ProperCase("sara james joseph NAUMAN")`
Borrowing a bit of Hariharan Anbazhagan's code; if you happen to have blank spaces before and after your string, use Trim(str). No function comes to mind to remove blank spaces between words so this code will do the job.
stringvar str1;
stringvar str2;
numbervar counter;
numbervar leng;
str2 := Trim(" Big Space ");
leng := len(str2);
if leng>0 then
(
for counter := 1 to leng do
(
If Not(Mid(str2, counter, 1) = " " and (Mid(str2, counter+1, 1)) = " ") Then
(
str1:=str1 + Mid(str2, counter, 1)
)
);
str1
)
Custom formula Format():
// Format()
Function (Stringvar value, Optional Stringvar delimiter:=",")
// create an array of formetted names (and blanks)
// "sara james joseph NAUMAN" => "Sara James Joseph Nauman"
Local Stringvar Array temp:=Split( ProperCase(value), " ");
Local Stringvar Array values;
Local Numbervar i;
// remove empty array positions
For i:= 1 To Ubound(temp) Do (
If temp[i]<>"" Then (
Redim Preserve values[Ubound(values)+1];
values[Ubound(values)]:=temp[i]
)
);
// delimited list
Join(values, delimiter);
Reference it in a formula:
// returns "Sara,James,Joseph,Nauman"
Format("sara james joseph NAUMAN")
In VB.net, the equivalent is:
str1 = StrConv(str1, vbProperCase)
This is just an FYI in case someone else is looking for this in VB.net.