我正在尝试从文本文件中读取,将每一行分割成用空格分隔的片段,放入循环单链表中,执行插入/删除操作并打印结果。代码编译了,但运行时给我";引发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";对于您的操作系统。。。
-
使用调试和异常跟踪信息构建可执行文件
-
运行可执行文件以引发异常
-
使用addr2line和异常跟踪来查看调用序列。
更详细(参考Debian 10上的Gnat-8.3(
-
构建可执行文件。代替
gnatmake spliterror.adb
将调试(-g(添加到编译标志,将异常跟踪(-bargs-E(添加到绑定参数,并根据gnat和addr2line的版本,关闭与位置无关的链接(-largs-no-pie(。。。查看结果。
(如果您使用的是gprbuild,或者从GPS IDE运行,您会将附加选项放入.gpr文件中(
gnatmake -g spliterror.adb -bargs -E -largs -no-pie
- 运行可执行文件以收集异常跟踪
你会注意到我的错误,但有一个不同的例外:对于你不完整的复制机,我能做的最好。
./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
- 使用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
构建可能只用于调试,并在调试完成后关闭。