Ada,使用slice时字符串拆分索引错误



我正在尝试从文本文件中读取,将每一行分割成用空格分隔的片段,放入循环单链表中,执行插入/删除操作并打印结果。代码编译了,但运行时给我";引发GNAT.STRING_SPLIT_INDEX_ERROR:g-arrspl.adb:335在g-strspl.ads:39"处实例化;。

我查看了g-arrspl.adb第335行的文档,其中写道:

elsif索引>S.D.N_切片引发Index_Error;

由此,我猜我在某个地方不知何故超过了指数限制,但我不知道如何或在哪里。

这是我迄今为止的代码

with Ada.Text_IO;
use Ada.Text_IO;
with Ada.Float_Text_IO;
use Ada.Float_Text_IO;
with GNAT.String_Split;
use GNAT.String_Split;
procedure Main is
package IIO is new Ada.Text_IO.Integer_IO(Integer);
use IIO;
type DepartmentType is(Sales, Crew, IT, Media, Accounting, noDept);
type TitleType is(manager, sales_person, designer, programmer, software_engineer, spokes_person, pilot, copilot, scientist, mission_specialist, notitle);
type NameType is(Bob, Mary, Sally, David, Marty, Vallerie, Sam, Joe, noName);
type CodeType is(IL, IR, DL, DR, PA, PD, DD);

type fileContent is record
code: CodeType;
dept: DepartmentType;
name: NameType;
title: TitleType;
id: Integer := 0;
payrate: Float := 0.00;
end record;
type empNode;
type empPt is access empNode;
type empNode is record
deptName: DepartmentType := noDept;
empName: NameType := noName;
title: TitleType := notitle;
id: Integer := 0;
payrate: Float := 0.00;
next: empPt := null;
end record;
type departmentNode;
type deptPt is access departmentNode;
type departmentNode is record
deptName: DepartmentType;
empName: NameType;
num: Integer := 0;
next: empPt := null;
end record;


fileOut: File_Type;
fileLine: fileContent;
limit: Positive;

package FloatIO is new Ada.Text_IO.Float_IO(Float);
use FloatIO;
package DeptTypeIO is new Ada.Text_IO.Enumeration_IO(DepartmentType);
use DeptTypeIO;
package TitleTypeIO is new Ada.Text_IO.Enumeration_IO(TitleType);
use TitleTypeIO;
package NameTypeIO is new Ada.Text_IO.Enumeration_IO(NameType);
use NameTypeIO;
package CodeTypeIO is new Ada.Text_IO.Enumeration_IO(CodeType);
use CodeTypeIO;

Department: array(DepartmentType) of departmentNode;

inFile: FILE_TYPE;
lineSection: Slice_Set;
procedure inLeftNode(deptNameIn: DepartmentType; empNameIn: NameType; titleIn: TitleType; IDin: Integer; payRateIn: float) is
input: empPt;
begin
input := new empNode'(deptNameIn, empNameIn, titleIn, IDin, payRateIn, null);
if(Department(deptNameIn).next = null) then      --empty list
input.next := input;
Department(deptNameIn).next := input;
else                                             --populated list
input.next := Department(deptNameIn).next;
Department(deptNameIn).next := input;
end if;
Department(deptNameIn).num := Department(deptNameIn).num + 1;        --number in list
end inLeftNode;
procedure inRightNode(deptNameIn: DepartmentType; empNameIn: NameType; titleIn: TitleType; IDin: Integer; payRateIn: float) is
last: empPt;
first: empPt;
input: empPt;
begin
input := new empNode'(deptNameIn, empNameIn, titleIn, IDin, payRateIn, null);
if(Department(deptNameIn).next = null) then
input.next := input;
Department(deptNameIn).next := input;
else
last := Department(deptNameIn).next;
first := last.next;
input.next := Department(deptNameIn).next;
Department(deptNameIn).next := input;
end if;
Department(deptNameIn).num := Department(deptNameIn).num + 1;
Department(deptNameIn).next := input;
end inRightNode;
procedure deleteLeftNode(deptNameIn: DepartmentType) is
p: empPt;
begin
if(Department(deptNameIn).next = null) then
put_line("Underflow");
else
p := Department(deptNameIn).next;
Department(deptNameIn).next := p.next;
if p = Department(deptNameIn).next then
Department(deptNameIn).next := null;
end if;
end if;

end deleteLeftNode;
procedure deleteRightNode(deptNameIn: DepartmentType) is
Dp, prev, p: empPt;
begin
if(Department(deptNameIn).next = null) then
put_line("Underflow");
else
p := Department(deptNameIn).next;
while p.next /= Department(deptNameIn).next loop
p := p.next;
end loop;
Dp := p.next;
prev := p;
Department(deptNameIn).next := p;
p.next := Department(deptNameIn).next;
if Dp = Department(deptNameIn).next then
Department(deptNameIn).next := null;
end if;
end if;
end deleteRightNode;
procedure deleteDepartment(deptNameIn: DepartmentType) is
begin
null;
end deleteDepartment;
procedure printAll is
begin
null;
end printAll;

procedure printDept(deptNameIn: DepartmentType) is
pt: empPt;
begin
if(Department(deptNameIn).next = null) then
put_line("Department is empty");
put_line(fileOut, "Department is empty");
else
pt := Department(deptNameIn).next;
for emp in 1 .. 5 loop
put(pt.empName);
put(" ");
put(fileOut, pt.empName);
put(fileOut, " ");
pt := pt.next;
end loop;
end if;
end printDept;

begin
Open(inFile, In_File, "Cinput.txt");
skip_line(inFile);
while not End_Of_File (inFile) loop
declare
begin
Create(fileOut, Out_File, "output.txt");
Create(lineSection, get_line(inFile), " ", Multiple);

CodeTypeIO.Get(slice(lineSection, 1), fileLine.code, limit);
DeptTypeIO.Get(slice(lineSection, 2), fileLine.dept, limit);
NameTypeIO.Get(slice(lineSection, 3), fileLine.name, limit);
TitleTypeIO.Get(slice(lineSection, 4), fileLine.title, limit);
IIO.Get(slice(lineSection, 5), fileLine.id, limit);
FloatIO.Get(slice(lineSection, 6), fileLine.payrate, limit);

case fileLine.code is
when IL =>
inLeftNode(fileLine.dept, fileLine.name, fileLine.title, fileLine.id, fileLine.payrate);

when IR =>
inRightNode(fileLine.dept, fileLine.name, fileLine.title, fileLine.id, fileLine.payrate);

when DL =>
deleteLeftNode(fileLine.dept);

when DR =>
deleteRightNode(fileLine.dept);

when PA =>
printAll;

when PD =>
printDept(fileLine.dept);

when DD =>
deleteDepartment(fileLine.dept);

when others =>
null;
end case;
end;
end loop;
end Main;

我试着改变printDept中的数值,以为它可能在那里,但我得到了同样的结果。

我认为应该在循环之外创建输出文件。除此之外,这似乎是依赖于数据的(即,当我尝试它时,在一个短数据集上,没有问题(。使用-g -gnateE编译可能会提供更多帮助(希望能显示代码中向String_Split提供错误输入的位置(。使用调试器并说出catch exception,然后获取回溯,应该会有所帮助。此外,您可以在阅读时打印每一行,这样您就可以判断是什么输入导致了问题。

另一方面,异常清楚地(对我来说(表明您正试图从只有N-1个标记的行中读取第N个标记。

这里需要的是堆栈跟踪,以找出导致引发异常的调用序列。。。

这是编译器和操作系统特有的;但假设你正在使用Gnat;addr2line";对于您的操作系统。。。

  1. 使用调试和异常跟踪信息构建可执行文件

  2. 运行可执行文件以引发异常

  3. 使用addr2line和异常跟踪来查看调用序列。

更详细(参考Debian 10上的Gnat-8.3(

  1. 构建可执行文件。代替

    gnatmake spliterror.adb

将调试(-g(添加到编译标志,将异常跟踪(-bargs-E(添加到绑定参数,并根据gnat和addr2line的版本,关闭与位置无关的链接(-largs-no-pie(。。。查看结果。

(如果您使用的是gprbuild,或者从GPS IDE运行,您会将附加选项放入.gpr文件中(

gnatmake -g spliterror.adb -bargs -E -largs -no-pie
  1. 运行可执行文件以收集异常跟踪

你会注意到我的错误,但有一个不同的例外:对于你不完整的复制机,我能做的最好。

./spliterror
`Execution of ./spliterror terminated by unhandled exception  
raised ADA.IO_EXCEPTIONS.NAME_ERROR : Cinput.txt: No such file or directory  
Call stack traceback locations:
0x7f64c5a89782 0x7f64c59f5a37 0x403ca1 0x403a9f 0x7f64c5690099 0x4034f8 0xfffffffffffffffe
  1. 使用addr2line查看调用顺序:

不完整,因为我还没有安装Ada运行时系统的调试版本(或者有,但链接在非调试版本中,或者没有编译带有调试标志-g的RTS,或者其他…(

addr2line --exe=spliterror 0x7f6c16529782 0x7f6c16495a37 0x403ca1 0x403a9f 0x7f6c16130099 0x4034f8 0xfffffffffffffffe
??:0
??:0
/home/brian/Ada/Play/spliterror.adb:164
/home/brian/Ada/Play/b~spliterror.adb:260
??:0
??:?
??:0

但您可以看到重要的一点:错误与源文件中的第164行有关:它恰好是:Open(inFile, In_File, "Cinput.txt");

正如预期的那样。但是,用输入文件再现这个可能会非常接近实际错误。您也可以使用例如gdb来获得堆栈跟踪,但我发现这是一个重量轻得多的过程,并且不记得必须在Ada程序上使用gdb。

最后一点需要记住:这个过程在没有-largs -no-pie的情况下一直工作,直到Debian上的gcc(可能是6.3(的最新版本,但操作系统策略在PIE和地址空间随机化方面发生了变化:addr2line没有跟上这一变化,并报告(无用的(

addr2line --exe=spliterror 0x7fad79382782 0x7fad792eea37 0x55a9b4f96cb5 0x55a9b4f96ab3 0x7fad78f89099 0x55a9b4f96508 0xfffffffffffffffe
??:0
??:0
??:0
??:0
??:0
??:0
??:0

其必然结果是,依赖位置的可执行文件可能被视为潜在的安全风险,因此使用-largs -no-pie构建可能只用于调试,并在调试完成后关闭。

最新更新