Ada - (Streams) 如何在事先不知道字符串长度的情况下正确调用 String'Read ()
Ada - (Streams) How to correctly call String'Read () without knowing string length beforehand
我正在尝试编写一个快速程序来将 AT 命令发送到串行端口调制解调器。我已使用正确的设置(B115200、8N1 等)打开端口,下面代码示例中的 String'Write 调用确实正常工作。
现在我要添加代码以将调制解调器的响应作为字符串读回。但是我无法事先知道响应的长度,因此我无法创建一个字符串变量来传递给 out String
参数,除非我 do 知道长度。
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 字节!)(或者可能使用递归 - 但那样会更复杂并且难以诊断可能的超限错误)。
如果字符串以特定字符结尾,您可以使用 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 字节。
实现这一点的典型方法是先发送长度,然后读取值。 (这就是 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;
我正在尝试编写一个快速程序来将 AT 命令发送到串行端口调制解调器。我已使用正确的设置(B115200、8N1 等)打开端口,下面代码示例中的 String'Write 调用确实正常工作。
现在我要添加代码以将调制解调器的响应作为字符串读回。但是我无法事先知道响应的长度,因此我无法创建一个字符串变量来传递给 out String
参数,除非我 do 知道长度。
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 字节!)(或者可能使用递归 - 但那样会更复杂并且难以诊断可能的超限错误)。
如果字符串以特定字符结尾,您可以使用 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 字节。
实现这一点的典型方法是先发送长度,然后读取值。 (这就是 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;