首页 文章

阿达用字符串切片

提问于
浏览
3

我是一名很长时间的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 回答

  • 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之间的差异2.您的 in 参数不会被复制 . 这一点非常重要,所以我会大声说出来: Ada parameters are not passed like C parameters! 确切的规则有点复杂,但为了你的目的,原则是阿达会做出明智的事情 . 如果参数可以适合寄存器,则它将通过复制(或可能是寄存器)传递 . 如果参数太大,它将通过引用传递 . 无论如何,你都不允许修改它们 . 那将是屈膝的 . 只有完全白痴(或C编译器)会做这样的事情 . 如果您发现Ada编译器正在将其报告为错误 . 这将是 .

    最后,在大多数情况下,创造性地使用Ada的范围规则将允许您使用完美大小的常量"fixed"字符串 . 您几乎不需要使用动态字符串或单独的长度变量 . 可悲的是, Ada.Text_IO.Get_Line 是例外之一 . 如果您从用户那里读取此字符串,则不能使用't care too much about performance (which you shouldn',您可以使用Text_IO中的Carlisle's routine to read in a perfectly-sized fixed string .

  • 4

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

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

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

相关问题