2011-02-07 144 views
3

我是一个很长时间的C++程序员学习Ada的乐趣。如果以下任何一种情况不好,请随时指出。我试图学习Ada的方式来做事,但旧习惯很难打破(我错过了助推器!)Ada与字符串切片

我试图加载一个文件,其中包含一个整数,一个空格,然后字符串。可能有更好的方法来做到这一点,但我认为我应该加载到一个字符串缓冲区,我知道不会超过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”参数传递给过程时,是在堆栈上创建的副本,还是对使用的原始字符串的引用?

回答

6

首先,我的同情心。 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会做明智的事情。如果参数可以放入寄存器中,它将通过复制(或者可能是寄存器)传递。如果参数太大,它将通过引用传递。你不能决定这一点。这是一个优化问题,将由编译器完成。但是你可以指望编译器不创建大数组的副本,只是为了将它们传递给不允许修改它们的例程。那将是stoopid。只有一个白痴(或一个C++编译器)会做这样的事情。如果您发现Ada编译器会将其报告为一个错误。这将是。

最后,在大多数情况下,创意使用Ada的作用域规则将允许您使用完美大小的常量“固定”字符串。几乎不需要使用动态字符串或单独的长度变量。可悲的是,Ada.Text_IO.Get_Line是一个例外。如果你不关心性能(如果你正在从用户那里读取这个字符串,你不应该这么做),你可以使用Text_IO中的Carlisle's routine to read in a perfectly-sized fixed string

+0

这些属性很光滑。不用担心索引值是很好的。 Ada的另一个特点是,我正在学习欣赏(除了类型和目前为止的纯可读性) – 2011-02-07 21:19:18

+0

如TED所暗示的那样,您可以定义子类型,然后使用这些类型的范围来创建和索引数组,甚至是范围非标准像(-5 .. 5),它不需要将任何索引抵消到0或1 :) – NWS 2011-02-15 15:12:18

3

如果您可以使用GNAT实现定义的软件包,则可以使用软件包Ada.Strings.Unbounded.Text_IO。另外,Ada.Strings子包(特定于Fixed,Bounded或Unbounded字符串)为字符串处理提供了一些有用的子程序,如用于在其他字符串中查找特定字符串的Index() - 用于定位嵌入的空白: - )

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