3

我是一个长期学习 Ada 的 C++ 程序员。如果以下任何一种形式不好,请随时指出。我正在尝试学习 Ada 做事的方式,但旧习惯很难改掉(我想念 Boost!)

我正在尝试加载一个包含整数、空格和字符串的文件。可能有更好的方法来做到这一点,但我认为我应该将该行加载到我知道不会超过 80 个字符的字符串缓冲区中。我在适当的地方声明了一个缓冲区变量,如下所示:

 Line_Buffer : String(1..80);

打开文件后,我循环遍历每一行并在空格字符处拆分缓冲区:

 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;

Add_Entry 中发生的事情并不重要,但它的规范如下所示:

 procedure Add_Entry(
   Root_Link : in out Link;
   ID_String : in String;
   Entry_String : in String);

我想使用无界字符串而不是有界字符串,因为我不想担心必须在这里和那里指定大小。这编译并且工作正常,但是在 Add_Entry 中,当我尝试遍历 Entry_String 中的每个字符时,而不是让索引从 1 开始,它们从原始字符串中的偏移量开始。例如,如果 Line_Buffer 是“14 硅”,如果我如下循环,则索引从 4 变为 10。

for Index in Entry_String'Range loop
  Ada.Text_IO.Put("Index: " & Integer'Image(Index));
  Ada.Text_IO.New_Line;  
end loop;

有没有更好的方法来进行这种解析,以便我传递给 Add_Entry 的字符串具有以 1 开头的边界?此外,当我将切片字符串作为“in”参数传递给过程时,是在堆栈上创建了一个副本,还是使用了对原始字符串的引用?

4

2 回答 2

8

首先,我的同情。Ada 字符串可能是 C++ 和 Ada 之间最不同的东西。更糟糕的是,这些差异隐藏在表面之下,因此天真的 C/C++ 编码人员开始他们的 Ada 职业生涯时认为他们可能不存在,他们可以像对待 C 字符串一样对待 Ada 字符串。现在针对您的具体问题:

Ada 数组(包括字符串)都有隐含的边界。这意味着通常不需要特殊的标记值(如 nul),也很少需要单独的长度变量。这也意味着没有什么特别的10或任何其他索引。

因此,在 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;

好的。现在对于 Ada 和 C 之间的区别二。您的in参数没有被复制。这一点很重要,所以我会喊一下:Ada参数不像C参数那样传递!确切的规则有点复杂,但为了您的目的,原则是 Ada 会做明智的事情。如果参数可以放入寄存器,它将通过副本(或者可能是寄存器)传递。如果参数太大,它将通过引用传递。你不能决定这个。这是一个优化问题,将由编译器完成。但是您可以指望您的编译器不会创建大型数组的副本,只是将它们传递给不允许修改它们的例程。那将是愚蠢的。只有彻头彻尾的白痴(或 C++ 编译器)才会这样做。如果你发现有一个 Ada 编译器在执行它,请将其报告为错误。这将是。

最后,在大多数情况下,创造性地使用 Ada 的范围规则将允许您使用大小完美的常量“固定”字符串。您几乎不需要使用动态字符串或单独的长度变量。可悲的是,Ada.Text_IO.Get_Line是例外之一。如果您不太关心性能(如果您从用户那里读取此字符串,则不应该这样做),您可以使用Carlisle 的例程从 Text_IO读取大小合适的固定字符串。

于 2011-02-07T20:39:45.677 回答
4

如果您可以使用 GNAT 实现定义的包,包Ada.Strings.Unbounded.Text_IO可用。

此外,Ada.Strings 子包(特定于 Fixed、Bounded 或 Unbounded 字符串)为字符串处理提供了一些有用的子程序,例如 Index() 用于在其他字符串中查找特定字符串——用于定位嵌入的空白 :-)

还有另一个 GNAT 包,GNAT.Array_Split(预先实例化为 GNAT.String_Split 字符串),它提供了更多面向拆分数组(和字符串)的子程序。

于 2011-02-07T21:09:27.000 回答