How to parse string in Ada - ada

I have user input string which i am getting with Get_Line,
input from user can be like, each line terminated with new line,
1;2;3
4;50;6
7;A;8
1;;7
How can i parse each of this string one by one to get character, Integer and ';'?
--
Thanks

Strings in Ada are arrays so getting a single character would be Str(n)
for i in Str'Range loop
c := Str(i);
...
end loop;
and in Ada 2012 there are generalized for loops
for c of Str loop
...
end loop;

Related

I m trying to make pipelined table function call but i m getting error in package body. Can u please make it correct

create or replace Type e_record AS Object(
e_uid number,
e_first_name varchar2(50) ,
e_last_name varchar2(50),
e_age number,
e_department varchar2(50),
e_designation varchar2(50),
e_salary number
);
create or replace Type e_record_table IS table Of e_record;
Create or replace package E_package
AS
function list_empDetails return e_record_table pipelined;
end E_package;
/
-------Package Body----------
Create or replace package body E_package
AS
Function list_empDetails return e_record_table pipelined
IS
e_ret e_record_table := e_record_table(null,null,null,null,null,null,null);
Begin
for x in(select e_uid,e_first_name,e_last_name,e_age,e_department,e_designation,e_salary into e_ret from Employee_details) Loop
pipe row(e_ret);
End Loop;
return;
end list_empDetails;
end E_package;
/
And the error i m getting is :
Error at line 8: PL/SQL: ORA-00947: not enough values
Error at line 9: PL/SQL: Statement ignored
Error at line 9: PLS-00382: expression is of wrong type
Error at line 8: PL/SQL: SQL Statement ignored
There are a couple of problems with your code.
Firstly, the not enough values error was because you are querying 7 columns and only providing one variable to put them in in the INTO clause. However, you don't use the INTO clause when looping over the results of a query that way, so the first thing you can do is to get rid of it. Your loop variable x will contain each row of data read from your Employee_details table, you don't need another variable for it.
The next thing to note is that you declared e_ret of type e_record_table. Your pipelined function returns e_record_table, but the type of each row you pipe out needs to be the type your table type contains, not the table type. The expression is of wrong type error is because you were attempting to pipe out a value of type e_record_table. Anyway, you're not selecting anything into e_ret any more, so you can just delete this local variable.
Your loop variable x contains each row read from the query. As written, it contains 7 values, so you could create an e_record row to pipe back using something like the following:
pipe row(e_record(x.e_uid, x.e_first_name, x.e_surname, x.e_age, ...));
However, that makes quite a lot of typing since you are repeating the list of columns in your query. You can avoid the duplicated typing by creating the record in the query and then piping that out:
for x in(select e_record(e_uid,e_first_name,e_last_name,e_age,e_department,e_designation,e_salary) as emp from Employee_details) Loop
pipe row(x.emp);
End Loop;
The full working function is below:
Function list_empDetails return e_record_table pipelined
IS
Begin
for x in(select e_record(e_uid,e_first_name,e_last_name,e_age,e_department,e_designation,e_salary) as emp from Employee_details) Loop
pipe row(x.emp);
End Loop;
return;
end list_empDetails;

Printing arrays elements using recursion

I am learning Ada programming language and I can admit that it's strongly typed. In this code, I am trying to ask the user to enter 4 integers and then by using recursion, print out these numbers. However, I am facing trouble writing a recursion subprogram for it and wonder if I can get any suggestions on how to write it correctly?
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
procedure Dugga is
Y: Natural;
type arr is
array (1..4) of Natural;
A:arr;
function Recurs(Item: in Natural) return Natural is
begin
if Item >= 1 then
return Get(Item ); --Error: Context requires function call, found procedure name
end if;
end Recurs;
begin
Y:=Recurs(arr); --Error: expected type "Standard.Integer. Found type: arr
end Dugga;
Warnings: Program_error may be raised at run time
"return" statement missing following this statement
Error: Missing argument for parameter "Item" in call to get
You appear to be confused about what a recursive subprogram is. A recursive subprogram calls itself unless a termination condition is reached.
If your subprogram calls itself before printing the array element the array elements will be printed in reverse order. If your subprogram calls itself after printing the array element the array elements will be printed in the order they are entered.
with Ada.Text_Io; use Ada.Text_IO;
procedure Main is
subtype Idx is integer range 1..4;
type Arr is array (Idx) of Integer;
procedure Print(Nums : Arr; Index : Idx) is
begin
Put (Nums(Index)'Image);
if Index = Nums'Last then
New_Line;
else
Print(Nums, Index + 1);
end if;
end Print;
A : Arr := (1, 2, 3, 4);
begin
Print(A, A'First);
end Main;
The program above declares an array type and creates an instance of that array type.
The procedure Print is a recursive procedure that prints the value of an array at the specified index value. The procedure then checks the index value. If the index value is the last index value for the array the procedure calls New_Line, otherwise it calls Print with second parameter being Index + 1.
For any recursive algorithm you need to have some sort of control logic (IF, CASE, etc.) that decides between calling the function again with a different input (and returning that result with the potential result of this call) or otherwise returning a final value.
A couple of things.
You say our end game is to use recursion to "print" the values, so your recursive operation only needs to be a procedure. It doesn't need to return anything, it just needs to print something each time it runs. One way to do this is print the first element of the array and then recall the procedure with just the remaining elements. If there are no elements, don't print anything and leave the procedure.
In order to do this, I would recommend passing in the whole array instead of a single value (like your version does). Each time you recursively call the function, you shorten the array until there is no array left. To do this though, you need to adjust your array type to be any potential size, not just 1..4, so that you can use that to end the recursion.
Start with making an array type:
type Natural_Array is array (Positive range <>) of Natural;
Now change the specification of your function to a procedure that uses that type.
procedure Recurse(Numbers: in Natural_Array);
The last part is implementing the recursion:
procedure Recurse(Numbers: in Natural_Array) is
begin
-- See if the array has any elements first
if Numbers'Length > 0 then
-- There are elements in the array, so print the
-- first one. This is why we use Numbers'First
-- as the index
Put_Line(Numbers(Numbers'First)'Image);
-- Now that we printed the first number, lets
-- only pass in the remaining elements in the array
-- here. This is where the "recursion" happens
-- as it calls this function again with a reduced
-- input set. Eventually we'll pass in an
-- empty array once the last element is printed
-- above.
Recurse(Numbers(Numbers'First+1..Numbers'Last));
else
-- Here there weren't any eleemnts in the array
-- left, so lets just end the recursion by
-- doing nothing
null;
end if;
end Recurse;
Here's the full compilable example:
with Ada.Text_IO; use Ada.Text_IO;
procedure Hello is
type Natural_Array is array (Positive range <>) of Natural;
procedure Recurse(Numbers: in Natural_Array) is
begin
-- See if the array has any elements first
if Numbers'Length > 0 then
-- There are elements in the array, so print the
-- first one. This is why we use Numbers'First
-- as the index
Put_Line(Numbers(Numbers'First)'Image);
-- Now that we printed the first number, lets
-- only pass in the remaining elements in the array
-- here. This is where the "recursion" happens
-- as it calls this function again with a reduced
-- input set. Eventually we'll pass in an
-- empty array once the last element is printed
-- above.
Recurse(Numbers(Numbers'First+1..Numbers'Last));
else
-- Here there weren't any eleemnts in the array
-- left, so lets just end the recursion by
-- doing nothing
null;
end if;
end Recurse;
A : Natural_Array(1..4) := (1,2,3,4);
begin
Recurse(A);
end Hello;

How do I read each word and store it to a variable to use?

I'm new to Ada. Syntax is throws me off. I have 6 years in Java and it's similar to this what we do in java but I quite can't get it working. I'm studying using learn.adacore.com.
with Ada.Text_IO; Use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
with Ada.Float_Text_IO; use Ada.Float_Text_IO;
procedure TextFile is
F : File_Type;
File_Name : constant String := "Store.txt";
begin
Open (F, In_File, File_Name);
while not End_Of_File (F) loop
Put_Line (Get_Line (F));
end loop;
Close (F);
end TextFile;
This is my text file called Store.txt
Code Department Name/Vendor Title ID Payrate
IL Sales John Sales_person 1378 25.46
If you don't mind portability between different Ada compilers, you can use package GNAT.String_Split to split the whole line as array of separated String values:
with Ada.Text_IO; use Ada.Text_IO;
with GNAT.String_Split; use GNAT.String_Split;
procedure TextFile is
File : File_Type;
Tokens : Slice_Set;
begin
Open (File, In_File, "Store.txt");
-- Skip the file header
Skip_Line (File);
-- Read the data
while not End_Of_File (File) loop
-- Split the line from the file on array which contains separated
-- words. Treat multiple spaces as a single separator (don't
-- create empty elements).
Create (Tokens, Get_Line (File), " ", Multiple);
-- Print each of the array's values
for I in 1 .. Slice_Count (Tokens) loop
Put_Line (Slice (Tokens, I));
end loop;
end loop;
Close (File);
end TextFile;

How to Process a Comma Delimited string in Batches in Oracle 11g

I have a comma delimited string which that exceeds the length of VARCHAR2(32767) in Oracle 11g and PL/SQL.
For example, a sample of my string might look like this:
my_field:= ‘ "BULL","ABCD","BEER","TOMM", "BULL1","ABCD1","BEER1","TOMM1", "BULL2","ABCD2","BEER2","TOMM2", "BULL3","ABCD3","BEER3","TOMM3", "BULL4","ABCD4","BEER4","TOMM4", "BULL5","ABCD5","BEER5","TOMM5"’;
As I am hitting the **Oracle error: ORA-06502: PL/SQL: numeric or value error: character string buffer too small**, what I would like to do is take my_field and process this in batches so that I do not exceed my limit of VARCHAR2(32767) in one.
So basically, grab the length of a string up to a specified length that ends with the “,” (comma) – process. Then from the “,” (comma) onwards, grab the next batch length up to a “,” (comma) and process, until I eventually hit the end of my_field, which doesn’t have a “,” (comma).
For example:
Batch 1: "BULL","ABCD","BEER","TOMM", - process row
Batch 2: "BULL1","ABCD1","BEER1","TOMM1", - process row
Batch 3: "BULL2","ABCD2","BEER2","TOMM2", - process row
Batch 4: "BULL3","ABCD3","BEER3","TOMM3", - process row
Batch 5: "BULL4","ABCD4","BEER4","TOMM4", - process row
Batch 6: "BULL5","ABCD5","BEER5","TOMM5" - process row
All processing finished.
How can I achieve the above in PL/SQL?
You can use INSTR and SUBSTR to break the input string into processable substrings, as in the following:
DECLARE
strCSV VARCHAR2(32767) := '"BULL","ABCD","BEER","TOMM", "BULL1","ABCD1","BEER1","TOMM1", "BULL2","ABCD2","BEER2","TOMM2", "BULL3","ABCD3","BEER3","TOMM3", "BULL4","ABCD4","BEER4","TOMM4", "BULL5","ABCD5","BEER5","TOMM5"';
strRow_csv VARCHAR2(32767);
nStart_pos NUMBER := 1;
nEnd_pos NUMBER;
BEGIN
LOOP
nEnd_pos := INSTR(strCSV, ',', nStart_pos, 4);
strRow_csv := RTRIM(TRIM(SUBSTR(strCSV, nStart_pos, CASE
WHEN nEnd_pos > 0 THEN
nEnd_pos
ELSE
LENGTH(strCSV)
END - nStart_pos+1)), ',');
DBMS_OUTPUT.PUT_LINE('strRow_csv=''' || strRow_csv || '''');
IF nEnd_pos = 0 THEN
EXIT;
END IF;
nStart_pos := nEnd_pos + 1;
END LOOP;
END;
If you have concerns about commas appearing inside the double-quoted values you may wish to consider using REGEXP_INSTR.
Share and enjoy.
You can solve this using regular expressions:
select word from
(with t as (select '"aaaa", "bbbb" , "cccc","dddd eeee", "ffff aaaa"' as txt from dual)
select DISTINCT REGEXP_SUBSTR (txt, '("[^"]+")|^([:space:]*)', 1, level) as word
from t
connect by level <= length(regexp_replace(txt,'("[^"]+")|^([:space:])*'))+1)
where word is not null

Reading in a record from a file in Ada?

Whenever I go to read in record from a file in Ada, I always get an error. The goal of the program is to read (from a file) an integer which is how many items needed to be recorded, in a last name consisting of (at most) 12 letters, a first name consisting of (at most) 12 letters, and a float value, then store those into a record.
This was from AdaGIDE:
record2.adb:32:04: invalid parameter list in call (use -gnatf for details)
My code:
with Ada.Text_IO, Ada.float_Text_IO, ada.Integer_Text_IO;
use Ada.Text_IO, Ada.float_Text_IO, ada.Integer_Text_IO;
procedure Record2 is
TYPE Testrec IS
record
test1 : string (1..12);
test2 : string (1..12);
test3 : float;
END RECORD;
T: Testrec;
Lt: Integer;
numitem: integer;
random1: Ada.Text_IO.File_Type;
begin -- Record2
Ada.Text_IO.Open(File => random1, Mode => Ada.Text_IO.In_File, Name => "info1.dat");
Get_Line(File => random1, Item => Testrec, Last => Lt);
Put(T.test1);
Put(T.Test2);
Put(T.Test3);
end Record2;
info1.dat's contents (no extra spaces or lines, just from "L" to "0":
LastName FirstName 4.00
My problems is the Get_Line, that I know. LastName is padded with spaces, filling the 12 characters, the same goes for FirstName. Then the float is taken for its value in general. What exactly am I doing wrong?
Basically, you're using Get_Line, which reads strings, to attempt to read an instance of a record.
Since this looks like a homework assignment (which is okay), I'll give you a hint:
Try reading the fields individually.
That's not enough to totally solve your problem, but it'll get you further, from which point you can try to work out the rest.

Resources