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

Posted

技术标签:

【中文标题】Ada - (Streams) 如何在事先不知道字符串长度的情况下正确调用 String\'Read()【英文标题】:Ada - (Streams) How to correctly call String'Read () without knowing string length beforehandAda - (Streams) 如何在事先不知道字符串长度的情况下正确调用 String'Read() 【发布时间】:2020-03-20 20:41:20 【问题描述】:

我正在尝试编写一个快速程序来将 AT 命令发送到串行端口调制解调器。我已经使用正确的设置(B115200、8N1 等)打开了端口,并且下面代码示例中的 String'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;

我这里有鸡/蛋的情况。

【问题讨论】:

【参考方案1】:

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

您可以分配一个足够长的缓冲区来保存可能的最长响应(例如 1024 字节!)(或者可以使用递归 - 但这会更复杂,并且难以诊断可能的溢出错误)。

【讨论】:

哦,递归那些深度在内部不受限制是一个好主意,因为设备故障而导致堆栈溢出。【参考方案2】:

如果字符串以特定字符结尾,您可以使用Interfaces.C.Pointers:

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 结尾(可以通过适当设置 Default_Terminator 来修改)。 响应只包含字符串(在字符串被静默丢弃后可能已读取的其他内容)。 整个内容永远不会超过 1024 字节。

【讨论】:

【参考方案3】:

实现这一点的典型方法是先发送长度,然后读取值。 (这就是 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;

【讨论】:

以上是关于Ada - (Streams) 如何在事先不知道字符串长度的情况下正确调用 String'Read()的主要内容,如果未能解决你的问题,请参考以下文章

Ada 和 Python 之间的 TCP 套接字

覆盖在Ada中接收类范围类型作为参数的过程

如何在事先不知道数量的情况下制作多个 BETWEEN 语句? (通过IP范围检测国家)

如何使用 Java Streams API 添加和更新地图条目

HTML 和 BeautifulSoup:当结构并不总是事先知道时如何迭代解析?

Numpy:当您事先不知道排名时,在最后一个轴上建立索引