第18课|输入输出详解

10 阅读5分钟

本课目标

完成本课后,你将能够:

  • 掌握 Ada.Text_IO 的基本输入输出操作
  • 熟练使用 Ada.Integer_Text_IOAda.Float_Text_IO
  • 理解文件输入输出的各种模式与操作
  • 学会格式化输出与用户交互
  • 应用输入输出构建交互式程序

一、标准输入输出概述

Ada 的输入输出通过泛型包子包实现,主要分为:

  • Ada.Text_IO:文本输入输出(字符、字符串、行)
  • Ada.Integer_Text_IO:整数输入输出
  • Ada.Float_Text_IO:浮点数输入输出
  • Ada.Enumeration_Text_IO:枚举输入输出(需实例化)

所有标准输入输出包都基于 Ada.Text_IO 扩展。

1.1 基本输出

with Ada.Text_IO;

procedure Basic_Output is
begin
   Ada.Text_IO.Put ("Hello");        -- 不换行
   Ada.Text_IO.Put (" World");
   Ada.Text_IO.New_Line;              -- 换行
   Ada.Text_IO.Put_Line ("Hello Ada"); -- 输出并换行
end Basic_Output;

1.2 基本输入

with Ada.Text_IO;

procedure Basic_Input is
   S : String (1 .. 80);
   Last : Integer;
begin
   Ada.Text_IO.Get_Line (S, Last);   -- 读取一行到 S,Last 是实际长度
   Ada.Text_IO.Put_Line ("You entered: " & S (1 .. Last));
end Basic_Input;

二、整数与浮点数输入输出

2.1 Ada.Integer_Text_IO

with Ada.Text_IO;
with Ada.Integer_Text_IO;

procedure Int_IO is
   X : Integer;
begin
   -- 输出整数
   Ada.Integer_Text_IO.Put (42);           -- 输出 "42"
   Ada.Integer_Text_IO.Put (42, Width => 5); -- 输出 "   42"
   Ada.Text_IO.New_Line;
   
   -- 输入整数
   Ada.Text_IO.Put ("Enter an integer: ");
   Ada.Integer_Text_IO.Get (X);
   Ada.Text_IO.Put_Line ("You entered: " & Integer'Image (X));
end Int_IO;

常用过程

  • Put (Item : in Num; Width : in Field := Default_Width):输出整数,Width 指定最小字符宽度
  • Get (Item : out Num):从标准输入读取整数,跳过空白

2.2 Ada.Float_Text_IO

with Ada.Text_IO;
with Ada.Float_Text_IO;

procedure Float_IO is
   X : Float;
begin
   -- 输出浮点数
   Ada.Float_Text_IO.Put (3.14159);           -- 默认格式
   Ada.Float_Text_IO.Put (3.14159, Fore => 3, Aft => 2, Exp => 0);
   -- Fore:整数部分宽度,Aft:小数位数,Exp:指数宽度(0表示不使用指数)
   Ada.Text_IO.New_Line;
   
   -- 输入浮点数
   Ada.Text_IO.Put ("Enter a float: ");
   Ada.Float_Text_IO.Get (X);
   Ada.Text_IO.Put_Line ("You entered: " & Float'Image (X));
end Float_IO;

参数说明

  • Fore:整数部分最少字符数(包括负号)
  • Aft:小数部分位数(四舍五入)
  • Exp:指数部分字符数(0 表示不使用科学记数法)

三、枚举类型的输入输出

枚举类型需要实例化 Ada.Text_IO.Enumeration_IO 泛型包:

with Ada.Text_IO;

procedure Enum_IO is
   type Day is (Mon, Tue, Wed, Thu, Fri, Sat, Sun);
   package Day_IO is new Ada.Text_IO.Enumeration_IO (Day);
   use Day_IO;
   
   Today : Day := Wed;
begin
   -- 输出
   Put (Today);                    -- 输出 "WED"
   Put (Today, Width => 5);        -- 输出 "  WED"
   Put (Today, Set => Lower_Case); -- 输出 "wed"
   Ada.Text_IO.New_Line;
   
   -- 输入
   Ada.Text_IO.Put ("Enter day: ");
   Get (Today);                    -- 输入 "Mon" 等
end Enum_IO;

四、文件输入输出

4.1 文件类型与模式

Ada 使用 File_Type 类型表示文件,支持以下模式:

模式含义
In_File只读
Out_File只写(覆盖)
Append_File追加

4.2 基本文件操作

with Ada.Text_IO;

procedure File_Demo is
   In_File  : Ada.Text_IO.File_Type;
   Out_File : Ada.Text_IO.File_Type;
   Line     : String (1 .. 100);
   Last     : Integer;
begin
   -- 打开文件
   Ada.Text_IO.Open (In_File, Ada.Text_IO.In_File, "input.txt");
   Ada.Text_IO.Create (Out_File, Ada.Text_IO.Out_File, "output.txt");
   
   -- 读取并写入
   while not Ada.Text_IO.End_Of_File (In_File) loop
      Ada.Text_IO.Get_Line (In_File, Line, Last);
      Ada.Text_IO.Put_Line (Out_File, Line (1 .. Last));
   end loop;
   
   -- 关闭文件
   Ada.Text_IO.Close (In_File);
   Ada.Text_IO.Close (Out_File);
exception
   when Ada.Text_IO.Name_Error =>
      Ada.Text_IO.Put_Line ("File not found");
   when Ada.Text_IO.Status_Error =>
      Ada.Text_IO.Put_Line ("File already open");
end File_Demo;

4.3 文件状态检查

常用函数:

  • End_Of_File (File):是否到达文件末尾
  • Is_Open (File):文件是否已打开
  • Mode (File):返回当前模式
  • Name (File):返回文件名

4.4 追加模式

procedure Append_Demo is
   File : Ada.Text_IO.File_Type;
begin
   Ada.Text_IO.Open (File, Ada.Text_IO.Append_File, "log.txt");
   Ada.Text_IO.Put_Line (File, "New log entry");
   Ada.Text_IO.Close (File);
end Append_Demo;

五、格式化输出

5.1 使用 Put 的宽度控制

-- 整数宽度
Ada.Integer_Text_IO.Put (123, Width => 5);  -- "  123"
Ada.Integer_Text_IO.Put (-45, Width => 4); -- " -45"

-- 浮点数格式
Ada.Float_Text_IO.Put (12.3456, Fore => 4, Aft => 2, Exp => 0);
-- 输出 "  12.35"(四舍五入)

5.2 字符串格式化

使用 Ada.Strings.Fixed 中的函数:

with Ada.Strings.Fixed;

procedure String_Format is
   S : String := Ada.Strings.Fixed.Trim ("  Hello  ", Ada.Strings.Both);
begin
   Ada.Text_IO.Put_Line ("'" & S & "'");  -- 'Hello'
end String_Format;

5.3 创建表格

procedure Print_Table is
begin
   Ada.Text_IO.Put_Line ("Name     Age  Score");
   Ada.Text_IO.Put_Line ("-------- ---  -----");
   for I in 1 .. 3 loop
      Ada.Text_IO.Put ("Alice    ");
      Ada.Integer_Text_IO.Put (20 + I, Width => 3);
      Ada.Text_IO.Put ("  ");
      Ada.Float_Text_IO.Put (85.5 + Float (I) * 2.0, Fore => 1, Aft => 1, Exp => 0);
      Ada.Text_IO.New_Line;
   end loop;
end Print_Table;

六、用户交互示例

6.1 菜单驱动程序

with Ada.Text_IO;
with Ada.Integer_Text_IO;

procedure Menu_Demo is
   Choice : Integer;
begin
   loop
      Ada.Text_IO.Put_Line ("=== Menu ===");
      Ada.Text_IO.Put_Line ("1. Option 1");
      Ada.Text_IO.Put_Line ("2. Option 2");
      Ada.Text_IO.Put_Line ("0. Exit");
      Ada.Text_IO.Put ("Your choice: ");
      Ada.Integer_Text_IO.Get (Choice);
      
      case Choice is
         when 1 =>
            Ada.Text_IO.Put_Line ("You chose option 1");
         when 2 =>
            Ada.Text_IO.Put_Line ("You chose option 2");
         when 0 =>
            Ada.Text_IO.Put_Line ("Goodbye");
            exit;
         when others =>
            Ada.Text_IO.Put_Line ("Invalid choice");
      end case;
   end loop;
end Menu_Demo;

6.2 输入验证

procedure Safe_Input is
   X : Integer;
begin
   loop
      Ada.Text_IO.Put ("Enter a positive integer: ");
      begin
         Ada.Integer_Text_IO.Get (X);
         if X > 0 then
            exit;
         else
            Ada.Text_IO.Put_Line ("Must be positive");
         end if;
      exception
         when Ada.Text_IO.Data_Error =>
            Ada.Text_IO.Put_Line ("Invalid number");
            Ada.Text_IO.Skip_Line;  -- 清空缓冲区
      end;
   end loop;
   Ada.Text_IO.Put_Line ("You entered: " & Integer'Image (X));
end Safe_Input;

七、完整示例:成绩管理系统

-- 文件名: grade_manager.adb
-- 功能:管理学生成绩,支持添加、查看、保存到文件

with Ada.Text_IO;
with Ada.Integer_Text_IO;
with Ada.Float_Text_IO;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
with Ada.Strings.Unbounded.Text_IO;

procedure Grade_Manager is
   
   Max_Students : constant := 100;
   
   type Student_Record is record
      Name  : Unbounded_String;
      Grade : Float;
   end record;
   
   Students : array (1 .. Max_Students) of Student_Record;
   Count    : Integer := 0;
   
   -- 添加学生
   procedure Add_Student is
      Name : Unbounded_String;
      Grade : Float;
   begin
      Ada.Text_IO.Put ("Name: ");
      Ada.Strings.Unbounded.Text_IO.Get_Line (Name);
      Ada.Text_IO.Put ("Grade: ");
      Ada.Float_Text_IO.Get (Grade);
      Count := Count + 1;
      Students (Count) := (Name => Name, Grade => Grade);
      Ada.Text_IO.Put_Line ("Student added.");
   exception
      when Ada.Text_IO.Data_Error =>
         Ada.Text_IO.Put_Line ("Invalid grade");
         Ada.Text_IO.Skip_Line;
   end Add_Student;
   
   -- 显示所有学生
   procedure Show_All is
   begin
      Ada.Text_IO.Put_Line ("=== Student List ===");
      for I in 1 .. Count loop
         Ada.Text_IO.Put (To_String (Students (I).Name) & ": ");
         Ada.Float_Text_IO.Put (Students (I).Grade, Fore => 1, Aft => 2, Exp => 0);
         Ada.Text_IO.New_Line;
      end loop;
   end Show_All;
   
   -- 保存到文件
   procedure Save_To_File (Filename : String) is
      File : Ada.Text_IO.File_Type;
   begin
      Ada.Text_IO.Create (File, Ada.Text_IO.Out_File, Filename);
      for I in 1 .. Count loop
         Ada.Text_IO.Put (File, To_String (Students (I).Name));
         Ada.Text_IO.Put (File, ",");
         Ada.Float_Text_IO.Put (File, Students (I).Grade, Fore => 1, Aft => 2, Exp => 0);
         Ada.Text_IO.New_Line (File);
      end loop;
      Ada.Text_IO.Close (File);
      Ada.Text_IO.Put_Line ("Saved to " & Filename);
   end Save_To_File;
   
   Choice : Integer;
   
begin
   loop
      Ada.Text_IO.New_Line;
      Ada.Text_IO.Put_Line ("1. Add student");
      Ada.Text_IO.Put_Line ("2. Show all");
      Ada.Text_IO.Put_Line ("3. Save to file");
      Ada.Text_IO.Put_Line ("0. Exit");
      Ada.Text_IO.Put ("Choice: ");
      Ada.Integer_Text_IO.Get (Choice);
      
      case Choice is
         when 1 => Add_Student;
         when 2 => Show_All;
         when 3 => Save_To_File ("grades.csv");
         when 0 => exit;
         when others => Ada.Text_IO.Put_Line ("Invalid");
      end case;
   end loop;
end Grade_Manager;

八、本课总结

  • Ada.Text_IO 提供基本文本输入输出,Put_LineGet_LineNew_Line 最常用
  • 整数用 Ada.Integer_Text_IO,浮点数用 Ada.Float_Text_IO
  • 枚举需实例化 Enumeration_IO 泛型包
  • 文件操作:OpenCreateClose,模式有 In_FileOut_FileAppend_File
  • 使用 WidthForeAft 控制输出格式
  • 输入时要处理 Data_Error 异常并清理缓冲区

九、课后练习

  1. 基本输入输出:编写程序读取两个整数,输出它们的和、差、积、商。

  2. 文件复制:实现一个文件复制程序,源文件名和目标文件名由用户输入。

  3. 格式化表格:输出一个10行2列的表格,第一列为1到10,第二列为平方根(保留2位小数)。

  4. 学生成绩文件:从CSV文件读取学生姓名和成绩,计算平均分并输出。

  5. 交互式菜单:实现一个简单的计算器,支持加减乘除,持续运行直到用户选择退出。


十、下节预告

第19课|字符串处理

我们将:

  • 掌握固定字符串与无界字符串的使用
  • 学习字符串的查找、替换、分割操作
  • 理解字符串与数值的转换
  • 应用字符串处理解决实际问题

关键术语表

Ada.Text_IO:标准文本输入输出包

Put:输出不换行的过程

Put_Line:输出并换行的过程

Get_Line:读取一行字符串的过程

New_Line:输出换行符

File_Type:文件对象类型

In_File / Out_File / `Append_File:文件访问模式

Data_Error:输入数据格式错误时触发的异常

End_Of_File:检测文件末尾的函数

Width:输出整数或枚举时的最小宽度

Fore / Aft:浮点数输出时整数部分宽度和小数部分位数