我很长时间都在学习Ada的C++程序员.如果以下任何一种形式不好,请随时指出.我正在努力学习Ada做事的方法,但旧习惯很难打破(我想念Boost!)
我正在尝试加载包含整数,空格和字符串的文件.可能有更好的方法来做到这一点,但我认为我应该将该行加载到一个字符串缓冲区,我知道它不会超过80个字符.我在适当的地方声明了如下的缓冲区变量:
Line_Buffer : String(1..80);
Run Code Online (Sandbox Code Playgroud)
打开文件后,我遍历每一行并在空格字符处拆分缓冲区:
while not Ada.Text_IO.End_Of_File(File_Handle) loop
Ada.Text_IO.Get_Line(File_Handle, Item=>Line_Buffer, Last=>Last);
-- Break line at space to get match id and entry
for String_Index in Line_Buffer'Range loop
if Line_Buffer(String_Index) = ' ' then
Add_Entry(Root_Link=>Root_Node,
ID_String=> Line_Buffer(1..String_Index-1),
Entry_String=> Line_Buffer(String_Index+1..Last-1)
);
end if;
end loop;
end loop;
Run Code Online (Sandbox Code Playgroud)
Add_Entry中发生的事情并不重要,但其规范如下所示:
procedure Add_Entry(
Root_Link : in out Link;
ID_String : in String;
Entry_String : in String);
Run Code Online (Sandbox Code Playgroud)
我想使用无界字符串而不是有界字符串,因为我不想担心必须在这里和那里指定大小.这编译并正常工作,但在Add_Entry中,当我尝试遍历Entry_String中的每个字符时,而不是从1开始的索引,它们从原始字符串中的偏移量开始.例如,如果Line_Buffer为"14 silicon",如果我循环如下,则索引从4到10.
for Index in Entry_String'Range loop
Ada.Text_IO.Put("Index: " & Integer'Image(Index));
Ada.Text_IO.New_Line;
end loop;
Run Code Online (Sandbox Code Playgroud)
有没有更好的方法来进行这种解析,以便传递给Add_Entry的字符串具有以1开头的边界?另外,当我将切片字符串作为"in"参数传递给过程时,是在堆栈上创建的副本,还是对所用原始字符串的引用?
首先,我的同情.Ada字符串可能是C++和Ada之间最不同的东西.更糟糕的是,差异在表面之下,所以天真的C/C++编码员开始他们的Ada职业生涯,认为他们可能不存在,他们可以像处理C字符串那样对待Ada字符串.现在针对您的具体问题:
Ada数组(包括字符串)都具有与它们传递的隐式边界.这意味着通常不需要特殊的标记值(如nul),并且很少需要单独的长度变量.这也意味着有什么特别的地方1或者0或任何其他指数.
因此,在Ada中处理数组的正确方法是,您不要在子例程中假设您的起始和结束边界是什么.你搞清楚了.该语言提供'first,'last并且'range专门用于此目的.从你的例子中,如果你想从给定字符串的开头打印偏移量(出于一些奇怪的原因),它将是:
for Index in Entry_String'Range loop
Ada.Text_IO.Put("Index offset: " & Integer'Image(Index-Entry_string'first));
Ada.Text_IO.New_Line;
end loop;
Run Code Online (Sandbox Code Playgroud)
好.现在为Ada和C之间的差异二.您的in参数不会被复制.这个非常重要,所以我会大声说:Ada参数不像C参数那样传递!确切的规则有点复杂,但为了你的目的,原则是阿达会做出明智的事情.如果参数可以适合寄存器,则它将通过复制(或可能是寄存器)传递.如果参数太大,它将通过引用传递.你无法决定这一点.这是一个优化问题,将由编译器完成.但是你可以指望你的编译器不创建大型数组的副本只是为了将它们传递给一个不允许修改它们的例程.那将是屈膝的.只有完全白痴(或C++编译器)会做这样的事情.如果您发现Ada编译器正在将其报告为错误.这将是.
最后,在大多数情况下,创造性地使用Ada的范围规则将允许您使用完美大小的常量"固定"字符串.您几乎不需要使用动态字符串或单独的长度变量.可悲的是,Ada.Text_IO.Get_Line是例外之一.如果你不太关心性能(如果你从用户那里读取这个字符串就不应该这样做),你可以使用Carlisle的例程从Text_IO中读取一个完美大小的固定字符串.
如果您同意使用 GNAT 实现定义的包,则可以使用Ada.Strings.Unbounded.Text_IO包。
此外,Ada.Strings 子包(特定于固定、有界或无界字符串)为字符串处理提供了一些有用的子程序,例如 Index() 用于在其他字符串中查找特定字符串 - 对于定位嵌入的空白很有用:-)
还有另一个 GNAT 包,GNAT.Array_Split(它用字符串预实例化为 GNAT.String_Split),它提供了更多面向分解数组(和字符串)的子程序。