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;