I have some problem regarding writing String variable to a file.
The problem is that I have to specify the exact length of that String. Or else the output file will just contain some scrap values. I wonder if this can be solved somehow without the need to tell the length of the String before hand?
I know that my Get() procedure stores the Length of that variable, and I just could return it to the main program. However I want to write my program so it will do all the read from the input file first before I begin writing to the out file.
with Ada.Text_Io, Ada.Integer_Text_Io;
use Ada.Text_Io,Ada.Integer_Text_Io;
procedure Uppgift is
type Bil_Register is
record
Namn : String(1..50);
Adress : String(1..50);
Post : String(1..50);
Reg : String(1..6);
end record;
Infil : File_Type;
Utfil : File_Type;
L, I : Integer;
Br : Bil_Register;
procedure Get(F : in out File_Type; Br : out Bil_Register) is
Length : Integer;
begin
Get_Line(F, Br.Namn, Length);
end;
begin
Open(Infil, In_File, "register.txt");
Create(Utfil, Out_File, "test.txt");
Get(Infil, Br);
Put_Line(Utfil, Br.Namn);
Close(Infil);
Close(Utfil);
end Uppgift;
-
EDIT (2011.08.20)
This seems to be an issue with Unix based OS only. When using Windows you don't have to be absolute with string size when you print it out to file or screen
Well, the length of the valid part of a string has to be tracked somewhere.
You could maintain the valid length of each of your record's string fields as a separate field:
Namn : String (1..50);
Namn_Length : Natural;
You could define your own variable string type package, or use a pre-existing one such as Variable_Length. E.g.
Namn : Variable_Length.Variable_String(50);
You could use Unbounded_String for the fields and variables:
Namn : Unbounded_String;
And Ada.Text_IO.Unbounded_IO for I/O:
with Ada.Strings.Unbounded;
use Ada.Strings.Unbounded;
with Ada.Text_IO.Unbounded_IO;
procedure UTIO_Demo is
use Ada.Text_IO;
F : Ada.Text_IO.File_Type;
Data : Unbounded_String := To_Unbounded_String("Output by Unbounded_IO");
begin
Create(F, Ada.Text_IO.Out_File, "utio.tst");
Unbounded_IO.Put_Line(F, Data);
Close(F);
end UTIO_Demo;
If you'd prefer not to use the Unbounded_IO package, use To_String and To_Unbounded_String to convert back and forth between Unbounded_String values and the strings being read and written via Text_IO.
I would personally just use Unbounded_String, as suggested by Marc C, but if you want to avoid that, you could do something like this:
with Ada.Text_IO;
with Ada.Containers.Indefinite_Doubly_Linked_Lists;
use Ada.Text_IO;
use Ada.Containers;
procedure Uppgift is
type Bil_Register (Namn_Length : Natural) is
record
Namn : String (1 .. Namn_Length);
-- Other components removed for brevity.
end record;
package BR_Container is new Indefinite_Doubly_Linked_Lists (Bil_Register);
use BR_Container;
BR_List : BR_Container.List;
BR_List_Cursor : BR_Container.Cursor;
Buffer : String (1 .. 100);
Length : Natural;
Register_File : File_Type;
Test_File : File_Type;
begin
-- First we read the contents of register.txt and add all the data to
-- our list of Bil_Register objects.
Open (File => Register_File,
Mode => In_File,
Name => "register.txt");
while not End_Of_File (File => Register_File) loop
Get_Line (File => Register_File,
Item => Buffer,
Last => Length);
declare
BR : Bil_Register
(Namn_Length => Length);
begin
BR.Namn := Buffer (1 .. Length);
BR_List.Append (New_Item => BR);
end;
end loop;
Close (File => Register_File);
-- Then we output the contents of our list of Bil_Register objects to
-- test.txt
Create (File => Test_File,
Mode => Out_File,
Name => "test.txt");
BR_List_Cursor := BR_List.First;
while Has_Element (Position => BR_List_Cursor) loop
Put_Line (File => Test_File,
Item => Element (Position => BR_List_Cursor).Namn);
Next (Position => BR_List_Cursor);
end loop;
Close (File => Test_File);
end Uppgift;
I've divided read and write into two blocks because you said:
...do all the read from the input file
first before I begin writing to the
out file
Obviously with this method, you're going to have to size the Buffer variable appropriately. But really, it's pretty clumsy, compared to just using Unbounded_String. I'd say, that unless you have some very specific concerns or requirements, Unbounded_String is probably the way to go. It will simplify things immensely.
Good luck! :o)
Generally you can get away without having to have a special "length" variable in Ada. Sadly, this one of the cases where that is very difficult to pull off.
There is however a trick that lets you do it in this case. If you don't mind a little recursion, and either don't expect your strings to be tremendously long, or don't care about execution speed too much (you are doing an I/O anyway, so it's going to be slow). If this sounds OK to you, try Carlisle's trick.
function Next_Line(File : in Ada.Text_IO.File_Type :=
Ada.Text_Io.Standard_Input) return String is
Answer : String(1..256);
Last : Natural;
begin
Ada.Text_IO.Get_Line(File => File,
Item => Answer,
Last => Last);
if Last = Answer'Last then
return Answer & Next_Line(File);
else
return Answer(1..Last);
end if;
end Next_Line;
Now you can change your code to:
begin
Open(Infil, In_File, "register.txt");
Create(Utfil, Out_File, "test.txt");
Put_Line(Utfil, Next_Line (Infil));
Close(Infil);
Close(Utfil);
end Uppgift;
Related
I'm working with VHDL, and I'm wondering if there is any way to constrain the string size when declaring it, using initialization. For example, we declare a string as follow:
variable sequence : string(1 to 20) := "AGTAACCAATTCGCATTCGC";
I would like to know if there is any way to do something like:
variable sequence : string := "AGTAACCAATTCGCATTCGC";
Of course, second line isn't valid, because interpreter says:
[VRFC 10-1547] variable cannot be unconstrained
Constants don't have to be constrained, so you could do this:
constant Csequence : string := "AGTAACCAATTCGCATTCGC";
variable Vsequence : string(Csequence'range) := Csequence;
https://www.edaplayground.com/x/r3wK
entity E is
end entity E;
architecture A of E is
begin
process
constant Csequence : string := "AGTAACCAATTCGCATTCGC";
variable Vsequence : string(Csequence'range) := Csequence;
begin
wait;
end process;
end architecture A;
I'm attempting to write a quick program to send AT commands to a serial-port modem. I have opened the port with the right settings (B115200, 8N1 etc) and the String'Write call in the below code sample does actually work correctly.
Now I'm adding the code to read the modem's response back as a string. However I cannot know the length of the response beforehand and hence I cannot create a String variable to pass in to the out String parameter unless I do know the length.
package GSC renames GNAT.Serial_Communications;
SP : aliased GSC.Serial_Port;
function Send (Port : in GSC.Serial_Port; S : in String) return String is
begin
String'Write (SP'Access, S);
delay 0.1;
declare
Retval : String; -- NOT VALID - needs to be initialised
begin
String'Read (SP'Access, Retval);
return Retval;
end;
end Send;
I have a chicken / egg situation here.
The answer is probably to read the input one character at a time until you reach the terminator.
You could allocate a buffer long enough to hold the longest possible response (e.g. 1024 bytes!) (or maybe use recursion - but that’d be more complicated and make it difficult to diagnose possible overrun errors).
If the string is terminated by a specific character, you could use Interfaces.C.Pointers:
function Receive (Port : in GSC.Serial_Port) return String is
package Character_Pointers is new Interfaces.C.Pointers (
Index => Positive, Element => Character, Element_Array => String,
Default_Terminator => Character'Val (13)); -- CR-Terminated
function Convert is new Ada.Unchecked_Conversion (
Source => access all Streams.Stream_Element,
Target => Character_Pointers.Pointer);
-- assuming no more than 1023 characters + terminator can be given.
Max_Elements : constant Streams.Stream_Element_Offset :=
1024 * Character'Size / Streams.Stream_Element'Size;
Buffer : Streams.Stream_Element_Array (1 .. Max_Elements);
Last : Stream_Element_Offset;
begin
Port.Read (Buffer, Last);
return Characters_Pointers.Value (Convert (Buffer (1)'Access));
end Receive;
This code makes several assumptions:
String is terminated with CR (can be modified by setting Default_Terminator appropriately).
The response contains nothing other than the string (additional content that may have been read after the string is silently discarded).
The whole content will never be longer than 1024 bytes.
The typical way to achieve this is to send the length first, then read the value. (This is what things like bencode do.) -- Something like:
-- A stream from Standard-Input; for passing to example parameters:
Some_Stream: not null access Ada.Streams.Root_Stream_Type'Class :=
Ada.Text_IO.Text_Streams.Stream( Ada.Text_IO.Standard_Input );
-- The simple way, use the 'Input attribute; this calls the appropriate
-- default deserializations to return an unconstrained type.
-- HOWEVER, if you're reading from an already extant data-stream, you may
-- need to customize the type's Input function.
Some_Value : Constant String := String'Input( Some_Stream );
-- If the stream places a length into the stream first, you can simply read
-- it and use that value, to prealocate the proper size and fill it with the
-- 'Read attribure.
Function Get_Value( Input : not null access Ada.Streams.Root_Stream_Type'Class ) return String is
Length : Constant Natural := Natural'Input( Input );
Begin
Return Result : String(1..Length) do
String'Read( Input, Result );
End Return;
End Get_Value;
-- The last method is to use when you're dealing with buffered information.
-- (Use this if you're dealing with idiocy like null-terminated strings.)
Function Get_Buffered_Value( Input : not null access Ada.Streams.Root_Stream_Type'Class;
Buffer_Size : Positive := 1024;
Full_Buffer : Boolean := True;
Terminator : Character:= ASCII.NUL
) return String is
Buffer : String(1..Buffer_Size);
Begin
-- Full_Buffer means we can read the entire buffer-size w/o
-- "overconsuming" -- IOW, the stream is padded to buffer-length.
if full_buffer then
String'Read(Input, Buffer);
declare
Index : Natural renames Ada.Strings.Fixed.Index(
Source => Buffer,
Pattern => (1..1 => Terminator),
From => Buffer'First
);
begin
Return Buffer(Buffer'First..Index);
end;
else
declare
Index : Positive := Buffer'First;
begin
-- Read characters.
loop
Character'Read( Input, Buffer(Index) );
exit when Buffer(Index) = Terminator;
Index:= Positive'Succ( Index );
exit when Index not in Buffer'Range;
end loop;
-- We're returning everything but the terminator.
Return Buffer(1..Positive'Pred(Index));
end;
end if;
End Get_Buffered_Value;
I've designed a little project reads from Comport with ComPort Library version 4.10. When i tested with com0com emulator it works perfectly but when i read data from real machine (it's an electronic test machine sending data via RS232) data comes with CR and LF,i've searched alot and tried all procedures to clear CR&LF but failed.
Here are my codes;
var veri:string;
begin
panel1.Color:=clGreen;
panel1.Caption:='VERİ OKUNUYOR';
veri:=Trim(Str);
memo1.lines.add(CleanupString(trim(veri)));
I've tried to remove #13,#13#10,#13+#10,#32,sLineBreak and tried below functions;
function CleanupString(S :string) :string;
var
i :integer;
begin
Result := S;
for i := 1 to Length(Result) do
if Result[i] in [#3..#13] then
Result[i] := '_';
end;
function RemoveCRLF (const aString : string) : string;
var i, j : integer;
begin
result := aString;
j := 0;
for i := 1 to Length (result) do
begin
if (not (result [i] in [#10, #13]))
then begin
inc (j);
result [j] := result [i];
end;
end;
SetLength (result, j);
end;
But no luck so far, below image shows what my memo content looks like;
Device sending data starting with "APSiS0BE" and seems like sending CR&LF with every parameter but i couldn't be able to get data in one line.
I don't think that you are seeing what you believe you are seeing.
When you add a line to TMemo, you implicitly add a CR+LF, so you are removing them, but then adding them back in. If you were not, when you open the contents in Notepad++ as you do, there would be empty blank lines, and you are not seeing them.
So, I think you are successfully removing the CR+LF, you just don't realise it.
Delphi has the Trim method which is built in and will remove leading and trailing whitespace and control characters (CRLF, etc). Your method removes them from anywhere in the string but for RS-232 protocols often Trim is all you would typically need.
That said, the TMemo.lines.Add method adds a line to the memo... with a CRLF, so if you're examining the memo output it will naturally have line ending characters in it. Without it the text would just continue on a single line.
I use ComPort too and I use StringReplace to remove CR + LF :
S:= StringReplace(C, sLineBreak, '', [rfReplaceAll]);
StringReplace
What I want to do
I want to have a script in python or matlab that creates files which can be read by VHDL / modelsim as a file of real values.
What I've done so far
I've written a small VHDL entity that creates a file of real values so I can find out what format it uses. It looks like this:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.math_real.all;
library std;
use std.textio.all;
entity read_arr is
end entity read_arr;
architecture reader of read_arr is
type t_real_arr is array (0 to 1023) of real;
signal real_arr : t_real_arr := (others => 0.0);
signal start : std_logic := '0';
signal filled : std_logic := '0';
type t_real_file is file of t_real_arr;
file real_file : t_real_file;
begin -- architecture reader
start <= '0', '1' after 10 ns;
fill_arr : process (start) is
variable real_fill : real := -10.0;
begin -- process fill_arr
if rising_edge(start) then
for i in real_arr'range loop
real_arr(i) <= real_fill;
real_fill := real_fill + 0.25;
end loop; -- i
filled <= '1';
end if;
end process fill_arr;
wr_arr : process (filled) is
variable filename : string (1 to 10) := "realvc.dat";
variable status : file_open_status := status_error;
begin -- process fill_arr
if rising_edge(filled) then
file_open(status, real_file, filename, write_mode);
write(real_file, real_arr);
file_close(real_file);
end if;
end process wr_arr;
end architecture reader;
What it does is it fills an array of 1024 elements with real values starting from -10.0 and incrementing in steps of 0.25. It then writes this data into the binary file realvc.dat. The content of the file (viewed with a hex editor) can be seen in this gist.
It's easy to see that modelsim uses 64 bit to store each real value, but I haven't figured out what kind of format that is. It isn't ieee double-precision.
The Question
Does anyone know what kind of data format that is and how I can recreate it in a script language such as python or matlab?
The standard VHDL package std.textio makes it possible to read/write real in
literal format to a text file, whereby scripting languages like Python can
easily access it. The binary format is thereby avoided, which improves portability.
An example similar to the question code, doing first write then read of an array of real with std.textio is:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library std;
use std.textio.all;
entity test_arr is
end entity test_arr;
architecture test of test_arr is
begin
process is
type real_arr_t is array (0 to 1023) of real;
variable real_arr_v : real_arr_t := (others => 0.0);
variable real_fill_v : real := -10.0;
procedure real_arr_write(variable real_arr : in real_arr_t) is
file txt_file : text;
variable real_v : real;
variable line_v : line;
begin
file_open(txt_file, "real.txt", write_mode);
for idx in real_arr'range loop
write(line_v, real_arr(idx));
writeline(txt_file, line_v);
end loop;
file_close(txt_file);
end procedure;
procedure real_arr_read(variable real_arr : out real_arr_t) is
file txt_file : text;
variable line_v : line;
variable good_v : boolean;
begin
file_open(txt_file, "real.txt", read_mode);
for idx in real_arr'range loop
readline(txt_file, line_v);
read(line_v, real_arr(idx), good_v);
assert good_v report "Failed convert to real of: " & line_v.all severity FAILURE;
end loop;
file_close(txt_file);
end procedure;
begin
-- Make and write real array to text file
for i in real_arr_v'range loop
real_arr_v(i) := real_fill_v;
real_fill_v := real_fill_v + 0.25;
end loop;
real_arr_write(real_arr_v);
-- Read real array from text file
real_arr_read(real_arr_v);
-- End
wait;
end process;
end architecture test;
The real values in the "real.txt" file are then:
-1.000000e+001
-9.750000e+000
-9.500000e+000
-9.250000e+000
Python 3 can create strings like this with '{:e}'.format(x), and convert from this format using float(s).
I'm new to Delphi XE5 (part of Embarcadero's RAD Studio), and using the StrToFloat function in a VCL Forms project designed to build a simple calculator.
The code to display the text to the user works well, but I'm having trouble extracting it in a way that will allow the calculator to actually perform calculations. The means I've settled on is displaying the code to the user in a Tedit, and storing the code to be executed in a Tmemo. Pressing the buttons on the calculator stores the numbers to both, and pressing an operator (the symbols for add, subtract, exponent, multiply, and divide) starts a new line in the memo. This way, each number is on its own line, and each operator is on its own line.
I then want to extract the numbers with respect to the operators, by iterating through the lines of the memo. For 3 ^ 4, you would see...
3
^
4
...in the TMemo.
I want to find the ^ sign at line index 1 (lines start at index 0, right?), and store 3 in a base variable by the logic that, if ^ is lines[1], then 3 is lines[1-1], or lines[0], and store 4 in an exponent variable. Both variables would be of type extended. This is the code I've created to do this...
procedure TForm1.btnEqualsClick(Sender: TObject);
var
i: Integer;
base: extended;
exponent: extended;
result: extended;
begin
{This For loop looks for the exponentiation operator (^) in memCurrentEntry. If
it's found, it then solves the equation.}
for i := Low(memCurrentEntry.Lines.Text.Length) to High(memCurrentEntry.Lines.Text.Length) do
if AnsiContainsStr(memCurrentEntry.Lines.Text, '^') then
begin
base := StrToFloat(memCurrentEntry.Lines[ansipos('^', memCurrentEntry.Lines.Text)-1]);
exponent := StrToFloat(memCurrentEntry.Lines[ansiPos('^', memCurrentEntry.Lines.Text)+2]);
result := power(base, exponent);
end;
When I run the calculator, the numbers appear correctly, but when I hit the equals button to run the code above, I get the message Project Calculator_u.exe raised exception class EConvertError with Message ''' is not a valid floating point value'.
I know the StrToFloat function is trying to convert a null character or something (How do I identify what code is generating " '' is not a valid floating point value" error dialogue box), but what should I do to correct this?
Below is the code for each button -- the digit (0, 1, 2, 3, etc.) changes, but the rest is the same for each one...
procedure TForm1.btn0Click(Sender: TObject);
begin
//Adds the digit 0 to the end of the current number in memCurrentEntry
memCurrentEntry.SelStart := Length(memCurrentEntry.Text);
memCurrentEntry.Lines.Text := Copy(memCurrentEntry.Lines.Text,1,
memCurrentEntry.SelStart)+'0'+ Copy(memCurrentEntry.Lines.Text,
memCurrentEntry.SelStart+1,Length(memCurrentEntry.Lines.Text)
- memCurrentEntry.SelStart);
//Adds the digit 0 to the end of the current number in edtCurrentEntry
edtcurrententry.Text := edtcurrententry.Text + '0';
end;
I can post the full unit file upon request.
Use TryStrToFloat. This returns a boolean indicating whether or not the conversion succeeded.
var
str: string;
val: Double;
....
if TryStrToFloat(str, val) then
// val contains the floating point value represented by str
else
// str does not contain a floating point value, do something else with it
Of course, you could use StrToFloat, and catch the exception. But since you expect to encounter the exception, it will result in much cleaner code if you use TryStrToFloat.
the usage of lines on the memo is wrong:
base := StrToFloat(memCurrentEntry.Lines[ansipos('^', memCurrentEntry.Lines.Text)-1]);
the lines.text returns the ENTIRE text, which is not what you want. Instead use some LOCAL variables to hold your lookup values...that way you can set a breakpoint and see whats wrong and use the following method to locate the "^"
result := 0;
carrotLine := memCurrentEntry.Lines.IndexOf('^');
if carrotline > 0 then // must be 1 or greater, so a number can be at the 0 position
begin
base := TryStrToFloat(MemCurrentEntry.Lines[carrotLine-1],0);
exponent := 0;
// only try to read the next value if there is one.
if memCurrentEntry.Lines.Count >= carrotLine then
exponent := TryStrToFloat(MemCurrentEntry.Lines[carrotline+1],0);
result := power(base,exponent);
end;
Note that this code ONLY handles a single ^ in the memo (your code had the same issue), to go beyond this is to use a loop and possibly a state engine or some other parsing logic to handle computations.
Use StrToFloat is not wrong. If the conversion is failed, user should see an error message. If you want to suppress the error quietly, then you should add a try/except to protect your code.
try
Value := StrToFloat(A_String);
except
on EConvertError do
Value := 0; // Fallback to a default value or execute other code logic
end;
According to your given code example, if the base and the exponent are entered in your given format (first line is the base, second line is a "^" and the third line is the exponent). I'd suggest you write your code as follows:
var
Numbers: TArray<string>;
// Other variables go here
begin
// ...
Numbers := memCurrentEntry.Lines.Text.Split('^');
if Length(Numbers) = 2 then
try
Base := StrToFloat(Numbers[0]);
Exponent := StrToFloat(Numbers[1]);
Result := Power(Base, Exponent);
except
on EConvertError do
begin
ShowMessage(Format('Unable to calculate the formula %s', [memCurrentEntry.Lines.Text]));
Result := 0; // Fallback to a default value, such as 0
end;
end;
// ...
end;