我被分配这项任务作为我的家庭作业。我有一个文件,其中包含不同长度的文本行。该程序应该按照与写入文件的顺序完全相同的顺序将数据写入屏幕,但它未能这样做。为了达到预期的结果,我尝试每次迭代仅读取一个字符,以便检测新行字符。我究竟做错了什么?
WITH Ada.Text_IO;
WITH Ada.Characters.Latin_1;
USE Ada.Text_IO;
PROCEDURE ASCII_artwork IS
File : File_Type;
c : Character;
BEGIN
Open(File, In_File, "Winnie_The_Pooh.txt");
WHILE NOT End_Of_File(File) LOOP
Get(File, C);
IF (C = Ada.Characters.Latin_1.LF) THEN Put_Line(" "); ELSE
Put(C);
END IF;
END LOOP;
Close(File);
END ASCII_Artwork;
Run Code Online (Sandbox Code Playgroud)
对于每个文件,Ada 运行时都维护一个虚构的“光标”。这不是典型的文件位置光标(索引),而是指示页面、行等上的位置的光标(另请参见RM A.10 (7))。这在某种程度上继承了 Ada 的早期版本。
Get源于同一时代,并且预计在读取某些特定控制字符(例如行尾标记)时更新此光标的位置。如果Get读取这样的控制字符,它只会使用它来更新光标(内部),然后继续读取下一个字符(另请参见RM A.10.7(3))。因此,在使用 时,您永远不会检测到行尾标记Get。
然而,这种行为会产生一些令人不舒服的后果:如果文件以一系列控制字符结尾,则将Get继续读取这些字符并到达文件末尾,从而导致异常End_Error。
当然,您可以捕获此异常并处理它,但这样的构造是可疑的,因为在文件末尾有一系列控制字符实际上并不是这样的异常情况(因此如果值得异常则值得怀疑)。然而,作为一名程序员,您无法更改此行为:它是由语言定义的,并且语言不会更改,因为它已决定保持 Ada(高度)向后兼容(考虑到其应用领域,这本身是可以理解的)。
因此,就您的情况而言,如果您想坚持逐字符处理方法,我建议您放弃Get并使用(例如)流来执行 I/O,如下例所示。
主程序.adb
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Text_IO.Text_Streams; use Ada.Text_IO.Text_Streams;
procedure ASCII_artwork IS
File : File_Type;
Input : Stream_Access;
Output : Stream_Access;
C : Character;
begin
Open (File, In_File, "Winnie_The_Pooh.txt");
Input := Stream (File);
Output := Stream (Standard_Output);
while not End_Of_File (File) loop
Character'Read (Input, C);
Character'Write (Output, C);
end loop;
Close(File);
end ASCII_Artwork;
Run Code Online (Sandbox Code Playgroud)
输出符合预期(即ascii-art.de文件的内容)。
注意:检查GNAT 运行时的源代码以实际了解Get其内部工作原理(重点关注最后的循环)。