Question

I'm trying to define an array of Strings in Ada to store Strings of variable size. The problem I have is that I must pre-define the size of my Strings which I don't know at compile time, and with the Unbounded_Strings the String_Split.Create wouldn't work, as it requires Standard.String.

Below is my code, where I need to be able to parse Strings of variable size, and not just the fixed length 4.

Thanks in advance.

type StrArray is array(1..7) of String(1..4);

function Split(Line : String;Sep : String) return StrArray is
    Tokens : String_Split.Slice_Set;
    Output : StrArray;
    Count : Natural := 0;
begin
    String_Split.Create(s => Tokens,
                        From => Line,
                        Separators => Sep,
                        Mode => String_Split.Single);

    For I in 1 .. String_Split.Slice_Count (Tokens) loop
         Count := Count + 1;
         Output(Count) := String_Split.Slice(Tokens,I);  -- Not sure how to convert                Slice_Count to Integer either!
    end loop;

    return Output;

end Split;
Was it helpful?

Solution

The fact that GNAT.String_Split uses String doesn’t mean that your StrArray has to. And you need to cater for input strings with varying numbers of tokens, so begin by declaring StrArray as an unconstrained array type:

type StrArray is array (Positive range <>)
  of Ada.Strings.Unbounded.Unbounded_String;

Now Split begins like this:

function Split (Line : String; Sep : String) return StrArray is
   Tokens : GNAT.String_Split.Slice_Set;
begin

(we can’t declare Output yet, and we won’t need Count; and I had to go to Google to find out that String_Split is a GNAT utility package).

The first thing to do is to split the input line, so that we know how large Output needs to be (by the way, do you really want Single?):

   GNAT.String_Split.Create (S => Tokens,
                             From => Line,
                             Separators => Sep,
                             Mode => GNAT.String_Split.Single);

Now we can declare Output using the Slice_Count. Note the conversion to Natural (not Positive; if the input is an empty string, there will be no tokens, so the output will be an empty array, range 1 .. 0).

   declare
      Output : StrArray
        (1 .. Natural (GNAT.String_Split.Slice_Count (Tokens)));
   begin

Now fill Output with the tokens. AdaCore chose to implement Slice_Number as new Natural, rather than as a subtype of Natural, which is why we need the conversion.

      for I in Output'Range loop
         Output (I) :=
           Ada.Strings.Unbounded.To_Unbounded_String
             (GNAT.String_Split.Slice
                (Tokens, GNAT.String_Split.Slice_Number (I)));
      end loop;

... and return Output, within the declare block.

      return Output;
   end;

end Split;

To call Split, which is going to return a StrArray whose length you don’t know beforehand, you can use the technique of constraint by initial value:

declare
   T : constant StrArray := Split ("goodbye  world ", " ");
begin
   for J in T'Range loop
      Ada.Text_IO.Put_Line ("'"
                              & Ada.Strings.Unbounded.To_String (T (J))
                              & "'");
   end loop;
end;
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top