Ada程序检测行尾

Ada program to detect an end of line

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

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 运行时维护一个虚构的 "cursor"。这不是典型的文件位置光标(索引),而是指示页面、行等位置的光标(另请参见 RM A.10 (7))。这在某种程度上继承了 Ada 的早期版本。

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

时,您永远不会检测到行尾标记

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

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

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

main.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;

输出符合预期(即 the file 在 ascii-art.de 的内容)。

注意:检查 source code of the GNAT runtime 以实际了解 Get 的内部工作方式(关注末尾的循环)。

正如 DeeDee 所解释的,文本输入在 Ada 中是按行缓冲的。这个想法是能够在同一行读取两个整数。为了保持一致性(Ada 的设计者对此很挑剔......),Get(File, C) 也这样做。在您的情况下这是不切实际的。幸运的是,Ada 95 引入了 Get_Immediate,正好解决了这个问题。

否则,按照 Frédéric 的建议,您可以使用 函数 Get_Line 逐行无缝吸收 Winnie_The_Pooh.txt。顺便说一下,Get_Line 方法会自动转换不同的行尾约定。

Ada.Text_IO 中的行终止符是一个概念,而不是文件中的字符或字符序列。 (虽然最常用的文件系统将它们实现为文件中的字符或字符序列,但存在不这样做的文件系统。)因此必须使用包中的操作来操作行终止符。对于阅读,End_Of_Line 检查光标是否在行终止符处,Skip_Line 跳过下一行终止符,Get_Line 可能跳过行终止符。对于写作,New_LinePut_Line 写行终止符。

对于你的问题,规范的解决方案是使用Get_Line函数读取行,并使用Put_Line输出读取的行。