Fixed Strings & Character Handling

Understand that String is an array of Character with fixed length, use slices and attributes, Ada.Strings.Fixed (Index, Count, Head, Tail, Trim, Move, Replace_Slice, Insert, Delete, Translate, "*"), Ada.Characters.Handling, 'Image and 'Value conversions, and Wide/Wide_Wide strings.

Ada's String type is simply 'array (Positive range <>) of Character'. That gives strings all the power of arrays — slicing, concatenation, comparison, bounds attributes — but also the key rule beginners trip over: a String object has a fixed length forever. Assigning a string of a different length raises Constraint_Error. This lesson covers idiomatic fixed-string handling; the next covers bounded and unbounded strings for variable-length text.

Strings as Arrays

'Greeting : constant String := "Hello, Ada World";' gets bounds 1 .. 16 from its initial value. 'Name : String (1 .. 10);' always holds exactly 10 characters. Indexing, slicing (Greeting (8 .. 10)), 'Length, 'First and concatenation with & work as for any array. Quotes inside a literal are doubled: "She said ""hi""". Character literals use single quotes: 'A'. The idiomatic way to deal with computed text is to avoid variables altogether and build strings as constants or function results — 'declare Msg : constant String := Compute; begin ... end;' — since a constant takes whatever length its initial value has.

• String = array (Positive range <>) of Character. • Length is fixed per object; wrong length on assignment → Constraint_Error. • Prefer constants initialized from expressions for computed text. • "" inside a literal represents one quote character.

Ada.Strings.Fixed

Ada.Strings.Fixed provides the classic operations: Index (Source, Pattern) returns the position or 0; Count counts occurrences; Head and Tail take (and pad) prefixes/suffixes; Trim (S, Left | Right | Both) removes blanks; Move copies into a fixed-size target with padding or truncation; Replace_Slice, Insert, Delete and Overwrite return modified copies; Translate applies a character mapping (Ada.Strings.Maps.Constants.Lower_Case_Map); N * "ab" replicates. Ada.Strings.Maps defines character sets for searches such as Index (S, Set, Test => Outside).

• Index / Count / Head / Tail / Trim / Move. • Replace_Slice / Insert / Delete / Overwrite return new strings. • Translate with maps; N * S replicates.

Characters and Conversions

Ada.Characters.Handling offers Is_Letter, Is_Digit, Is_Upper, Is_Alphanumeric, Is_Space (etc.) and To_Upper / To_Lower for characters and strings. Character'Pos and Character'Val convert between characters and codes; Ada.Characters.Latin_1 names control characters (LF, HT, NUL). T'Image (X) produces text with a leading space for non-negative numbers (trim it with Trim (..., Left)); Ada 2022 also allows X'Image on objects. T'Value (S) parses text, ignoring surrounding blanks and raising Constraint_Error on bad input.

String literals are overloaded: "apple" could be a String, a Wide_String (16-bit characters) or a Wide_Wide_String (32-bit, full Unicode). When the context does not decide — comparing two literals — the compiler reports an ambiguity and you qualify one side: String'("apple") < "banana". For UTF-8 text, Ada.Strings.UTF_Encoding converts between encodings.

• Ada.Characters.Handling: Is_Digit, Is_Letter, To_Upper, ... • 'Image adds a leading space for non-negative numbers; 'Value parses. • String / Wide_String / Wide_Wide_String; qualify ambiguous literals. • Ada.Strings.UTF_Encoding for UTF-8/16 conversions.

Code Examples

GNAT warns at compile time that the two literal comparisons are constant and that assigning a 9-character string to a 10-character variable will raise Constraint_Error — and at run time it does. Output: Length = 16, first char = H, slice (8 .. 10) = Ada [Lovelace ] trimmed: [Lovelace] Index of "Ada" = 8 Index of "xyz" = 0 Count of 'l' = 3 Head (5) = Hello, Tail (5) = World 3 * "ab" = ababab Upper: HELLO, ADA WORLD Translate: hello, ada world Digits in "R2-D2 ok": 2 Character'Pos ('A') = 65, Character'Val (97) = a Integer'Image (42) = [ 42] (note the leading space) 42'Image trimmed = [42] Integer'Value (" 123 ") + 1 = 124 "apple" < "banana" = TRUE "Ada" = "Ada " = FALSE Hello, SPARK World Hello, dear Ada World HelloWorld Assigning 9 chars to String (1 .. 10) raised Constraint_Error

Comparing two bare literals is ambiguous because they could be String, Wide_String or Wide_Wide_String. Real GNAT diagnostic: l21_err.adb:5:15: error: ambiguous operands for comparison

Name : String (1 .. 10); ... Name := "Ada";

Move ("Ada", Name); -- pads with spaces -- or use Unbounded_String / a constant

Assigning a shorter string raises Constraint_Error; Move pads or truncates.

Put_Line ("Count:" & Integer'Image (N) & "items");

Put_Line ("Count: " & Trim (Integer'Image (N), Left) & " items");

'Image includes a leading space for non-negative values; format deliberately.

if Index (S, "x") = -1 then if Index (S, "x") = 0 then Index returns 0 when the pattern is not found.

What happens with 'S : String (1 .. 5); S := "Hi";'? A. S becomes "Hi " B. Constraint_Error (length mismatch) C. S becomes "Hi" and shrinks D. Compile error only

B. Constraint_Error at run time (GNAT also warns at compile time).

What does Integer'Image (42) return? A. "42" B. " 42" C. "+42" D. "042"

B. " 42" — a leading space for non-negative numbers.

Which returns 0 if a pattern is not found? A. Ada.Strings.Fixed.Index B. Ada.Strings.Fixed.Count C. Both D. Neither

C. Both — Index returns position 0; Count returns 0 occurrences.

Why is 'if "a" < "b" then' rejected? A. Strings cannot be compared B. The literals' type is ambiguous (String/Wide_String/Wide_Wide_String) C. Needs 'Image D. It is accepted

B. Qualify one operand, e.g. String'("a") < "b".

Why does this fail to compile?

Real GNAT output: l21_err.adb:5:15: error: ambiguous operands for comparison Fix: if String'("apple") < "banana" then ...

Write a function Is_Palindrome (S : String) return Boolean that ignores case.

function Is_Palindrome (S : String) return Boolean is U : constant String := Ada.Characters.Handling.To_Upper (S); begin return (for all I in U'Range => U (I) = U (U'First + U'Last - I)); end Is_Palindrome;

A quantified expression compares each character with its mirror; using U'First + U'Last - I makes it correct for any bounds.

with Ada.Text_IO;             use Ada.Text_IO;
with Ada.Strings;             use Ada.Strings;
with Ada.Strings.Fixed;       use Ada.Strings.Fixed;
with Ada.Strings.Maps.Constants;
with Ada.Characters.Handling; use Ada.Characters.Handling;

procedure L21_Strings is
   --  String is simply: type String is array (Positive range <>) of Character;
   Greeting : constant String := "Hello, Ada World";
   Name     : String (1 .. 10);            --  fixed length: exactly 10 characters
   Count    : Natural := 0;
   Droid    : constant String := "R2-D2 ok";
begin
   Put_Line ("Length =" & Integer'Image (Greeting'Length)
             & ", first char = " & Greeting (Greeting'First)
             & ", slice (8 .. 10) = " & Greeting (8 .. 10));

   --  Fixed-length strings must be filled exactly; Move pads (or truncates).
   Move ("Lovelace", Name);                 --  pads with spaces
   Put_Line ("[" & Name & "]  trimmed: [" & Trim (Name, Right) & "]");

   --  Searching
   Put_Line ("Index of ""Ada""  =" & Integer'Image (Index (Greeting, "Ada")));
   Put_Line ("Index of ""xyz""  =" & Integer'Image (Index (Greeting, "xyz")));
   Put_Line ("Count of 'l'    =" & Integer'Image (Ada.Strings.Fixed.C
…
with Ada.Text_IO; use Ada.Text_IO;

procedure L21_Err is
begin
   if "apple" < "banana" then
      Put_Line ("before");
   end if;
end L21_Err;