Ada 字符串切片

标签 ada gnat

我是一个长期学习 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”参数传递给过程时,是在堆栈上创建了副本,还是对使用的原始字符串的引用?

最佳答案

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

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

因此,在 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是异常(exception)之一。如果您不太关心性能(如果您从用户那里读取此字符串,则不应该这样做),您可以使用 Carlisle's routine to read in a perfectly-sized fixed string来自 Text_IO。

关于Ada 字符串切片,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4926117/

相关文章:

艾达 Hello World 示例

ada - Ada 编程语言的 GUI

ada - 操作只能以一种类型进行调度

.net - 在 Ada 中使用 .net 命令

ada - 访问 Ada 中的 volatile 寄存器

linux - 在 WSL 上链接 Arm 时出现错误 "__aeabi_unwind_cpp_pr0"

ada - 如何序列化和反序列化私有(private)类型?

types - Ada 中子类型的非连续范围?

c++ - 如何将退出代码从 Ada 进程返回到调用 Windows 进程?

ada - 让 Ada(用 GNAT 编译)从当前目录外部导入文件?