首页 > 解决方案 > 检测行尾的 Ada 程序

问题描述

我被分配了这个任务作为我的家庭作业。我有一个文件,其中包含不同长度的文本行。该程序应该以与写入文件的顺序完全相同的顺序将数据写入屏幕,但它没有这样做。为了达到预期的效果,我尝试每次迭代只读取一个字符,以便检测换行符。我究竟做错了什么?

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;

标签: ada

解决方案


对于每个文件,Ada 运行时维护一个虚构的“光标”。这不是典型的文件位置光标(索引),而是指示页面、行等位置的光标(另见RM A.10 (7))。这在某种程度上是 Ada 早期版本的继承。

Get源于同一时代,预计在读取某些特定控制字符(例如行尾标记)时更新此光标的位置。如果Get读取这样的控制字符,它将仅使用它来更新光标(内部),然后继续读取下一个字符(另见RM A.10.7 (3))。因此,您在使用时永远不会检测到行尾标记Get

然而,这种行为会带来一些令人不安的后果:如果文件以一系列控制字符结尾,那么Get将继续读取这些字符并到达文件末尾,从而导致End_Error异常。

当然,您可以捕获此异常并处理它,但这种构造是可疑的,因为在文件末尾具有一系列控制字符实际上并不是这种异常情况(因此如果值得异常,则值得怀疑)。但是,作为程序员,您无法更改此行为:它由语言定义,并且语言不会更改,因为已决定保持 Ada(高度)向后兼容(考虑到其应用领域,这本身是可以理解的)。

因此,在您的情况下,如果您想坚持逐个字符的处理方法,我建议您远离Get并使用(例如)流来执行 I/O,如下例所示。

主文件

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;

输出符合预期(即ascii-art.de文件的内容)。

注意:检查GNAT 运行时的源代码以实际了解Get内部是如何工作的(关注最后的循环)。


推荐阅读