❌ About FreshRSS

Normal view

There are new articles available, click to refresh the page.
Before yesterdayNews from the Ada programming language world

Coding Ada: get vs. get_line (ii) – some caveats

By: spqr
9 February 2022 at 15:08

When used separately, get and get_line rarely cause issues. When used together, there can be some issues. Consider the following code:

a : integer;
s : string(1..4);

get(a);
s := get_line;
put(a);
put_line(s);

Here is the dialog obtained when running the program containing this code:

4

raised CONSTRAINT_ERROR : testgetm.adb:14 length check failed

It allows the user to input the number 4, but then balks and pulls an exception. What went wrong? To see what happened we have to consider exactly what these input functions do. As we have seen, calls to get for numeric and string input skip over blanks and line terminators that come before the number. That’s why it is possible to press the <return> key several times between numbers being entered. However these get functions do not skip over any blanks and line terminators that come after the number. After the integer variable a is read, the input pointer is set to the line terminator after 4.

Because get_line stops reading when it encounters a line terminator, it stops and sets the length of the string to zero to indicate a null string. As the size of the (fixed) string is expected to be 4 characters in length, an exception is actioned. One way to circumvent this is to type the string on the same line as the number 4. For example:

6tool
          6tool

This illustrates another feature of numeric input. The get function for numeric data stops reading when a non-numeric character is encountered. The non-numeric character β€œt” is not read and is still available to be read by the next call to an input function.

This solution is not really optimal. A better idea is to use the function skip_line. When skip_line is called, it moves the input pointer to the next line. It is most useful when using a get_line function after getting a numeric value. Here is an example of its use:

a : integer;
s : string(1..4);

get(a);
skip_line;
s := get_line;
put(a);
put_line(s);

Note that if we use a unbound_string instead of the fixed string, then the program will not convulse, but it also will skip the string input… but it will print out a string. Why? Well the unbound_string will actually store the line terminator in the string… so it does read something, just not what it wanted.

To make things more consistent, it is also possible to read all input in as strings using get_line, and convert the string to numbers. Below is an example of the code. The first value is input to the unbound_string buf, which is then converted to a string, and then an integer, and assigned to the integer variable a.

a : integer;
buf : unbounded_string;

buf := get_line;
a := integer'value(to_string(buf));
buf := get_line;
put(a);
put_line(buf);

Note that weird things can also occur when using consecutive get_line() statements. For example consider the code:

s,t : string(1..4);
get_line(s,len);
put(s); put(len);
get_line(t,len);
put(t); put(len);

Here is an example of it running:

tool
tool          4οΏ½#           0

The first string input β€œtool”. works, the second string reads in the line terminator. The same does not occur using the alternate form of get_line. For example the code below works as it should.

s,t : string(1..4);
s := get_line;
put(s);
t := get_line;
put(t);

Coding Ada: get vs. get_line (i) – the basics

By: spqr
7 February 2022 at 16:29

Ada is a very strict language, but then it has to be, as it was designed for creating real-time systems. It does have some idiosyncrasies, but then all languages do. Let’s look at the two functions get() and get_line().

get()

The function get(), reads a value from the standard input. It knows what to read based on the argument it is given. For example the following piece of code allows an integer to be read into value x.

x : integer;
get(x);

Here are the characteristics of get():

  • For numbers it skips whitespaces (including newlines).
  • For characters and strings it reads exactly the right number of characters, but excludes newlines in count.

For example consider the following code:

s : string(1..4);
get(s);

Then the input from the user could be either one of:

tool
t
o
o
l

get_line()

The function get_line(), reads an entire line, i.e. a string. It stops reading when it encounters a line terminator (or rather if the end of the line is encountered, it automatically calls skip_line). Ada offers two ways to use get_line:

s : string(1..4);
len : natural;
s := get_line;
get_line(s,len);

The first (Line 3) treats get_line like a function, the other (Line 4) treats it like a procedure. Note there is a second parameter in the latter call, len. This returns the length of the string read in (the second parameter should be omitted for unbound_string). When using these two, there are different outcomes. Consider the following code, which reads in a fixed string.

s : string(1..4);
s := get_line;
put(s);

This code will only work when the string input is exactly 4 characters in length. Any other length will throw an exception. This is unlike C which allows more or less to be read. Alternatively, consider the following code:

s : string(1..4);
len : natural;
get_line(s,len);
put(s); put(len);

In this case, the value stored in s will be any string less than or equal to 4 characters in length, with the size stored in len. To circumvent these issues, often an unbound_string is used, which will basically read any size string up until the line terminator (typically a <return>). The code would look like this:

buf : unbounded_string;
buf := get_line;
put(buf);

Further reading:

Coding Ada: get vs. get_line (ii) – some caveats

By: spqr
9 February 2022 at 15:08

When used separately, get and get_line rarely cause issues. When used together, there can be some issues. Consider the following code:

a : integer;
s : string(1..4);

get(a);
s := get_line;
put(a);
put_line(s);

Here is the dialog obtained when running the program containing this code:

4

raised CONSTRAINT_ERROR : testgetm.adb:14 length check failed

It allows the user to input the number 4, but then balks and pulls an exception. What went wrong? To see what happened we have to consider exactly what these input functions do. As we have seen, calls to get for numeric and string input skip over blanks and line terminators that come before the number. That’s why it is possible to press the <return> key several times between numbers being entered. However these get functions do not skip over any blanks and line terminators that come after the number. After the integer variable a is read, the input pointer is set to the line terminator after 4.

Because get_line stops reading when it encounters a line terminator, it stops and sets the length of the string to zero to indicate a null string. As the size of the (fixed) string is expected to be 4 characters in length, an exception is actioned. One way to circumvent this is to type the string on the same line as the number 4. For example:

6tool
          6tool

This illustrates another feature of numeric input. The get function for numeric data stops reading when a non-numeric character is encountered. The non-numeric character β€œt” is not read and is still available to be read by the next call to an input function.

This solution is not really optimal. A better idea is to use the function skip_line. When skip_line is called, it moves the input pointer to the next line. It is most useful when using a get_line function after getting a numeric value. Here is an example of its use:

a : integer;
s : string(1..4);

get(a);
skip_line;
s := get_line;
put(a);
put_line(s);

Note that if we use a unbound_string instead of the fixed string, then the program will not convulse, but it also will skip the string input… but it will print out a string. Why? Well the unbound_string will actually store the line terminator in the string… so it does read something, just not what it wanted.

To make things more consistent, it is also possible to read all input in as strings using get_line, and convert the string to numbers. Below is an example of the code. The first value is input to the unbound_string buf, which is then converted to a string, and then an integer, and assigned to the integer variable a.

a : integer;
buf : unbounded_string;

buf := get_line;
a := integer'value(to_string(buf));
buf := get_line;
put(a);
put_line(buf);

Note that weird things can also occur when using consecutive get_line() statements. For example consider the code:

s,t : string(1..4);
get_line(s,len);
put(s); put(len);
get_line(t,len);
put(t); put(len);

Here is an example of it running:

tool
tool          4οΏ½#           0

The first string input β€œtool”. works, the second string reads in the line terminator. The same does not occur using the alternate form of get_line. For example the code below works as it should.

s,t : string(1..4);
s := get_line;
put(s);
t := get_line;
put(t);

spqr

Coding Ada: get vs. get_line (i) – the basics

By: spqr
7 February 2022 at 16:29

Ada is a very strict language, but then it has to be, as it was designed for creating real-time systems. It does have some idiosyncrasies, but then all languages do. Let’s look at the two functions get() and get_line().

get()

The function get(), reads a value from the standard input. It knows what to read based on the argument it is given. For example the following piece of code allows an integer to be read into value x.

x : integer;
get(x);

Here are the characteristics of get():

  • For numbers it skips whitespaces (including newlines).
  • For characters and strings it reads exactly the right number of characters, but excludes newlines in count.

For example consider the following code:

s : string(1..4);
get(s);

Then the input from the user could be either one of:

tool
t
o
o
l

get_line()

The function get_line(), reads an entire line, i.e. a string. It stops reading when it encounters a line terminator (or rather if the end of the line is encountered, it automatically calls skip_line). Ada offers two ways to use get_line:

s : string(1..4);
len : natural;
s := get_line;
get_line(s,len);

The first (Line 3) treats get_line like a function, the other (Line 4) treats it like a procedure. Note there is a second parameter in the latter call, len. This returns the length of the string read in (the second parameter should be omitted for unbound_string). When using these two, there are different outcomes. Consider the following code, which reads in a fixed string.

s : string(1..4);
s := get_line;
put(s);

This code will only work when the string input is exactly 4 characters in length. Any other length will throw an exception. This is unlike C which allows more or less to be read. Alternatively, consider the following code:

s : string(1..4);
len : natural;
get_line(s,len);
put(s); put(len);

In this case, the value stored in s will be any string less than or equal to 4 characters in length, with the size stored in len. To circumvent these issues, often an unbound_string is used, which will basically read any size string up until the line terminator (typically a <return>). The code would look like this:

buf : unbounded_string;
buf := get_line;
put(buf);

Further reading:

spqr

❌
❌