2011-02-07 198 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)提供了更多的子程序,以分解數組(和字符串)。