开发者

How to I build a string from other strings in Ada?

开发者 https://www.devze.com 2022-12-19 23:49 出处:网络
I want to output a header line in a log file and then a line of \'-\' before the data. To do this I create a string of the header and then outpout the same number of \'-\'.

I want to output a header line in a log file and then a line of '-' before the data. To do this I create a string of the header and then outpout the same number of '-'.

But the below code always fails with a CONSTRAINT_ERROR because the generated string is not 1024 characters. In Ada string assignments r开发者_如何学编程equire exactly the same length not just sufficient capacity.

Option 1) is to compute the exact length but that is brittle to future changes. Option 2) is to use something other than String.

procedure F() is 
    Msg : String(1..1024);
begin
    Open_Log();
    Msg :=       FLS(" Field1", 12) &
           "|" & FLS(" Field2", 12) &
           "|" & FLS(" Field3", 16);

    Log_To_File("# " & Msg);
    Log_To_File("# " & Fill_String(Msg'Last, '-'));
end;


A lot of folks who are used to the C way of building strings in steps have trouble wrapping their minds around Ada strings, which you are supposed to initialize and use as-is. When you grok this fact about Ada strings, the solution becomes much simpler. I can even throw out your "Fill" routine.

procedure F() is  
   Msg : constant String
      := FLS(" Field1", 12) & 
       "|" & FLS(" Field2", 12) & 
       "|" & FLS(" Field3", 16); 
   Separator : constant String := (1..Msg'length => '-'); --'
begin 
   Open_Log(); 

   Log_To_File("# " & Msg); 
   Log_To_File("# " & Separator); 
end;

(Note: The comment is a hack to get SO's colorizer back on track)

If you didn't have to have the separator the same length, you wouldn't even need to declare the variable.

If it were me, I'd do something like have Log_To_File keep track of lengths and generate its own properly-sized separator upon request. Then you could just write:

Open_Log();
Log_To_File ("# " & FLS(" Field1", 12) & 
       "|" & FLS(" Field2", 12) & 
       "|" & FLS(" Field3", 16)); 
Log_Separator_To_File;


Just declare Msg as a String instead of a String(1 .. 1024)

procedure F() is 

    Msg: String  
    :=       FLS(" Field1", 12) &
       "|" & FLS(" Field2", 12) &
       "|" & FLS(" Field3", 16);
    --// this 'magically' declares Msg as a String(1 .. Something)
    --// with the right Something

begin
   Open_Log();

   Log_To_File("# " & Msg);
   Log_To_File("# " & Fill_String(Msg'Last, '-')); --'
end;


One approach might be to write a function that fills a fixed length string with a dynamically sized input string, padding with spaces:

procedure Pad_String(Str: in String; Dest: out String; Len: out Integer) is
begin
    Len := Str'Last - Str'First + 1;
    Dest(Dest'First .. Dest'First + Len - 1) := Str(Str'First .. Str'First + Len - 1);
    Dest(Dest'First + Len .. Dest'Last) := Fill_String(Dest'Last - Len, ' ');
end Pad_String;

Ada's string handling allows you to pass any fixed length buffer into Dest and the 'First and 'Last attributes will be correct within the body of the procedure.

Then, your code could become:

procedure F() is     
    Msg : String(1..1024);    
    Len : Integer;
begin    
    Open_Log();    
    Pad_String(      FLS(" Field1", 12) &    
               "|" & FLS(" Field2", 12) &    
               "|" & FLS(" Field3", 16),
               Msg,
               Len);

    Log_To_File("# " & Msg(1 .. Len));    
    Log_To_File("# " & Fill_String(Len, '-'));    
end;    


As a convenience, you can use the String constructor functions in Ada.Strings.Fixed, Ada.Strings.Bounded or Ada.Strings.Unbounded. These overload the * operator to "replicate a character or string a specified number of times." For example,

with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
   ...
   Log_To_File("# " & Length(Msg) * '-');


I worked out how to use Unbounded_String. That type will accept other sized strings.

You can't build an unbounded string with the & operator unless you are using unbounded strings so use the To_Unbounded_String function.

with Ada.Strings.Unbounded;
procedure F() is  
   use Ada.Strings.Unbounded;
   Msg : Unbounded_String;
begin 
   Open_Log(); 
   Msg := Ada.Strings.Unbounded.To_Unbounded_String(
                FLS(" Field1", 12) & 
          "|" & FLS(" Field2", 12) & 
          "|" & FLS(" Field3", 16)); 

   Log_To_File("# " & Ada.Strings.Unbounded.To_String(Msg)); 
   Log_To_File("# " &
          Fill_String(Ada.Strings.Unbounded.Length(Msg), '-')); 
end; 
0

精彩评论

暂无评论...
验证码 换一张
取 消