Ada-(Streams)如何正确调用String';在事先不知道字符串长度的情况下读取()

Ada-(Streams)如何正确调用String';在事先不知道字符串长度的情况下读取(),string,serialization,stream,ada,String,Serialization,Stream,Ada,我正在尝试编写一个快速程序,将AT命令发送到串行端口调制解调器。我用正确的设置(B115200、8N1等)打开了端口,下面代码示例中的字符串“Write”调用实际上工作正常 现在我添加代码,将调制解调器的响应作为字符串读回。但是,我无法事先知道响应的长度,因此我无法创建一个字符串变量来传入out String参数,除非我知道长度 package GSC renames GNAT.Serial_Communications; SP : aliased GSC.Serial_Port;

我正在尝试编写一个快速程序,将AT命令发送到串行端口调制解调器。我用正确的设置(B115200、8N1等)打开了端口,下面代码示例中的字符串“Write”调用实际上工作正常

现在我添加代码,将调制解调器的响应作为字符串读回。但是,我无法事先知道响应的长度,因此我无法创建一个字符串变量来传入
out String
参数,除非我知道长度

  package GSC renames GNAT.Serial_Communications;

  SP : aliased GSC.Serial_Port;

  function Send (Port : in GSC.Serial_Port; S : in String) return String is
  begin
    String'Write (SP'Access, S);
    delay 0.1;

    declare
      Retval : String;  -- NOT VALID - needs to be initialised
    begin
      String'Read (SP'Access, Retval);
      return Retval;
    end;
  end Send;

我这里有一个鸡/蛋的情况。

答案可能是每次读取输入的一个字符,直到到达终止符


您可以分配足够长的缓冲区来容纳尽可能长的响应(例如1024字节!)(或者使用递归-但这将更加复杂,并且使诊断可能的溢出错误变得困难)。

如果字符串以特定字符终止,您可以使用
接口。C.指针

function Receive (Port : in GSC.Serial_Port) return String is
   package Character_Pointers is new Interfaces.C.Pointers (
     Index => Positive, Element => Character, Element_Array => String,
     Default_Terminator => Character'Val (13)); -- CR-Terminated
   function Convert is new Ada.Unchecked_Conversion (
     Source => access all Streams.Stream_Element,
     Target => Character_Pointers.Pointer);
   --  assuming no more than 1023 characters + terminator can be given.
   Max_Elements : constant Streams.Stream_Element_Offset :=
     1024 * Character'Size / Streams.Stream_Element'Size;
   Buffer : Streams.Stream_Element_Array (1 .. Max_Elements);
   Last : Stream_Element_Offset;
begin
   Port.Read (Buffer, Last);
   return Characters_Pointers.Value (Convert (Buffer (1)'Access));
end Receive;
该代码做出了以下几个假设:

  • 字符串以CR终止(可通过适当设置
    默认值\终止符来修改)
  • 响应只包含字符串(字符串被静默丢弃后可能已读取的其他内容)
  • 整个内容的长度永远不会超过1024字节

    • 实现这一点的典型方法是先发送长度,然后读取值。(这就是像
      bencode
      这样的东西所做的事情。)--比如:

      -- A stream from Standard-Input; for passing to example parameters:
      Some_Stream: not null access Ada.Streams.Root_Stream_Type'Class :=
            Ada.Text_IO.Text_Streams.Stream( Ada.Text_IO.Standard_Input );
      
      -- The simple way, use the 'Input attribute; this calls the appropriate
      -- default deserializations to return an unconstrained type.
      -- HOWEVER, if you're reading from an already extant data-stream, you may
      -- need to customize the type's Input function.
      Some_Value : Constant String := String'Input( Some_Stream );
      
      -- If the stream places a length into the stream first, you can simply read
      -- it and use that value, to prealocate the proper size and fill it with the
      -- 'Read attribure.
      Function Get_Value( Input : not null access Ada.Streams.Root_Stream_Type'Class ) return String is
          Length : Constant Natural := Natural'Input( Input );
      Begin
          Return Result : String(1..Length) do
              String'Read( Input, Result );
          End Return;
      End Get_Value;
      
      -- The last method is to use when you're dealing with buffered information.
      -- (Use this if you're dealing with idiocy like null-terminated strings.)
      Function Get_Buffered_Value( Input : not null access Ada.Streams.Root_Stream_Type'Class;
                                   Buffer_Size : Positive := 1024;
                                   Full_Buffer : Boolean  := True;
                                   Terminator  : Character:= ASCII.NUL
                                  ) return String is
          Buffer : String(1..Buffer_Size);
      Begin
          -- Full_Buffer means we can read the entire buffer-size w/o
          -- "overconsuming" -- IOW, the stream is padded to buffer-length.
          if full_buffer then
              String'Read(Input, Buffer);
              declare
                  Index : Natural renames Ada.Strings.Fixed.Index(
                     Source  => Buffer,
                     Pattern => (1..1 => Terminator),
                     From    => Buffer'First
                    );
              begin
                  Return Buffer(Buffer'First..Index);
              end;
          else
              declare
                  Index : Positive := Buffer'First;
              begin
                  -- Read characters.
                  loop
                      Character'Read( Input, Buffer(Index) );
                      exit when Buffer(Index) = Terminator;
                      Index:= Positive'Succ( Index );
                      exit when Index not in Buffer'Range;
                  end loop;
      
                  -- We're returning everything but the terminator.
                  Return Buffer(1..Positive'Pred(Index));
              end;
          end if;
      End Get_Buffered_Value;
      

      哦,递归——这些深度不受内部限制——是一个好主意,因为设备出现故障而导致堆栈溢出。