My problem is that I created a protected Safe_Printer; but when I use it's procedure, it messes up the Put function. Every time it prints to a new line. How could I prevent this? Basically, I would like to print my matrix with it in a readable format.
Here is my code:
with Ada.Text_IO;
use Ada.Text_IO;
procedure main is
type Matrix is array(integer range <>, integer range <>) of integer;
protected Safe_Printer is
procedure Put(S: String);
procedure Put(M: Matrix);
end Safe_Printer;
protected body Safe_Printer is
procedure Put(S: String) is
begin
for I in S'range loop
Put(S(I));
end loop;
New_Line;
end Put;
procedure Put(M:Matrix) is
begin
for I in m'range(1) loop
for j in m'range(2) loop
put(integer'image(m(i,j)) & " ");
end loop;
new_line(1);
end loop;
end Put;
end Safe_Printer;
m:Matrix := ((1,2,3),
(4,5,6));
begin
Safe_Printer.Put(m);
for I in m'range(1) loop
for j in m'range(2) loop
put(integer'image(m(i,j)) & " ");
end loop;
new_line(1);
end loop;
end main;
And my output is :
1
2
3
4
5
6
1 2 3
4 5 6
Because the 'Image attribute returns a String, your implementation of Put(M : Matrix) invokes Safe_Printer.Put(S : String) in its inner loop; each matrix entry then gets a New_Line. One solution is to call the language defined Put explicitly in Safe_Printer.Put(M : Matrix):
procedure Put(M : Matrix) is
begin
for I in M'range(1) loop
for j in M'range(2) loop
Ada.Text_IO.Put(integer'image(M(i,j)) & " ");
end loop;
New_Line;
end loop;
end Put;
As tested:
with Ada.Text_IO;
use Ada.Text_IO;
procedure main is
type Matrix is array(integer range <>, integer range <>) of integer;
protected Safe_Printer is
procedure Put(S : String);
procedure Put(M: Matrix);
end Safe_Printer;
protected body Safe_Printer is
procedure Put(S : String) is
begin
for I in S'range loop
Put(S(I));
end loop;
New_Line;
end Put;
procedure Put(M : Matrix) is
begin
for I in M'range(1) loop
for j in M'range(2) loop
Ada.Text_IO.Put(integer'image(M(i,j)) & " ");
end loop;
New_Line;
end loop;
end Put;
end Safe_Printer;
M : Matrix := ((1,2,3),
(4,5,6));
begin
Safe_Printer.Put(M);
end main;
Console:
$ ./main
1 2 3
4 5 6
Alternatively, as #SimonWright comments, you could "remove the New_Line in Safe_Printer.Put(String)." The approach you choose will depend on the desired effect offered in the Safe_Printer specification. Also consider limiting the scope of the use clause.
You should not call a 'potentially blocking operation' from within a protected object. The call to Text_IO.Put is a potentially blocking operation. See the Ada Reference Manual section 9.5.1. I am surprised your compiler didn't complain, actually.
One thing you can do in this situation is to use a task instead of a protected object.
For example:
with Ada.Text_IO;
procedure Main is
type Matrix is array (Integer range <>, Integer range <>) of Integer;
task Safe_Printer is
entry Put (S: in String);
entry Put (M: in Matrix);
end Safe_Printer;
task body Safe_Printer is
S: access String;
M: access Matrix;
begin
loop
select
accept Put (S: in String) do
Safe_Printer.S := new String (S'Range);
Safe_Printer.S.all := S;
end;
Ada.Text_IO.Put_Line (S.all);
or
accept Put (M: in Matrix) do
Safe_Printer.M := new Matrix (M'Range (1), M'Range (2));
Safe_Printer.M.all := M;
end;
for I in M'Range (1) loop
for J in M'Range (2) loop
Ada.Text_IO.Put (Integer'Image (M (I, J)) & " ");
end loop;
Ada.Text_IO.New_Line;
end loop;
or
terminate;
end select;
end loop;
end Safe_Printer;
M: Matrix := ((1,2,3),
(4,5,6));
begin
Safe_Printer.Put (M);
end Main;
An added advantage of this approach is that the calls to Text_IO.Put[_Line] are actually executed in parallel with the main task. A disadvantage is that (to be safe) the string or matrix to be printed is copied.
A better version of this code might use Unchecked_Deallocation or Ada.Containers.Indefinite_Holders to clean up after itself nicely.
Bonus question: What do you think the array bounds of the matrix M in the Main procedure are?
Related
In this code, I have built a list of three integers (5, 10, 15) and what I need help with is that I need to ask the user which of these elements he/she wants to remove and then only return the element/elements that are left. I need to write a subprogram for this and just by using recursion, I need to remove the elements that the user does not need.
Main program:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
with Linked_List; use Linked_List;
procedure Main_Remove is
I : Integer;
L : List_Type;
begin
Build_Test_List(L); -- builds a list of 3 integers (5 => 10 => 15)
Put(L);
Put("Which elements do you want to remove/delete ");
Get(I);
Remove(L, I);
Put(L);
end Main_Remove;
Package:
package Linked_List is
type List_Type is private;
procedure Put(Item : in List_Type);
procedure Build_Test_List(Item : out List_Type;
Base : in Integer := 5);
private
type E_Type;
type List_Type is access E_Type;
type E_Type is
record
Data : Integer;
Next : List_Type;
end record;
end Linked_List;
Pakage body:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
with Ada.Unchecked_Deallocation;
package body Linked_List is
procedure Put(Item : in List_Type) is
P : List_Type := Item;
begin
Put("Listan: ");
while P /= null loop
if P /= Item then
Put(" -> ");
end if;
Put(P.Data, Width => 0);
P := P.Next;
end loop;
New_Line;
end Put;
procedure Insert_First(L : in out List_Type;
D : in Integer) is
begin
L := new E_Type'(Data => D, Next => L);
end Insert_First;
procedure Build_Test_List(Item : out List_Type;
Base : in Integer := 5) is
begin
for I in reverse 1..3 loop
Insert_First(Item, Base * I);
end loop;
end Build_Test_List;
end Linked_List;
Something like this will do, with reservations: specifically, there’s a memory leak.
procedure Remove (L : in out List_Type; Item : Integer) is
begin
The recursion has to be stopped when the list is empty.
if L = null then
return;
end if;
The list isn’t empty. What to do next depends on whether the current list element contains the value we’re looking for, or not.
if L.Data = Item then
This item needs to be removed from the list. Do this by altering the original pointer (which came from the list head, or the previous element) to skip over this element, and then process that element.
This is the point at which the memory leak has occurred. Obviously the cell being pointed to by the initial L needs to be freed, but you’re going to have to be careful about the order of operations.
L := L.Next;
Remove (L, Item);
else
The item stays in the list, go on to the next element.
Remove (L.Next, Item);
end if;
end Remove;
This is my first question on StackOverflow and I'm self taught so please be gentle.
My goal here is to be able to bulk collect headers/values from a dynamic query/cursor in a generated package via
SELECT *
BULK COLLECT INTO l_cur_val
FROM TABLE (CUSTOM.GET_REF_VAL(l_cursor));
I have this working successfully for column headers in a similar block of code to GET_REF_VAL (simply RETURN l_col_head before entering the /* COLUMN VALUES */ section).
The errors are coming when I'm trying to assign the return value of DBMS_SQL.COLUMN_VALUE into my t_col_val UDT. (Type definitions are in comments)
TYPES
CREATE OR REPLACE TYPE CUSTOM.r_col_val IS OBJECT (l_col_val VARCHAR2(250 byte));
CREATE OR REPLACE TYPE CUSTOM.t_col_val IS TABLE OF CUSTOM.r_col_val;
ERRORS
--l_val(n) := r_col_val(l_dum_val); --returns: ORA-06533: Subscript beyond count
--l_val(n) := l_dum_val; --returns: PLS-00382: expression is of wrong type
Table return function GET_REF_VAL
CREATE OR REPLACE FUNCTION
CUSTOM.GET_REF_VAL
(
p_cursor IN SYS_REFCURSOR
)
RETURN t_col_val
IS
l_val t_col_val := t_col_val();
l_col t_col_head := t_col_head();
n INTEGER := 0;
l_cursor SYS_REFCURSOR := p_cursor;
l_cursor_id INTEGER;
l_dummy INTEGER;
l_col_cnt INTEGER;
l_tab_rec DBMS_SQL.DESC_TAB2;
l_dum_val VARCHAR2(250);
BEGIN
l_cursor_id := DBMS_SQL.TO_CURSOR_NUMBER(l_cursor);
DBMS_SQL.DESCRIBE_COLUMNS2(l_cursor_id, l_col_cnt, l_tab_rec);
/* COLUMN HEADERS */
FOR r IN 1..l_col_cnt
LOOP
l_col.extend;
n := n + 1;
l_col(n) := r_col_head(l_tab_rec(r).col_name);
DBMS_SQL.DEFINE_COLUMN(l_cursor_id, r, l_dum_val, 4000);
END LOOP;
/* COLUMN VALUES */
LOOP
IF DBMS_SQL.FETCH_ROWS(l_cursor_id)> 0 THEN
FOR i IN 1 .. l_col_cnt
LOOP
l_val.extend;
n := n + 1;
DBMS_SQL.COLUMN_VALUE(l_cursor_id, i, l_dum_val);
DBMS_OUTPUT.PUT_LINE(l_dum_val); -- This return l_dum_val with no issues
--l_val(n) := r_col_val(l_dum_val); -- ORA-06533: Subscript beyond count
--l_val(n) := l_dum_val; --PLS-00382: expression is of wrong type
END LOOP;
ELSE
EXIT;
END IF;
END LOOP;
DBMS_SQL.CLOSE_CURSOR(l_cursor_id);
RETURN l_val;
END;
/
Execution block
DECLARE
l_sql_stmt VARCHAR(10000) :=
q'!
SELECT
SYS_CONTEXT('USERENV','OS_USER') AS OS_USER ,
SYS_CONTEXT('USERENV','SESSION_USER') AS SESSION_USER,
SYS_CONTEXT('USERENV','ISDBA') AS ISDBA,
SYS_CONTEXT('USERENV','SID') AS SID,
SYS_CONTEXT('USERENV','CURRENT_SQL') AS CURRENT_SQL,
SYS_CONTEXT('USERENV','DB_NAME') AS DB_NAME,
SYS_CONTEXT('USERENV','HOST') AS HOST,
SYS_CONTEXT('USERENV','IP_ADDRESS') AS IP_ADDRESS,
SYS_CONTEXT('USERENV','SERVICE_NAME') AS SERVICE_NAME
FROM
DUAL
!';
l_cursor SYS_REFCURSOR;
l_cursor_id INTEGER;
l_dummy VARCHAR2(50);
TYPE t_cur_head IS TABLE OF VARCHAR2(250) INDEX BY BINARY_INTEGER;
l_cur_head t_cur_head;
TYPE t_cur_val IS TABLE OF VARCHAR2(250) INDEX BY BINARY_INTEGER;
l_cur_val t_cur_val;
BEGIN
l_cursor := CUSTOM.GET_REF_CUR(l_sql_stmt);
IF l_cursor%ISOPEN
THEN
/* Header fetch works fine */
/*
SELECT *
BULK COLLECT INTO l_cur_head
FROM TABLE (CUSTOM.GET_REF_HEAD(l_cursor));
FOR i IN 1 .. l_cur_head.COUNT
LOOP
DBMS_OUTPUT.PUT_LINE(l_cur_head(i));
END LOOP;
*/
/* Values fetch fails */
SELECT *
BULK COLLECT INTO l_cur_val
FROM TABLE (CUSTOM.GET_REF_VAL(l_cursor));
FOR i IN 1 .. l_cur_val.COUNT
LOOP
DBMS_OUTPUT.PUT_LINE(l_cur_val(i));
END LOOP;
END IF;
END;
So I guess in summary what I want to know is
a) How to handle the return value of dbms_sql.column_value using a user defined type
b) How insert a VARCHAR2 value (l_dum_val) into a UDT object with VARCHAR2 records (l_col_val)
c) Any other obvious errors/bad practices in the code?
Thank you for your time an patience.
The first of your commented-out lines:
--l_val(n) := r_col_val(l_dum_val); -- ORA-06533: Subscript beyond count
gets that error because you are not resetting n to zero before the second loop. You don't really need that counter variable at all though, you can do use l_val.count instead (in both loops).
The second of your commented-out lines:
--l_val(n) := l_dum_val; --PLS-00382: expression is of wrong type
gets that error because the l_val(n) is pointing to an object, which has a string attribute; it isn't pointing directly to a string. So you can assign a new object via its constructor; which is what the first version was trying to do, but it should be:
l_val(l_val.count) := r_col_val(l_dum_val);
Once that object exists you can assign the attribute directly with:
l_val(some_index).l_col_val := r_col_val(l_dum_val);
but you have to create an object before you can access its attributes, and as you only have a default constructor, that probably isn't going to be much use to you in this case.
So with those changes (and some indentation, and refactoring slightly to get rid of the else) this now works:
CREATE OR REPLACE FUNCTION
GET_REF_VAL
(
p_cursor IN SYS_REFCURSOR
)
RETURN t_col_val
IS
l_val t_col_val := t_col_val();
l_col t_col_head := t_col_head();
l_cursor SYS_REFCURSOR := p_cursor;
l_cursor_id INTEGER;
l_dummy INTEGER;
l_col_cnt INTEGER;
l_tab_rec DBMS_SQL.DESC_TAB2;
l_dum_val VARCHAR2(250);
BEGIN
l_cursor_id := DBMS_SQL.TO_CURSOR_NUMBER(l_cursor);
DBMS_SQL.DESCRIBE_COLUMNS2(l_cursor_id, l_col_cnt, l_tab_rec);
/* COLUMN HEADERS */
FOR r IN 1..l_col_cnt
LOOP
l_col.extend;
l_col(l_col.count) := r_col_head(l_tab_rec(r).col_name);
DBMS_SQL.DEFINE_COLUMN(l_cursor_id, r, l_dum_val, 4000);
END LOOP;
/* COLUMN VALUES */
LOOP
IF DBMS_SQL.FETCH_ROWS(l_cursor_id) = 0 THEN
EXIT;
END IF;
FOR i IN 1 .. l_col_cnt
LOOP
l_val.extend;
DBMS_SQL.COLUMN_VALUE(l_cursor_id, i, l_dum_val);
DBMS_OUTPUT.PUT_LINE(l_dum_val);
l_val(l_val.count) := r_col_val(l_dum_val);
END LOOP;
END LOOP;
DBMS_SQL.CLOSE_CURSOR(l_cursor_id);
RETURN l_val;
END;
/
db<>fiddle
Your code suggests you have a separate function to get the headers, so you're duplicating code. You could simplify into one procedure with two out variables instead:
CREATE OR REPLACE PROCEDURE
GET_REF_HEAD_AND_VAL
(
p_cursor IN OUT SYS_REFCURSOR,
p_col OUT SYS.odcivarchar2list,
p_val OUT SYS.odcivarchar2list
)
IS
l_cursor_id INTEGER;
l_col_cnt INTEGER;
l_tab_rec DBMS_SQL.DESC_TAB3;
l_value VARCHAR2(250 byte);
BEGIN
l_cursor_id := DBMS_SQL.TO_CURSOR_NUMBER(p_cursor);
DBMS_SQL.DESCRIBE_COLUMNS3(l_cursor_id, l_col_cnt, l_tab_rec);
/* COLUMN HEADERS */
p_col := SYS.odcivarchar2list();
FOR r IN 1..l_col_cnt
LOOP
p_col.extend;
p_col(p_col.count) := l_tab_rec(r).col_name;
DBMS_SQL.DEFINE_COLUMN(l_cursor_id, r, l_value, 250);
END LOOP;
/* COLUMN VALUES */
p_val := SYS.odcivarchar2list();
LOOP
IF DBMS_SQL.FETCH_ROWS(l_cursor_id) = 0 THEN
EXIT;
END IF;
FOR i IN 1 .. l_col_cnt
LOOP
p_val.extend;
DBMS_SQL.COLUMN_VALUE(l_cursor_id, i, l_value);
--DBMS_OUTPUT.PUT_LINE(l_dum_val);
p_val(p_val.count) := l_value;
END LOOP;
END LOOP;
DBMS_SQL.CLOSE_CURSOR(l_cursor_id);
END;
/
This is using a built-in collection type rather than creating your own object/table types (though you could still create your own collection type; it doesn't need to used objects though).
db<>fiddle
I was wondering if any of you could answer a quick question for me. I am currently working with records right now and in my program I need it to understand what the line of a file that i'm importing contains. My problem lies in the fact that I don't know how to "split" the line into actual variables. For example the line is
22134.09 Kia Bernice
How do I make the program know that the first part, 22134.09 is the variable price, Kia is the variable company and Bernice is the variable model, and then sort them all into a record?
Such as
type PriceCompModel is record
price : Float range 1.0..99999.99;
company : String (1..CompNameLength);
Model : String (1..ModelNameLength);
Thanks.
edited code
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 Testexercise is
type Inventory is Record
CarPrice : Float := 0.0;
CarType : String (1..40);
-- CarType will include both the model and company
end record;
InventoryItem : Inventory;
ImportedFile : File_Type;
FileName : String := "Cars.txt";
WordsFromFile : String(1..40);
LengthofWords : Integer ;
PriceofCar : Float := 0.0;
LoopCount : Integer := 1;
type Cars is array (1..12) of Inventory;
begin
Open(File => ImportedFile, Mode => In_File, Name => FileName);
--for I in 1..12 loop
while LoopCount /= 12 loop
Get(File => ImportedFile, Item => InventoryItem.CarPrice);
Get_Line(File => ImportedFile, Item => WordsFromFile, Last=> LengthofWords);
Put (Integer(InventoryItem.CarPrice),1);
Put (WordsFromFile(1..LengthofWords));
New_Line;
LoopCount := LoopCount + 1;
InventoryItem.CarType := WordsFromFile;
end loop;
close(ImportedFile);
end Testexercise;
So i tried doing this within the loop
for I in 1..12 loop
Cars := Inventory;
end loop;
This ended up working for me after i set
Car : Cars;
for I in 1..12 loop
Car(I) := Inventory;
end loop;
You have many factors to consider when defining the record to contain your information.
It will be useful to create a named subtype of float, or a named floating point type so that the I/O routines can check the input values for the price component.
Fields of type String must be constrained to a predefined size. This means that all "company" strings must be the same size, and all "model" strings must be the same size, although model strings may be a different length than company strings. If the names of companies and/or models may vary then you should consider using either bounded strings (Ada Language Reference Manual section A.4.4) or unbounded strings (Ada Language Reference Manual section A.4.5).
If the strings of the input file are fixed in size you can use Ada.Text_IO.Text_Streams (Ada Language Reference Manual section A.12.2) to read each field of the record. If the strings can be different sizes then you will need to read and parse each field manually using Ada.Text_IO.
-- Read record data from a file
with Ada.Text_Io; use Ada.Text_IO;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
with Ada.Strings.Fixed; use Ada.Strings.Fixed;
procedure read_record is
type Prices is delta 0.01 digits 7 range 0.0..99999.99;
type Auto_Inventory is record
Price : Prices := 0.0;
Company : Unbounded_String := Null_Unbounded_String;
Model : Unbounded_String := Null_Unbounded_String;
end record;
package AI_IO is new Ada.Text_IO.Decimal_IO(Prices);
use AI_IO;
Inventory_Item : Auto_Inventory;
The_File : File_Type;
File_Name : String := "inventory.txt";
Inpt_Str : String(1..1024);
Length : Natural;
Start, Finis : Positive;
begin
Open(File => The_File,
Mode => In_File,
Name => File_Name);
Get(File => The_File,
Item => Inventory_Item.Price);
Get_Line(File => The_File,
Item => Inpt_Str,
Last => Length);
Close(The_File);
Start := Index_Non_Blank(Source => Inpt_Str(1..Length));
Finis := Start;
while Finis < Length and then Inpt_Str(Finis) /= ' ' loop
Finis := Finis + 1;
end loop;
Inventory_Item.Company := To_Unbounded_String(Inpt_Str(Start..Finis));
Start := Index_Non_Blank(Inpt_Str(Finis + 1..Length));
Inventory_Item.Model := To_Unbounded_String(Inpt_Str(Start..Length));
Put_Line("Price: " & Prices'Image(Inventory_Item.Price));
Put_Line("Company: " & To_String(Inventory_Item.Company));
Put_Line("Model: " & To_String(Inventory_Item.Model));
end read_record;
If you want to read a file containing many records you need to collect the information in some kind of container. The following example uses a Vector from the generic package Ada.Containers.Vectors.
-- Auto Inventory Package specification
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Containers.Vectors;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
package Auto_Inventory is
type Prices is delta 0.01 digits 7 range 0.0..99999.99;
type Automobile is tagged private;
procedure Print(Item : Automobile);
function Set return Automobile;
function Get_Price(Item : Automobile) return Prices;
function Get_Company(Item : Automobile) return String;
function Get_Model(Item : Automobile) return String;
type Inventory is tagged private;
procedure Read(Item : out Inventory; File : File_Type) with
Pre => Mode(File) = In_File;
procedure Write(Item : in Inventory; File : File_type) with
Pre => Mode(File) = Out_File;
procedure Print(Item : Inventory);
private
type Automobile is tagged record
Price : Prices := 0.0;
Company : Unbounded_String := Null_Unbounded_String;
Model : Unbounded_String := Null_Unbounded_String;
end record;
package Auto_Vect is new
Ada.Containers.Vectors(Index_Type => Positive,
Element_Type => Automobile);
use Auto_Vect;
type Inventory is tagged record
List : Vector;
end record;
end Auto_Inventory;
The body for this package is:
with Ada.Strings.Fixed; use Ada.Strings.Fixed;
package body Auto_Inventory is
package Prices_IO is new Ada.Text_IO.Decimal_IO(Prices);
use Prices_IO;
-----------
-- Print --
-----------
procedure Print (Item : Automobile) is
use Prices_Io;
begin
Put_Line("Price : " & Prices'Image(Item.Price));
Put_Line("Company: " & To_string(Item.Company));
Put_Line("Model : " & To_String(Item.Model));
New_Line;
end Print;
---------
-- Set --
---------
function Set return Automobile is
Temp : Automobile;
Inpt_Str : String(1..1024);
Length : Natural;
begin
Put("Enter the automobile price: ");
Get(Item => Temp.Price);
Put("Enter the automobile company: ");
Get_Line(Item => Inpt_Str, Last => Length);
Temp.Company := To_Unbounded_String(Inpt_Str(1..Length));
Put("Enter the automobile model: ");
Get_Line(Item => Inpt_Str, Last => Length);
Temp.Model := To_Unbounded_String(Inpt_Str(1..Length));
return Temp;
end Set;
---------------
-- Get_Price --
---------------
function Get_Price (Item : Automobile) return Prices is
begin
return Item.Price;
end Get_Price;
-----------------
-- Get_Company --
-----------------
function Get_Company (Item : Automobile) return String is
begin
return To_String(Item.Company);
end Get_Company;
---------------
-- Get_Model --
---------------
function Get_Model (Item : Automobile) return String is
begin
return To_String(Item.Model);
end Get_Model;
----------
-- Read --
----------
procedure Read (Item : out Inventory;
File : File_Type) is
Temp : Inventory;
Auto : Automobile;
Inpt_Str : String(1..1024);
Length : Natural;
Start, Finis : Positive;
begin
while not End_Of_File(File) loop
Get(File => File, Item => Auto.Price);
Get_Line(File => File, Item => Inpt_str, Last => Length);
Start := Index_Non_Blank(Inpt_Str(1..Length));
Finis := Start;
while Finis < Length and then Inpt_Str(Finis) /= ' ' loop
Finis := Finis + 1;
end loop;
Auto.Company := To_Unbounded_String(Inpt_Str(Start..Finis - 1));
Start := Index_Non_Blank(Inpt_Str(Finis..Length));
Auto.Model := To_Unbounded_String(Inpt_Str(Start..Length));
Temp.List.Append(Auto);
end loop;
Item := Temp;
end Read;
-----------
-- Write --
-----------
procedure Write (Item : in Inventory;
File : File_type) is
begin
for Element of Item.List loop
Put(File => File, Item => Prices'Image(Element.Price) &
" " & To_String(Element.Company) & " " &
To_String(Element.Model));
New_Line;
end loop;
end Write;
-----------
-- Print --
-----------
procedure Print (Item : Inventory) is
begin
for Element of Item.List loop
Element.Print;
end loop;
end Print;
end Auto_Inventory;
An example of a main procedure to exercise this package:
------------------------------------------------------------------
-- Read a file of many records --
------------------------------------------------------------------
with Auto_Inventory; use Auto_Inventory;
with Ada.Text_IO; use Ada.Text_IO;
procedure read_file is
The_Inventory : Inventory;
The_File : File_Type;
File_Name : String := "inventory.txt";
begin
Open(File => The_File,
Mode => In_File,
Name => File_Name);
The_Inventory.Read(The_File);
Close(The_File);
The_Inventory.Print;
end read_file;
An example input file for this program is:
22134.09 Kia Bernice
12201.15 Nissan Versa
22349.99 Chevrolet Cruse
It is not clear for me what language you are using .However the concept is to deal with each line from the file alone then process it with a function that do the token-zing or splitting depending on the language you use and save each token in a variable depending on how the function you are using will save the tokens
for example:
In java there is a class
StringTokenizer(String str, String delim)
StringTokenizer st = new StringTokenizer("this is a test", "$a; ");
while (st.hasMoreTokens()) {
System.out.println(st.nextToken());
}
delim in your case is space so use the format
StringTokenizer st = new StringTokenizer("this is a test");
or
String line = reader.readLine();
String[] tokens = line.split("\\s");
note that you need to save the line you read in a string so you can use those functions in java then access each token from the array
String price = tokens[1] and so on
for other languages please find the following resources:
In c https://www.tutorialspoint.com/c_standard_library/c_function_strtok.htm
In pyhton https://www.tutorialspoint.com/python/string_split.htm
I can't figure out why the code below is failing. I have my own version of the same script which returns the same error. I can't figure out in either case why the error persists.
This is the error:
raised CONSTRAINT_ERROR : main2.adb:32 index check failed
which is this line:
temp(i) := a(loop_high);
Anyone know what may be causing this?
with Text_IO;
with Ada.Integer_Text_IO;
procedure main2 is
use Text_IO;
use Ada.Integer_Text_IO;
type int_array is array(1..5) of integer;
tosort:int_array;
procedure merge (a:in out int_array; low,mid,high:in integer) is
temp: int_array;
choose1: boolean;
loop_low,loop_high:integer;
begin
loop_low:=low;
loop_high:=high;
for i in low..high loop
if (loop_low>mid) then choose1:=false;
elsif (loop_high>high) then choose1:=true;
else choose1:= a(loop_low)<a(loop_high);
end if; -- choose which side
if choose1 then -- choose from low side
temp(i):=a(loop_low);
loop_low:=loop_low+1;
else
temp(i):=a(loop_high); -- choose from high side
loop_high:=loop_high+1;
end if;
end loop;
a:=temp;
end merge;
procedure mergesort(a: in out int_array;low,high:integer) is
mid:integer;
begin
if low<high then
mid:= (high+low)/2;
mergesort(a,low,mid);
mergesort(a,mid+1,high);
merge(a,low,mid,high);
end if;
end mergesort;
begin
tosort := (171, 201, 397, 10, -381);
mergesort(tosort,1,5);
end main2;
I recommend compiling with -gnateE, which will give you more information for compiler-generated exceptions. In this specific case, it should tell you which value is outside of which range
How to read array of n elements dynamically and display the elements in plsql.
Below is my code (i am new to plsql programming)
set serveroutput on
set verify on
declare
type myarray is table of number index by binary_integer;
x myarray;
i pls_integer;
n number;
begin
-- populate array
dbms_output.put_line('Enter number of array elements');
n := &n;
dbms_output.put_line('Enter elements one by one');
for i in 1..n loop
dbms_output.get_lines(&&x(i),n);
end loop;
i :=0;
-- print array
loop
i := i + 1;
begin
dbms_output.put_line(x(i));
exception
when no_data_found then exit;
end;
end loop;
end;
/
quit;
I stumble into the same issue and wanted to try something.
You could do this with a brutal approach that consists in using you shell to call the client (i.e. sqlplus) any time you want to add a value to your array. I wrote a little package that stores the values of the array in a table. Then you can change the way you store the values, but principle would remain the same:
-- this package uses "execute immediate" statements for everything
-- because it assumes the temporary table T_MY_ARRAY can be non-existent.
create or replace package my_array
authid current_user
is
TYPE a_TBL_Number IS TABLE OF Number INDEX BY BINARY_INTEGER;
procedure init;
procedure add(v in number);
procedure display;
function to_var return a_TBL_Number;
end my_array;
/
create or replace package body my_array
is
procedure init is
-- create table if needed, then make sure its empty;
begin
begin
execute immediate 'create table T_MY_ARRAY(c1 number)';
exception when others then
-- dbms_output.put_line(sqlerrm);
-- we're never sure the temp. table already exists. So create it and catch if existing.
null;
end;
execute immediate 'truncate table T_MY_ARRAY';
end init;
procedure add(v in number) is
-- add new value
begin
execute immediate 'insert into T_MY_ARRAY (c1) values ('||v||')';
end add;
function to_var return a_TBL_Number is
-- hand out an array with the values
t_TBL_n a_TBL_Number;
begin
execute immediate 'select c1 from T_MY_ARRAY ' bulk collect into t_TBL_n;
return t_TBL_n;
end to_var;
procedure display is
t_TBL_n a_TBL_Number;
begin
t_TBL_n:=my_array.to_var();
for i in 1..t_TBL_n.count loop
dbms_output.put_line(t_TBL_n(i));
end loop;
end display;
end my_array;
/
Then here is how you could call this from a shell (here it is for an old ksh):
read SQL_CONNECT?"Enter your SQL connection string:"
read n?"Enter number of array elements:"
# first initialize the array:
sqlplus -s ${SQL_CONNECT} <<_EOF
set serveroutput off
set termout off
set feedback off
begin
my_array.init;
end;
/
_EOF
# then loop on the given number of elements
typeset -i10 i=0
while [[ ${i} -lt ${n} ]]
do
i=i+1
echo iter:${i}
read MY_VALUE?"Enter elements one by one:"
sqlplus -s ${SQL_CONNECT} <<_EOF
set serveroutput off
set termout off
set feedback off
begin
my_array.add(${MY_VALUE});
end;
/
commit;
_EOF
done
# at the end, use stored values to display result:
sqlplus -s ${SQL_CONNECT} <<_EOF
set serveroutput on
set feedback off
begin
dbms_output.put_line('---------------');
dbms_output.put_line('your input was:');
my_array.display;
end;
/
_EOF