Procedure to open, write and append in Ada
This question is a follow-up of the post at
Ada file operation: instantiation and exception
about writing to files in Ada.
I chose to place this question in a separate post so that it'll become visible to more people as I already accepted an answer on a slightly different issue (which was on exceptions in file handling) in that aforementioned post.
WITH Ada.Sequential_IO;
WITH Ada.Float_Text_IO;
PROCEDURE TEST is
package Seq_Float_IO is new Ada.Sequential_IO (Element_Type => Float);
X_File : Seq_Float_IO.File_Type;
File_Name : String;
procedure Open_Data(File : in out Seq_Float_IO.File_Type;
Name : in String) is
BEGIN
begin
Seq_Float_IO.Open (
File => File,
Mode => Seq_Float_IO.Append_File,
Name => File_Name );
exception
when Seq_Float_IO.Name_Error =>
Seq_Float_IO.Create (
File => File,
Mode => Seq_Float_IO.Out_File,
Name => File_Name);
end;
END Open_Data;
x : CONSTANT Float := 2.0;
BEGIN --main program
Open_Data(X_File, "xvalues.dat");
Seq_Float_IO.Write(File => X_File,Item =>开发者_StackOverflow中文版; x);
Seq_Float_IO.Close(File => X_File);
END TEST;
On compiling the above I get an error as follows:
- X_File : Seq_Float_IO.File_Type;
- File_Name : String;
|
unconstrained subtype not allowed (need initialization) provide initial value or explicit array bounds
- File_Name : String;
|
I don't know 2 things:
- I have File_Name : String; as I want to be able to write to different files. So I want a general string and not something like:
File_Name : CONSTANT String := "one_File_Only.dat"
- Would it be better to save the procedure Open_Data in separate ads and adb (for the body) files?
Thanks a lot...
NEW...
I've modified the code as follows:
WITH Ada.Sequential_IO;
PROCEDURE TEST1 is
package Seq_Float_IO is new Ada.Sequential_IO (Element_Type => Float);
X_File, Y_File : Seq_Float_IO.File_Type;
Name_X : CONSTANT String := "domainvalues.dat";
Name_Y : CONSTANT String := "ordinatevalues.dat";
procedure Open_Data(File : in out Seq_Float_IO.File_Type; Name : in String) is
BEGIN
begin
Seq_Float_IO.Open (
File => File,
Mode => Seq_Float_IO.Append_File,
Name => Name_X );
exception
when Seq_Float_IO.Name_Error =>
Seq_Float_IO.Create (
File => File,
Mode => Seq_Float_IO.Out_File,
Name => Name_X);
end;
END Open_Data;
x : CONSTANT Float := 2.0;
BEGIN --main program
Open_Data(File => X_File, Name => Name_X);
Seq_Float_IO.Write(File => X_File, Item => x);
Seq_Float_IO.Close(File => X_File);
Open_Data(File => Y_File, Name => Name_Y);
Seq_Float_IO.Write(File => Y_File, Item => x);
Seq_Float_IO.Close(File => Y_File);
END TEST1;
As you see I have
Seq_Float_IO.Open (
File => File,
Mode => Seq_Float_IO.Append_File,
Name => Name_X );
I have put Name_X as the parameter that Name is taking but this is not right as I should be able to pass in a general name which can be either Name_X or Name_Y. Sorry guys, I can't figure out what to put here.
I would much appreciate your help. Thanks
The thing about plain String
in Ada is that a particular string, like your File_Name
, has to be fixed-length; but different Strings can be of different length.
You can write
S1 : String := "1234";
S2 : String := "12345";
in which case S1 is of length 4, and assignments to it have to be of length 4. You can write
S1 := "abcd";
but if you try to write
S1 := "pqrst";
or
S1 := S2;
you will get a Constraint_Error
.
In the case of String parameters to subprograms, like your Open_Data
, the String parameter Name
takes on the length -- and of course the value! of the actual parameter in the call. So you can say
Open_Data (X_File, "x.dat");
Open_Data (Y_File, "a_very_long_name.dat");
You were having problems earlier with
procedure Open_Data(File : in out Seq_Float_IO.File_Type;
Name : in String) is
begin
Seq_Float_IO.Open (File => File,
Mode => Seq_Float_IO.Append_File,
Name => ????);
I'm reluctant to just tell you the answer, so -- consider the File => File
part. The first File
is the name of the formal parameter of Seq_Float_IO.Open
and the second File
is what is to be passed, in this case Open_Data
's File
parameter.
It might help if I point out that I could have written the calls above as
Open_Data (File => X_File, Name => "x.dat");
Open_Data (File => Y_File, Name => "a_very_long_name.dat");
@Simon Wright's answer is correct, and you may find it helpful to compare his answer to the second one I wrote earlier. Note that if you had
Name_X : constant String := "domainvalues.dat";
Name_Y : constant String := "ordinatevalues.dat";
Either string, Name_X
or Name_Y
, could be used as the actual Name
parameter to Open_Data
. The formal parameter, Name
, is of type String
. String
is unconstrained, and it may be any (implementation-defined) maximum length. In contrast, Name_X
and Name_Y
each have a fixed length determined by their initial assignment.
Addendum: You wrote a subprogram with a formal parameter (Name
) of type String
, having this signature
procedure Open_Data(
File : in out Seq_Float_IO.File_Type;
Name : in String) is ...
In the implementation, you want to forward to Open
the String
you received as the actual parameter (Name
), not the name of a global constant (Name_X
).
Seq_Float_IO.Open (
File => File,
Mode => Seq_Float_IO.Append_File,
Name => Name );
精彩评论