procedure Get (Item : out String); ( in a Function) - string

I'm trying to return a global variable's String value , and want to use the function who use it , later in a procedure .
function get_name return String
is begin
Put_line("Your name?");
Get(name); -- name is in "globals"
put(name);
return name;
end get_name;
The package file =
package globals
is
name : String(1..20) ;
end globals;
Here the "Get" which is used in the function =
procedure Get (Item : out String);
Now , if i use the fonction in a procedure , it compile but =
At launching , no get is executing , the program "create" a "skip" line !!?
So , is it possible to use this procedure Get in a function ??
And how do you call the function who contain it , after ??

If you call the procedure Get(Item : out String), then the number of characters you read will have to be exactly 20 characters.
If you want to use the function get, you'll need to initialise a variable with its value, or pass it through as a parameter. e.g.
x : string := get_line; -- functional version that will read an entire line
or
put(get_line); -- read and entire line, pass it immediately to a procedure
As to why your input is skipping over the get, and not reading anything, this is probably because you have previously read some input, and have left a newline/end of line marker in the input. This happens often if you read numbers.
e.g. input is
34\nThe next line\n
If you read an integer, the file pointer will show you are at...
34\nThe next line\n
..^
Then you ask for a get_line, and you'll end up only reading to the end of the line (where you currently are) and you'll have a empty string, and not have read the next line.
The solution is to have a skip_line after each get.
So
get(number); skip_line;
declare
input : string := get_line;
begin
...

Related

Passing a commandline parameter containing quotes to installer

I'm trying to pass a custom commandline parameter to an installer created with Inno Setup. The parameter value actually consists of several parameters that will be used to launch the installed program when installation is complete, so the value contains spaces as well as quotes to group together parameters.
For example, when -arg "C:\path with spaces" -moreargs should be used as Parameters in a [Run] section entry, I would like to launch the installer like this:
setup.exe /abc="-arg "C:\path with spaces" -moreargs"
Outputting the parameters that the installer receives in a [Code] section via ParamStr() shows them (of course) split up: /abc=-arg C:\path, with, spaces -moreargs
How do I escape the quotes to retain them?
I tried doubling the inner quotes:
setup.exe /abc="-arg ""C:\path with spaces"" -moreargs"
This correctly keeps the parameter together (/abc=-arg C:\path with spaces -moreargs), however it seems that ParamStr() removes all quotes.
Is there a way to retain quotes within a parameter retrieved with ParamStr() or a param constant {param:abc|DefaultValue}?
Alternatives seem to be to either do my own parameter parsing from GetCmdTail (which contains the original parameter string) or use another character instead of the inner quotes that are retained in ParamStr() and then replace them with quotes afterwards. But I would prefer not doing that if there is a way to use the built-in functions.
It seems both {param} and ParamStr() strip out double quotes, but as you pointed out (thanks!) the GetCmdTail function returns the original.
So here's a function to get the original parameters with quotes:
function ParamStrWithQuotes(ParamName: String) : string;
var
fullCmd : String;
currentParamName : string;
i : Integer;
startPos : Integer;
endPos : Integer;
begin
fullCmd := GetCmdTail
// default to end of string, in case the option is the last item
endPos := Length(fullCmd);
for i := 0 to ParamCount-1 do
begin
// extract parameter name (eg, "/Option=")
currentParamName := Copy(ParamStr(i), 0, pos('=',ParamStr(i)));
// once found, we want the following item
if (startPos > 0) then
begin
endPos := pos(currentParamName,fullCmd)-2; // -1 to move back to actual end position, -1 for space
break; // exit loop
end;
if (CompareText(currentParamName, '/'+ParamName+'=') = 0) then // case-insensitive compare
begin
// found target item, so save its string position
StartPos := pos(currentParamName,fullCmd)+2+Length(ParamName);
end;
end;
if ((fullCmd[StartPos] = fullCmd[EndPos])
and ((fullCmd[StartPos] = '"') or (fullCmd[StartPos] = ''''))) then
begin
// exclude surrounding quotes
Result := Copy(fullCmd, StartPos+1, EndPos-StartPos-1);
end
else
begin
// return as-is
Result := Copy(fullCmd, StartPos, EndPos-StartPos+1);
end;
end;
You can access this with {code:ParamStrWithQuotes|abc}
When invoking the setup.exe, you do have to escape the quotes, so one of the following works for that:
setup.exe /abc="-arg ""C:\path with spaces"" -moreargs"
or
setup.exe /abc='-arg "C:\path with spaces" -moreargs'

String manipulation in ada

I am getting a path of directory in a string , like "C:\Users\Me\Desktop\Hello”, and I am trying to get the last directory, but without success.
I tried a lot of manipulation on the string but in the end of the day i stayed with nothing... i will be grateful to get some help. Thanks !
Here was my first idea :
Get_Line(Line, Len);
while (Line /="") loop
FirstWord:=Index(Line(1..Len),"\")+1;
declare
NewLine :String := (Line(FirstWord .. Len));
begin
Line:=NewLine ;
end;
end loop;
I know its not working (I can’t assign NewLine to Line because there isn't a match between their lengths), and now I am stuck.
I’m assuming you want to manipulate directory (and file) names, rather than just any old string?
In which case you should look at the standard library packages Ada.Directories (ARM A.16) and Ada.Directories.Hierarchical_File_Names (ARM A.16.1):
with Ada.Directories;
with Ada.Text_IO; use Ada.Text_IO;
procedure Tal is
Line : constant String := "C:\Users\Me\Desktop\Hello";
begin
Put_Line ("Full_Name: "
& Ada.Directories.Full_Name (Line));
Put_Line ("Simple_Name: "
& Ada.Directories.Simple_Name (Line));
Put_Line ("Containing_Directory: "
& Ada.Directories.Containing_Directory (Line));
Put_Line ("Base_Name: "
& Ada.Directories.Base_Name (Line));
end Tal;
On the other hand, if you’re trying to work out plain string manipulation, you could use something like
with Ada.Strings.Fixed;
with Ada.Text_IO; use Ada.Text_IO;
procedure Tal is
function Get_Last_Word (From : String;
With_Separator : String)
return String is
Separator_Position : constant Natural :=
Ada.Strings.Fixed.Index (Source => From,
Pattern => With_Separator,
Going => Ada.Strings.Backward);
begin
-- This will fail if there are no separators in From
return From (Separator_Position + 1 .. From'Last); --'
end Get_Last_Word;
Line : constant String := "C:\Users\Me\Desktop\Hello";
Last_Name : constant String := Get_Last_Word (Line, "\");
begin
Put_Line (Last_Name);
end Tal;
As you can see, putting the logic in Get_Last_Word allows you to hoist Last_Name out of a declare block. But it will never be possible to overwrite a fixed string with a substring of itself (unless you’re prepared to deal with trailing blanks, that is): it’s much better never to try.

ada split() method

I am trying to write an Ada equivalent to the split() method in Java or C++. I am to intake a string and an integer and output two seperate string values. For example:
split of "hello" and 2 would return:
"The first part is he
and the second part is llo"
The code I have is as follows:
-- split.adb splits an input string about a specified position.
--
-- Input: Astring, a string,
-- Pos, an integer.
-- Precondition: pos is in Astring'Range.
-- Output: The substrings Astring(Astring'First..Pos) and
-- Astring(Pos+1..Astring'Last).
--------------------------------------------------------------
with Ada.Text_IO, Ada.Integer_Text_IO, Ada.Strings.Fixed;
use Ada.Text_IO, Ada.Integer_Text_IO, Ada.Strings.Fixed;
procedure Split is
EMPTY_STRING : String := " ";
Astring, Part1, Part2 : String := EMPTY_STRING;
Pos, Chars_Read : Natural;
------------------------------------------------
-- Split() splits a string in two.
-- Receive: The_String, the string to be split,
-- Position, the split index.
-- PRE: 0 < Position <= The_String.length().
-- (Ada arrays are 1-relative by default)
-- Passback: First_Part - the first substring,
-- Last_Part - the second substring.
------------------------------------------------
function Split(TheString : in String ; Pos : in Integer; Part1 : out String ; Part2 : out String) return String is
begin
Move(TheString(TheString'First .. Pos), Part1);
Move(TheString(Pos .. TheString'Last), Part2);
return Part1, Part2;
end Split;
begin -- Prompt for input
Put("To split a string, enter the string: ");
Get_Line(Astring, Chars_Read);
Put("Enter the split position: ");
Get(Pos);
Split(Astring, Pos, Part1, Part2);
Put("The first part is ");
Put_Line(Part1);
Put(" and the second part is ");
Put_Line(Part2);
end Split;
The main part I am having trouble with is returning the two separate string values and in general the whole split() function. Any pointers or help is appreciated. Thank you
Instead of a function, consider making Split a procedure having two out parameters, as you've shown. Then decide if Pos is the last index of Part1 or the first index of Part2; I've chosen the latter.
procedure Split(
TheString : in String; Pos : in Integer;
Part1 : out String; Part2 : out String) is
begin
Move(TheString(TheString'First .. Pos - 1), Part1);
Move(TheString(Pos .. TheString'Last), Part2);
end Split;
Note that String indexes are Positive:
type String is array(Positive range <>) of Character;
subtype Positive is Integer range 1 .. Integer'Last;
Doing this is so trivial, I'm not sure why you'd bother making a routine for it. Just about any routine you could come up with is going to be much harder to use anyway.
Front_Half : constant String := Original(Original'first..Index);
Back_Half : constant String := Original(Index+1..Original'last);
Done.
Note that static Ada strings are very different than strings in other languages like C or Java. Due to their static nature, they are best built either inline like I've done above, or as return values from functions. Since functions cannot return more than one value, a single unified "split" routine is just plain not a good fit for static Ada string handling. Instead, you should either do what I did above, call the corresponding routines from Ada.Strings.Fixed (Head and Tail), or switch to using Ada.Strings.Unbounded.Unbounded_String instead of String.
The latter is probably the easiest option, if you want to keep your Java mindset about string handling. If you want to really learn Ada though, I'd highly suggest you learn to deal with static fixed Strings the Ada way.
From looking over your code you really need to read up in general on the String type, because you're dragging in a lot of expectations in from other languages on how to work with them--which aren't going to work with them. Ada's String type is not one of its more flexible features, in that they are always fixed length. While there are ways of working around the limitations in a situation such as you're describing, it would be much easier to simply use Unbounded_Strings.
The input String to your function could remain of type String, which will adjust to the length of the string that you provide to it. The two output Unbounded_Strings then are simply set to the sliced string components after invoking To_Unbounded_String() on each of them.
Given the constraints of your main program, with all strings bounded by the size of EMPTY_STRING. the procedure with out parameters is the correct approach, with the out parameter storage allocated by the caller (on the stack as it happens)
That is not always the case, so it is worth knowing another way. The problem is how to deal with data whose size is unknown until runtime.
Some languages can only offer runtime allocation on the heap (via "new" or "malloc") and can only access the data via pointers, leaving a variety of messy problems including accesses off the end of the data (buffer overruns) or releasing the storage correctly (memory leaks, accessing freed pointers etc)
Ada will allow this method too, but it is usually unnecessary and strongly discouraged. Unbounded_String is a wrapper over this method, while Bounded_String avoids heap allocation where you can accept an upper bound on the string length.
But also, Ada allows variable sized data structures to be created on the stack; the technique just involves creating a new stack frame and declaring new variables where you need to, with "declare". The new variables can be initialised with function calls.
Each function can only return one object, but that object's size can be determined at runtime. So either "Split" can be implemented as 2 functions, returning Part1 or Part2, or it can return a record containing both strings. It would be a record with two size discriminants, so I have chosen the simpler option here. The function results are usually built in place (avoids copying).
The flow in your example would require two nested Declare blocks; if "Pos" could be identified first, they could be collapsed into one...
procedure Split is
function StringBefore( Input : String; Pos : Natural) return String is
begin
return Input(1 .. Pos-1);
end StringBefore;
function StringFrom ...
begin
Put("To split a string, enter the string: ");
declare
AString : String := Get_Line;
Pos : Natural;
begin
Put("Enter the split position: ");
Get(Pos);
declare
Part1 : String := StringBefore(AString, Pos);
Part2 : String := StringFrom(AString, Pos);
begin
Put("The first part is ");
Put_Line(Part1);
Put(" and the second part is ");
Put_Line(Part2);
end; -- Part1 and Part2 are now out of scope
end; -- AString is now out of scope
end Split;
This can obviously be wrapped in a loop, with different size strings each time, with no memory management issues.
Look at the Head and Tail functions in Ada.Strings.Fixed.
function Head (Source : in String; Count : in Natural; Pad : in Character := Space) return String;
function Tail (Source : in String; Count : in Natural; Pad : in Character := Space)
return String;
Here's an approach that just uses slices of the string.
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Strings.Fixed; use Ada.Strings.Fixed;
procedure Main is
str : String := "one,two,three,four,five,six,seven,eight";
pattern : String := ",";
idx, b_idx : Integer;
begin
b_idx := 1;
for i in 1..Ada.Strings.Fixed.Count ( Source => str, Pattern => pattern ) loop
idx := Ada.Strings.Fixed.Index( Source => str(b_idx..str'Last), Pattern => pattern);
Put_Line(str(b_idx..idx-1)); -- process string slice in any way
b_idx := idx + pattern'Length;
end loop;
-- process last string
Put_Line(str(b_idx..str'Last));
end Main;

Ada string comparison

I am new to Ada and currently trying to write a simple program involving an if-else if statement. The code is as follows:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
procedure Year_Codes is
Year : String(1..9) := " ";
CharsRead : Natural;
function YearCode(Name : in String) return Integer is
begin
if(Name = "freshman")then
return 1;
elsif(Name = "sophomore")then
return 2;
elsif(Name = "junior")then
return 3;
elsif(Name = "senior")then
return 4;
else
return 0;
end if;
end YearCode;
begin
Put("Enter your academic year: "); -- Prompt for input
Get_Line(Year, CharsRead); -- Input
Put( YearCode(Year) ); -- Convert and output
New_Line;
end Year_Codes;
I am getting 0 for every answer. Any input on what I am doing wrong?
The "=" operation on strings compares the entire strings. If the user's input is "freshman", the value of Name will be "freshman ", not "freshman". Read the documentation for the Get_Line procedure.
You should probably pass YearCode a slice of the Year string, not the entire string; CharsRead tells you what that slice should be.
Specifically, the call should be:
Put( YearCode(Year(Year'First..CharsRead)) );
Here's a case-insensitive version using attributes:
function YearCode(Name : in String) return Integer is
Type Class is (Freshman, Sophmore, Junior, Senior);
begin
Return 1 + Class'Pos(Class'Value(Name));
exception
When CONSTRAINT_ERROR => Return 0;
end YearCode;
With that extra character in your buffer, it looks to me like you are thinking of strings in C terms. You need to stop that. Of everything in the language, string handling is the most different between Ada and C.
While C strings are null terminated, Ada strings are not. Instead, an Ada string is assumed to be the size of the string array object. Its a simple difference, but it has enormous consequences in how you handle strings.
I go into this a bit in my answer to How to I build a string from other strings in Ada? The basic gist is that in Ada you always try to build perfectly-sized string objects on the fly.
Sadly, Text_IO input is one place that has traditionally made that really hard, due to its string buffer-based input. In that case, you are forced to use an overly large string object as a buffer, and use the returned value as the end of the defined area of the buffer, as Keith showed.
However, if you have a new version of the compiler, you can use the function version of Get_Line to fix that. Simply change your middle two lines to:
Put( YearCode(Get_Line) );

Printing a string as a variable in Pascal

This is the string variable that I have:
question1 := 'Please enter 1, 2 or 3.';
I also have a function which is supposed to print out the question1 variable, but it generates the question number before printing. Here's a fragment of the function, which turns the question number (question : integer) into a string variable (test : string) and then concatenates the string 'question' with this string variable (test : string).
str(question,test);
test := concat('question',test);
writeln(test);
The result of this writeln is 'question1' (without the quotes). I want it to output the question1 variable as a text string, not just this variable's name, so that the writeln prints Please enter 1, 2 or 3. I've tried writeln(question1) and it works, however, it appears that my function above (or the fragment of it) does this: writeln('question1'). How do I solve this?
Pascal doesn't support dynamic name resolution, like you want. You might consider using arrays instead:
…
var
Questions: array[1..3] of string;
…
procedure InitQuestions;
begin
Questions[1] := 'Please enter 1, 2 or 3.';
Questions[2] := '…';
Questions[3] := '…';
end;
procedure YourFunction(question: Integer);
begin
…
WriteLn(Questions[question]);
…
end;
…
begin
…
InitQuestions;
…
YourFunction(1);
…
end.
As far as I can figure out from your question, you need the following
writeln (question1, 'question ', question);
You don't need to concatenate string values, nor do you need to convert the question number to a string - writeln has the magical ability to accept any number of variables and print them according to their default format (strings, integers, reals and booleans).
In fact, you don't really need to create the 'question1' variable - you could simply write
writeln ('Please enter 1, 2 or 3. Question', question);

Resources