阿达用字符串切片

时间:2011-02-07 20:21:52

标签: ada gnat

我是一位很长时间的C ++程序员,为了好玩而学习Ada。如果以下任何一种形式不好,请随时指出。我正在努力学习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 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;

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

2 个答案:

答案 0 :(得分:7)

首先,我表示同情。 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是例外之一。如果你不太关心性能(如果你是从用户那里读取这个字符串就不应该这样做),你可以使用Text_IO中的Carlisle's routine to read in a perfectly-sized fixed string

答案 1 :(得分:4)

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

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

还有另一个GNAT包,GNAT.Array_Split(预先实现了字符串为GNAT.String_Split),它提供了更多的子程序,用于分解数组(和字符串)。