Rationale for Ada 2005

7.5 Characters and strings

An important improvement in Ada 2005 is the ability
to deal with 16- and 32-bit characters both in the program text and in
the executing program.

The fine detail of the changes to the program text
are perhaps for the language lawyer. The purpose is to permit the use
of all relevant characters of the entire ISO/IEC 10646:2003 repertoire.
The most important effect is that we can write programs using Cyrillic,
Greek and other character sets.

A good example is provided
by the addition of the constant

π : constant := Pi;

to the package Ada.Numerics.
This enables us to write mathematical programs in a more natural notation
thus

Circumference: Float := 2.0 * π * Radius;

Other examples might
be for describing polar coordinates thus

R: Float := Sqrt(X*X + Y*Y);
θ: Angle := Arctan(Y, X);

and of course in France
we can now declare a decent set of ingredients for breakfast

type Breakfast_Stuff is (Croissant, Café, Œuf, Beurre);

Curiously, although
the ligature æ is in Latin-1 and thus
available in Ada 95 in identifiers, the ligature œ
is not (for reasons we need not go into). However, in Ada 95, œ
is a character of the type Wide_Character
and so even in Ada 95 one can order breakfast thus

Put("Deux œufs easy-over avec jambon"); -- wide string

In order to manipulate
32-bit characters, Ada 2005 includes types Wide_Wide_Character
and Wide_Wide_String in the package Standard
and the appropriate operations to manipulate them in packages such as

There are also new attributes Wide_Wide_Image,
Wide_Wide_Value and Wide_Wide_Width
and so on.

The addition of wide-wide characters and strings
introduces many additional possibilities for conversions. Just adding
these directly to the existing package Ada.Characters.Handling
could cause ambiguities in existing programs when using literals. So
a new package Ada.Characters. Conversions
has been added.
This contains conversions in all combinations between Character,
Wide_Character and Wide_Wide_Character
and similarly for strings. The existing functions from Is_Character
to To_Wide_String in Ada.Characters.Handling
have been banished to Annex
J.

The introduction of more complex writing systems
makes the definition of the case insensitivity of identifiers, (the equivalence
between upper and lower case), much more complicated.

In some systems, such
as the ideographic system used by Chinese, Japanese and Korean, there
is only one case, so things are easy. But in other systems, like the
Latin, Greek and Cyrillic alphabets, upper and lower case characters
have to be considered. Their equivalence is usually straightforward but
there are some interesting exceptions such as

Greek has two forms for lower case sigma (the normal
form σ and the final form ς which is used at the end of a word).
These both convert to the one upper case letter Σ.

German has the lower case letter ß whose
upper case form is made of two letters, namely SS.

Slovenian has a grapheme LJ which is considered
a single letter and has three forms: LJ, Lj and lj.

The Greek situation used to apply in English where
the long s was used in the middle of words (where it looked like an f
but without a cross stroke) and the familiar short s only at the end.
To modern eyes this makes poetic lines such as "Where the bee sucks,
there suck I" somewhat dubious. (This is sung by Ariel in Act V
Scene I of The Tempest by William Shakespeare.)

The definition chosen for Ada 2005 closely follows
those provided by ISO/IEC 10646:2003 and by the Unicode Consortium; this
hopefully means that all users should find that the case insensitivity
of identifiers works as expected in their own language.

Of interest to all users whatever their language
is the addition of a few more subprograms in the string handling packages.
As explained in the Introduction, Ada 95 requires rather too many conversions
between bounded and unbounded strings and the raw type String
and, moreover, multiple searching is inconvenient.

The difference between these and the existing functions
is that these have an additional parameter From.
This makes it much easier to search for all the occurrences of some pattern
in a string.

Similar functions are also added to the packages
Ada.Strings.Bounded and Ada.Strings.Unbounded.

Thus suppose we want
to find all the occurrences of "bar"
in the string "barbara barnes" held
in the variable BS of type Bounded_String.
(I have put my wife into lower case for convenience.) There are 3 of
course. The existing function Count can be
used to determine this fact quite easily

N := Count(BS, "bar") -- is 3

But we really need
to know where they are; we want the corresponding index values. The first
is easy in Ada 95

I := Index(BS, "bar") -- is 1

But to find the next
one in Ada 95 we have to do something such as take a slice by removing
the first three characters and then search again. This would destroy
the original string so we need to make a copy of at least part of it
thus

and so on in the not-so-obvious
loop. (There are other ways such as making a complete copy first, this
could either be in another bounded string or perhaps it is simplest just
to copy it into a normal String first; but
whatever we do it is messy.) In Ada 2005, having found the index of the
first in I, we can find the second by writing

The procedure Set_Bounded_String
is similar to the existing function To_Bounded_String.
Thus rather than

BS := To_Bounded_String("A Bounded String");

we can equally write

Set_Bounded_String(BS, "A Bounded String");

The slice subprograms
avoid conversion to and from the type String.
Thus to extract the characters from 3 to 9 we can write

BS := Bounded_Slice(BS, 3, 9); -- "Bounded"

whereas in Ada 95 we
have to write something like

BS := To_Bounded(Slice(BS, 3, 9));

Similar subprograms are added to Ada.Strings.Unbounded.
These are even more valuable because unbounded strings are typically
implemented with controlled types and the use of a procedure such as
Set_Unbounded_String is much more efficient
than the function To_Unbounded_String because
it avoids assignment and thus calls of Adjust.

Input and output of
bounded and unbounded strings in Ada 95 can only be done by converting
to or from the type String. This is both slow
and untidy. This problem is particularly acute with unbounded strings
and so Ada 2005 provides the following additional package (we have added
a use clause for brevity as usual)

with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;package Ada.Text_IO.Unbounded_IO is

procedure Put(File: in File_Type; Item: in Unbounded_String);procedure Put(Item: in Unbounded_String);

procedure Put_Line(File: in File_Type; Item: in Unbounded_String);procedure Put_Line(Item: in Unbounded_String);

procedure Get_Line(File: in File_Type; Item: out Unbounded_String);procedure Get_Line(Item: out Unbounded_String);

end Ada.Text_IO.Unbounded_IO;

The behaviour is as expected.

There is a similar package for bounded strings but
it is generic. It has to be generic because the package Generic_Bounded_Length
within Strings.Bounded is itself generic and
has to be instantiated with the maximum string size. So the specification
is

with Ada.Strings.Bounded; use Ada.Strings.Bounded;genericwith package Bounded is new Generic_Bounded_Length(<>);use Bounded;package Ada.Text_IO.Bounded_IO is

procedure Put(File: in File_Type; Item: in Bounded_String);procedure Put(Item: in Bounded_String);

... -- etc as for Unbounded_IO

end Ada.Text_IO.Bounded_IO;

It will be noticed that these packages include functions
Get_Line as well as procedures Put_Line
and Get_Line corresponding to those in Text_IO.
The reason is that procedures Get_Line are
not entirely satisfactory.

If we do successive calls of the procedure Text_IO.Get_Line
using a string of length 80 on a series of lines of length 80 (we are
reading a nice old deck of punched cards), then it does not work as expected.
Alternate calls return a line of characters and a null string (the history
of this behaviour goes back to early Ada 83 days and is best left dormant).