C++基础13:C++输入输出

此专栏为移动机器人知识体系下的编程语言中的 C {\rm C} C++从入门到深入的专栏,参考书籍:《深入浅出 C {\rm C} C++》(马晓锐)和《从 C {\rm C} C到 C {\rm C} C++精通面向对象编程》(曾凡锋等)。



12.C++输入/输出
12.1 C++流类
  • 计算机的输入和输出是数据传送的过程, C {\rm C} C++将数据的传送过程称为流 ( s t r e a m ) ({\rm stream}) (stream);

  • C {\rm C} C++的 I / O {\rm I/O} I/O流是由若干字节组成的字节序列,称为字节流,字节流中的数据可以是多种形式的,包括:二进制数据、 A S C I I {\rm ASCII} ASCII字符、数字化视频和音频、数字图像信息等;

  • 在进行数据传输时,数据会被从一个对象传送到另一个对象中,这些对象包括:内存、输出设备、输入设备,当进行输入操作时,字节流是从输入设备流向内存,当进行输出操作时,字节流是从内存输出到输出设备;

  • 内存缓冲区:是系统为数据流开辟的一个内存空间,用于存放流中的数据;如果利用输入/输出操作对流进行传送,系统先将数据存入缓冲区中,当缓冲区满或遇到特定的输出指令时,才将这些数据进行传送;

  • 用 c o u t {\rm cout} cout向屏幕输出时,系统将输出的内容先保存在为其开辟的缓冲区内,当此缓冲区满或遇到 e n d l {\rm endl} endl时,才将数据传送到屏幕上显示;利用 c i n {\rm cin} cin进行输入操作时,数据先保存在键盘输入缓冲区,遇到回车时将数据传送到程序输入缓冲区,当程序遇到 < < {\rm <<} <<操作符时,输入缓冲区的数据会被赋予相关的变量;

  • 系统提供缓冲区机制的目的:为了减少数据传送频度,以提高 I / O {\rm I/O} I/O效率;

  • C {\rm C} C++中,输入/输出流被定义为一系列的类,这些类称为流类 ( s t r e a m c l a s s ) ({\rm stream\ class}) (stream class),这些类被包含在 I / O {\rm I/O} I/O类库中,其输入输出包括如下方面的内容:

    • 标准 I / O {\rm I/O} I/O:对系统指定的标准设备,如:显示器、打印机等的输入和输出;
    • 文件 I / O {\rm I/O} I/O:对外存磁盘或外存储设备文件进行输入和输出;
    • 内存 I / O {\rm I/O} I/O(串 I / O {\rm I/O} I/O):对内存中的空间进行输入和输出;
  • 常用的 I / O {\rm I/O} I/O操作类如下表:

    类名 类名 类名 作用 作用 作用 头文件 头文件 头文件
    i o s {\rm ios} ios 输入 / 输出抽象基类 输入/输出抽象基类 输入/输出抽象基类 i o s t r e a m {\rm iostream} iostream
    i s t r e a m {\rm istream} istream 通用输入流和其他输入流基类 通用输入流和其他输入流基类 通用输入流和其他输入流基类 i o s t r e a m {\rm iostream} iostream
    o s t r e a m {\rm ostream} ostream 通用输出流和其他输出流基类 通用输出流和其他输出流基类 通用输出流和其他输出流基类 i o s t r e a m {\rm iostream} iostream
    i o s t r e a m {\rm iostream} iostream 通用输入 / 输出流和其他输入 / 输出流基类 通用输入/输出流和其他输入/输出流基类 通用输入/输出流和其他输入/输出流基类 i o s t r e a m {\rm iostream} iostream
    i f s t r e a m {\rm ifstream} ifstream 输入文件流类 输入文件流类 输入文件流类 f s t r e a m {\rm fstream} fstream
    o f s t r e a m {\rm ofstream} ofstream 输出文件流类 输出文件流类 输出文件流类 f s t r e a m {\rm fstream} fstream
    f s t r e a m {\rm fstream} fstream 输入 / 输出文件流类 输入/输出文件流类 输入/输出文件流类 f s t r e a m {\rm fstream} fstream
    i s t r s t r e a m {\rm istrstream} istrstream 输入字符串流 输入字符串流 输入字符串流 s t r s t r e a m {\rm strstream} strstream
    o s t r s t r e a m {\rm ostrstream} ostrstream 输出字符串流 输出字符串流 输出字符串流 s t r s t r e a m {\rm strstream} strstream
    s t r s t r e a m {\rm strstream} strstream 输入 / 输出字符串流 输入/输出字符串流 输入/输出字符串流 s t r s t r e a m {\rm strstream} strstream
  • 常用的 I / O {\rm I/O} I/O操作流类之间的关系图:

  • 几个常用的流类:

    • i o s t r e a m {\rm iostream} iostream:基本输入/输出流操作;
    • s t r e a m {\rm stream} stream:用于文件的 I / O {\rm I/O} I/O操作;
    • s t r s t r e a m {\rm strstream} strstream:用于字符串流输入/输出;
    • i o m a n i p {\rm iomanip} iomanip:用于对流中的数据进行格式化输入/输出;
12.2 流对象和格式化输出
  • 在 i o s t r e a m {\rm iostream} iostream头文件中,定义了许多相关的类,如: i o s 、 i s t r e a m 、 o s t r e a m 、 i o s t r e a m {\rm ios、istream、ostream、iostream} ios、istream、ostream、iostream等,还定义了 4 4 4种常用的流对象,如下表所示:

    对象 对象 对象 含义 含义 含义 对应设备 对应设备 对应设备 对应的类 对应的类 对应的类
    c i n {\rm cin} cin 标准输入流 标准输入流 标准输入流 键盘 键盘 键盘 i s t r e a m _ w i t h a s s i g n {\rm istream\_withassign} istream_withassign
    c o u t {\rm cout} cout 标准输出流 标准输出流 标准输出流 显示器 显示器 显示器 o s t r e a m _ w i t h a s s i g n {\rm ostream\_withassign} ostream_withassign
    c e r r {\rm cerr} cerr 标准错误流 标准错误流 标准错误流 显示器 显示器 显示器 o s t r e a m _ w i t h a s s i g n {\rm ostream\_withassign} ostream_withassign
    c l o g {\rm clog} clog 标准输出流 标准输出流 标准输出流 显示器 显示器 显示器 o s t r e a m _ w i t h a s s i g n {\rm ostream\_withassign} ostream_withassign
    • c i n {\rm cin} cin:标准输入流,是类 i s t r e a m _ w i t h a s s i g n {\rm istream\_withassign} istream_withassign的对象,作用为从系统指定的标准输入设备输入数据到程序变量中;
    • c o u t {\rm cout} cout:标准输出流,是类 o s t r e a m _ w i t h a s s i g n {\rm ostream\_withassign} ostream_withassign的对象,作用是将内存数据输出到系统指定的输出设备上;
    • c e r r {\rm cerr} cerr:标准错误流,是类 o s t r e a m {\rm ostream} ostream的对象,作用为向系统指定的标准输出设备输出错误信息;
    • c l o g {\rm clog} clog:标准输出流,是类 o s t r e a m {\rm ostream} ostream的对象,作用为向系统指定的标准输出设备输出日志信息, c e r r {\rm cerr} cerr没有缓冲区, c l o g {\rm clog} clog具有缓冲区,即 c e r r {\rm cerr} cerr结果会立即被显示到输出设备上, c l o g {\rm clog} clog在缓冲区满或遇到 e n d l {\rm endl} endl时才显示;
  • 对输出数据进行格式化称为流格式化输出,如果需要控制流输出的格式,一是使用流格式控制符,二是利用流类的相关成员函数进行控制;

  • 流格式控制符在头文件 i o m a n i p {\rm iomanip} iomanip中定义,使用控制符时需要包含头文件 i o m a n i p {\rm iomanip} iomanip,常用的输入/输出流控制符如下表:

    控制符 控制符 控制符 控制符作用 控制符作用 控制符作用
    d e c {\rm dec} dec 置基数为 10 , 相当 于 ′ % d ′ 置基数为10,相当于'\%{\rm d}' 置基数为10,相当于′%d′
    h e x {\rm hex} hex 置基数为 16 , 相当 于 ′ % X ′ 置基数为16,相当于'\%{\rm X}' 置基数为16,相当于′%X′
    o c t {\rm oct} oct 置基数为 8 , 相当 于 ′ % o ′ 置基数为8,相当于'\%{\rm o}' 置基数为8,相当于′%o′
    s e t b a s e ( n ) {\rm setbase(n)} setbase(n) 置基数为 n 置基数为{\rm n} 置基数为n
    s e t f i l l ( c ) {\rm setfill(c)} setfill(c) 设置填充字符为 c 设置填充字符为{\rm c} 设置填充字符为c
    s e t p r e c i s i o n ( n ) {\rm setprecision(n)} setprecision(n) 设置显示小数精度为 n 位 设置显示小数精度为{\rm n}位 设置显示小数精度为n位
    s e t w ( n ) {\rm setw(n)} setw(n) 设置域宽为 n 个字符 设置域宽为{\rm n}个字符 设置域宽为n个字符
    s e t i o s f l a g s ( i o s : : f i x e d ) {\rm setiosflags(ios::fixed)} setiosflags(ios::fixed) 固定的浮点显示 固定的浮点显示 固定的浮点显示
    s e t i o s f l a g s ( i o s : : s c i e n t i f i c ) {\rm setiosflags(ios::scientific)} setiosflags(ios::scientific) 指数表示 指数表示 指数表示
    s e t i o s f l a g s ( i o s : : l e f t ) {\rm setiosflags(ios::left)} setiosflags(ios::left) 左对齐 左对齐 左对齐
    s e t i o s f l a g s ( i o s : : r i g h t ) {\rm setiosflags(ios::right)} setiosflags(ios::right) 右对齐 右对齐 右对齐
    s e t i o s f l a g s ( i o s : : s k i p w s ) {\rm setiosflags(ios::skipws)} setiosflags(ios::skipws) 忽略前导空白 忽略前导空白 忽略前导空白
    s e t i o s f l a g s ( i o s : : u p p e r c a s e ) {\rm setiosflags(ios::uppercase)} setiosflags(ios::uppercase) 十六进制大写输出 十六进制大写输出 十六进制大写输出
    s e t i o s f l a g s ( i o s : : l o w e r c a s e ) {\rm setiosflags(ios::lowercase)} setiosflags(ios::lowercase) 十六进制小写输出 十六进制小写输出 十六进制小写输出
    s e t i o s f l a g s ( i o s : : s h o w p o i n t ) {\rm setiosflags(ios::showpoint)} setiosflags(ios::showpoint) 强制显示小数点 强制显示小数点 强制显示小数点
    s e t i o s f l a g s ( i o s : : s h o w p o s ) {\rm setiosflags(ios::showpos)} setiosflags(ios::showpos) 强制显示符号 强制显示符号 强制显示符号
  • 控制符实例:用控制符控制数据输出格式 ( e x a m p l e 12 _ 1. c p p ) ({\rm example12\_1.cpp}) (example12_1.cpp)。

    c++ 复制代码
    /**
     * 作者:罗思维
     * 时间:2024/03/31
     * 描述:用控制符控制数据输出格式。 
     */
    #include <iostream>
    #include <iomanip>
    
    using namespace std;
    
    int main() {
    	int iNumber;
    	cout << "请输入一个数字:";
    	cin >> iNumber;
    
    	cout << "iNumber十进制:" << dec << iNumber << endl;
    	cout << "iNumber十六进制:" << hex << iNumber << endl;
    	cout << "iNumber八进制:" << setbase(8) << iNumber << endl;
    
    	char *pStr = (char*)"Welcome";		// pStr指向字符串"Welcome"; 
    	cout << setw(20) << pStr << endl;	// 设置域宽为20,输出字符串; 
    	cout << setfill('-') << setw(20) << pStr << endl;	// 设置域宽为20,输出字符串,空白处用-填充; 
    
    	double dNumber = 5.845201314;
    	cout << setiosflags(ios::scientific) << setprecision(8);
    	cout << "dNumber = " << dNumber << endl;
    	cout << "dNumber = " << setprecision(4) << dNumber << endl;			
    	cout << "dNumber = " << setiosflags(ios::fixed) << dNumber << endl;	// 小数形式输出; 
    
    	return 0;
    }
  • 流对象 c o u t {\rm cout} cout常用的控制输出格式的成员函数如下:

    成员函数 成员函数 成员函数 对应的控制符 对应的控制符 对应的控制符 作用 作用 作用
    p r e c i s i o n ( n ) {\rm precision(n)} precision(n) s e t p r e c i s i o n ( n ) {\rm setprecision(n)} setprecision(n) 设置显示小数精度为 n 位 设置显示小数精度为{\rm n}位 设置显示小数精度为n位
    w i d t h ( n ) {\rm width(n)} width(n) s e t w ( n ) {\rm setw(n)} setw(n) 设置输出宽度为 n 位 设置输出宽度为{\rm n}位 设置输出宽度为n位
    f i l l ( c ) {\rm fill(c)} fill(c) s e t f i l l ( c ) {\rm setfill(c)} setfill(c) 当输出长度不足输出长度时 , 设置填充字符 c 当输出长度不足输出长度时,设置填充字符{\rm c} 当输出长度不足输出长度时,设置填充字符c
    s e t f ( f l a g ) {\rm setf(flag)} setf(flag) s e t i o s f l a g s ( f l a g ) {\rm setiosflags(flag)} setiosflags(flag) 设置输出格式状况 设置输出格式状况 设置输出格式状况
    u n s e t f ( ) {\rm unsetf()} unsetf() r e s e t i o s f l a g s ( ) {\rm resetiosflags()} resetiosflags() 终止已设置的输出格式状态 终止已设置的输出格式状态 终止已设置的输出格式状态

    设置格式状态的格式标志:

    • 对齐方式:
      • i o s : : l e f t {\rm ios::left} ios::left:输出数据左对齐;
      • i o s : : r i g h t {\rm ios::right} ios::right:输出数据右对齐;
      • i o s : : i n t e r n a l {\rm ios::internal} ios::internal:数值的符号位左对齐,数值右对齐,中间由填充字符填充,如:输出的数值为 − 10 -10 −10,输出长度为 5 5 5,填充字符位为 ∗ * ∗,则: − ∗ ∗ 10 -**10 −∗∗10;
    • 数值基数设定:
      • i o s : : d e c {\rm ios::dec} ios::dec:设置整数的基数为 10 10 10;
      • i o s : : o c t {\rm ios::oct} ios::oct:设置整数的基数为 8 8 8;
      • i o s : : h e x {\rm ios::hex} ios::hex:设置整数的基数为 16 16 16;
    • 整数格式控制:
      • i o s : : s h o w b a s e {\rm ios::showbase} ios::showbase:强制输出整数的基数;
      • i o s : : s h o w p o i n t {\rm ios::showpoint} ios::showpoint:强制输出浮点数的小数点和尾数 0 0 0;
      • i o s : : u p p e r c a s e {\rm ios::uppercase} ios::uppercase:以科学记数法格式 E {\rm E} E和以十六进制输出字母时以大写表示;
      • i o s : : s h o w p o s {\rm ios::showpos} ios::showpos:对整数显示 + + +号;
    • 浮点数格式控制:
      • i o s : : s c i e n t i f i c {\rm ios::scientific} ios::scientific:浮点数以科学记数法格式输出;
      • i o s : : f i x e d {\rm ios::fixed} ios::fixed:浮点数以定点格式(小数形式)输出;
    • 流控制:
      • i o s : : u n i t b u f {\rm ios::unitbuf} ios::unitbuf:每次输出后刷新所有的流;
      • i o s : : s t d i o {\rm ios::stdio} ios::stdio:每次输出后清除 s t d o u t 、 s t d e r r {\rm stdout、stderr} stdout、stderr;
  • 流控制成员函数实例:用流控制成员函数控制数据输出格式 ( e x a m p l e 12 _ 2. c p p ) ({\rm example12\_2.cpp}) (example12_2.cpp)。

    C++ 复制代码
    /**
     * 作者:罗思维
     * 时间:2024/03/31
     * 描述:用流控制成员函数控制数据输出格式。 
     */
    #include <iostream>
    
    using namespace std;
    
    int main() {
    	int nNumber = 80;
    
    	// 进制的设置与取消设置;
    	cout.setf(ios::showbase);
    	cout << "nNumber十进制:" << nNumber << endl;
    	cout.unsetf(ios::dec);
    
    	cout.setf(ios::hex);
    	cout << "nNumber十六进制:" << nNumber << endl;
    	cout.unsetf(ios::hex);
    
    	cout.setf(ios::oct);
    	cout << "nNumber八进制:" << nNumber << endl;
    	cout.unsetf(ios::oct);
    
    	// 域宽与填充的设置与取消设置;
    	char *pStr = (char*)"Welcome";
    	cout.width(20);
    	cout << pStr << endl;
    	cout.width(20);
    	cout.fill('-');
    	cout << pStr << endl;
    
    	// 小数输出形式设置;
    	double dNumber = 5.845201314;
    	cout.setf(ios::scientific);
    	cout << "dNumber = ";
    	cout.width(15);
    	cout << dNumber << endl;
    	cout.unsetf(ios::scientific);
    	cout.setf(ios::fixed);
    	cout.width(14);
    	cout.setf(ios::showpos);
    	cout.setf(ios::internal);
    	cout.precision(5);
    	cout << dNumber << endl;
    
    	return 0;
    }
12.3 重载流运算符
  • 运算符重载的两种形式:重载为成员函数或重载为友元函数,重载插入运算符和提取运算符时,左边的参数是流,右边的参数是类的对象,因此,只能用友元函数的方式去重载插入运算符和提取运算符;

  • 插入运算符重载的语法格式:

    c++ 复制代码
    ostream& operator<<(ostream& os,const T& t)
    {
        ...;	// 函数体;
        return os;
    }
    • 第一个参数:是对 o s t r e a m {\rm ostream} ostream对象的引用,函数返回值必须是这个引用对象;
    • 第二个参数:为输出的对象, T {\rm T} T是用户自定义的类, t {\rm t} t为该类的对象名;
  • 插入运算符重载实例:复数类的输出 ( C o m p l e x 1 ) ({\rm Complex1}) (Complex1)。

    • C C o m p l e x {\rm CComplex} CComplex类定义头文件 ( C C o m p l e x . h ) ({\rm CComplex.h}) (CComplex.h):

      c++ 复制代码
      /**
       * 作者:罗思维
       * 时间:2024/03/31
       * 描述:复数类定义头文件。
       */
      #include <iostream>
      
      using namespace std;
      
      class CComplex {
      	protected:
      		double real;				// 复数的实部;
      		double imag;				// 复数的虚部;
      	public:
      		CComplex(double pr = 0.0, double pi = 0.0) {
      			real = pr;
      			imag = pi;
      		};
      		virtual ~CComplex();
      		// 重载插入运算符; 
      		friend ostream& operator<<(ostream&, const CComplex&);
      };
    • C C o m p l e x {\rm CComplex} CComplex类实现文件 ( C C o m p l e x . c p p ) ({\rm CComplex.cpp}) (CComplex.cpp):

      c++ 复制代码
      /**
       * 作者:罗思维
       * 时间:2024/03/31
       * 描述:复数类实现文件。
       */
      
      #include "CComplex.h"
      
      CComplex::~CComplex() {
      }
      
      // 重载插入运算符实现; 
      ostream& operator<<(ostream& os, const CComplex& c) {
      	os << "(" << c.real << "," << c.imag << "i" << ")" << endl;
      	return os;
      }
    • 程序主文件 ( m a i n . c p p ) ({\rm main.cpp}) (main.cpp):

      c++ 复制代码
      /**
       * 作者:罗思维
       * 时间:2024/03/31
       * 描述:程序主文件。
       */
      #include "CComplex.h"
      
      int main() {
      	CComplex a(1, 2);
      	cout << a;
      
      	return 0;
      }
  • 提取运算符重载的语法格式:

    c++ 复制代码
    istream& operator>>(istream& is,const T& t)
    {
        ...;	// 函数体;
        return is;
    }
    • 第一个参数:为输入流 i s t r e a m {\rm istream} istream对象的引用,函数返回值必须是这个引用对象;
    • 第二个参数:为提取的对象, T {\rm T} T是用户自定义的类, t {\rm t} t为该类的对象名;
  • 提取运算符重载实例:复数类的输入 ( C o m p l e x 2 ) ({\rm Complex2}) (Complex2)。

    • C C o m p l e x {\rm CComplex} CComplex类定义头文件 ( C C o m p l e x . h ) ({\rm CComplex.h}) (CComplex.h):

      c++ 复制代码
      /**
       * 作者:罗思维
       * 时间:2024/03/31
       * 描述:复数类定义头文件。
       */
      #include <iostream>
      
      using namespace std;
      
      class CComplex {
      	protected:
      		double real;				// 复数的实部;
      		double imag;				// 复数的虚部;
      	public:
      		CComplex(double pr = 0.0, double pi = 0.0) {
      			real = pr;
      			imag = pi;
      		};
      		virtual ~CComplex();
      
      		// 重载插入运算符;
      		friend ostream& operator<<(ostream&, const CComplex&);
      
      		// 重载提取运算符;
      		friend istream& operator>>(istream&, CComplex&);
      };
    • C C o m p l e x {\rm CComplex} CComplex类实现文件 ( C C o m p l e x . c p p ) ({\rm CComplex.cpp}) (CComplex.cpp):

      c++ 复制代码
      /**
       * 作者:罗思维
       * 时间:2024/03/31
       * 描述:复数类实现文件。
       */
      
      #include "CComplex.h"
      
      CComplex::~CComplex() {
      }
      
      // 重载插入运算符实现;
      ostream& operator<<(ostream& os, const CComplex& c) {
      	os << "(" << c.real << "," << c.imag << "i" << ")" << endl;
      	return os;
      }
      
      // 重载提取运算符实现;
      istream& operator>>(istream& is, CComplex& c) {
      	cout << "请输入一个复数:";
      	is >> c.real >> c.imag;
      
      	return is;
      }
    • 程序主文件 ( m a i n . c p p ) ({\rm main.cpp}) (main.cpp):

      c++ 复制代码
      /**
       * 作者:罗思维
       * 时间:2024/03/31
       * 描述:程序主文件。
       */
      #include "CComplex.h"
      
      int main() {
      	CComplex a;
      	cin >> a;
      	cout << a;
      
      	return 0;
      }
12.4 文件操作
  • C {\rm C} C++中对文件的读/写是通过流来操作的,操作文件的流称为文件流;

  • 文件是一组由有限且相关的数据组成的有序集合,"有限"指文件中的数据有最大限度,"相关"指这些数据有关联性;

  • 文件通常存储在外部存储设备上,当程序需要使用文件时,才将其中的数据读入内存;

  • 文件可以分为:普通文件和设备文件;

    • 普通文件:指存储在外部设备上的文件;
    • 设备文件:指与计算机主机相关联的各种外部设备,如:显示器、打印机、鼠标、键盘等;
  • 按照文件存储时的编码方式分为:文本文件( A S C I I {\rm ASCII} ASCII码文件)和二进制文件。

    • 文本文件:此类文件在存储时,以 A S C I I {\rm ASCII} ASCII码作为内容来存储,每一个字符对应文件中的一个字节,这些字节按照顺序被存储在磁盘上;
    • 二进制文件:将其内容按照二进制编码的方式来存放的文件;
  • 文本文件和二进制文件存储举例:

  • 在对文本文件进行 I / O {\rm I/O} I/O操作时,需要进行格式转换,二进制文件不需要进行转换;

  • 对文件进行 I / O {\rm I/O} I/O操作的步骤:

    • 打开文件:在读写文件前,需要定义一个文件流类的对象,并用该对象打开文件,得到文件的一个句柄;
    • 读写文件:打开文件后,文件对象中会有一个指向文件中当前位置的指针,该指针的初始位置取决于打开文件时开发者设置的参数,默认情况为 0 0 0;每次从文件读出数据或向文件写入信息后,文件指针自动向后移动相应的字节,对于文本文件,文件指针移动一个字节,对于二进制文件,由数据类型决定;读文件时,如果文件指针移到文件的末尾,则读出的内容是文件结束符;
    • 关闭文件:完成文件的所有操作,需要关闭文件;
  • 常用的文件流对象有: i f s t r e a m 、 o f s t r e a m 、 f s t r e a m {\rm ifstream、ofstream、fstream} ifstream、ofstream、fstream;

    • i f s t r e a m {\rm ifstream} ifstream:输入文件流对象;
    • o f s t r e a m {\rm ofstream} ofstream:输出文件流对象;
    • f s t r e a m {\rm fstream} fstream:输入/输出文件流对象;
  • 打开文件前,先根据需要进行的操作声明流对象:

    c++ 复制代码
    ifstream file_in;			// 建立输入文件流对象;
    ofstream file_out;			// 建立输出文件流对象;
    fstream file_io;			// 建立输入/输出文件流对象;
  • 打开文件的两种方式:一是通过文件流的构造函数,二是利用成员函数 o p e n ( ) {\rm open()} open();

    c++ 复制代码
    // 1.利用文件流对象构造函数打开文件;
    ofstream file_out("C:\\a_out.dat",ios::out|ios::binary);	// 以二进制方式打开输出文件;
    ifstream file_in("C:\\a_in.dat",ios::in|ios::binary);		// 以二进制方式打开输入文件;
    
    // 2.常用文件流对象构造函数:
    ifstream::ifstream(const char*,int=ios::in,int=filebuf::openprot);
    ofstream::ofstream(const char*,int=ios::out,int=filebuf::openprot);
    fstream::fstream(const char*,int,int=filebuf::openprot);
    
    // 3.文件流类的成员函数open()原型:
    void open(const char *,int);
    
    // 4.三个常用的文件流的默认成员函数原型:
    void ifstream::open(const char*,int=ios::in);
    void ofstream::open(const char*,int=ios::out);
    void fstream::open(const char*,int);
    
    // 4.1 参数说明:
    // 参数1:代表文件名的字符串,一般需要完整的路径名,若文件在与exe相同的目录,则可以使用相对路径;
    // 参数2:文件的打开方式;

    文件的打开方式:

    常用 常用 常用 作用 作用 作用
    i o s : : i n {\rm ios::in} ios::in 打开文件用于数据输入 , 即从文件中读数据 打开文件用于数据输入,即从文件中读数据 打开文件用于数据输入,即从文件中读数据
    i o s : : o u t {\rm ios::out} ios::out 打开文件用于数据输出 , 即向文件中写数据 打开文件用于数据输出,即向文件中写数据 打开文件用于数据输出,即向文件中写数据
    i o s : : a t e {\rm ios::ate} ios::ate 打开文件后将文件指针置在文件尾部 打开文件后将文件指针置在文件尾部 打开文件后将文件指针置在文件尾部
    i o s : : a p p {\rm ios::app} ios::app 打开文件用于追加数据 , 文件指针始终指向文件尾部 打开文件用于追加数据,文件指针始终指向文件尾部 打开文件用于追加数据,文件指针始终指向文件尾部
    i o s : : t r u n c {\rm ios::trunc} ios::trunc 当打开文件已存在时 , 则清除其内容 , 即擦除以前所有数据 , 使之成为空文件 当打开文件已存在时,则清除其内容,即擦除以前所有数据,使之成为空文件 当打开文件已存在时,则清除其内容,即擦除以前所有数据,使之成为空文件
    i o s : : n o c r e a t e {\rm ios::nocreate} ios::nocreate 如果打开文件不存在 , 则不建立任何文件 , 返回打开失败信息 如果打开文件不存在,则不建立任何文件,返回打开失败信息 如果打开文件不存在,则不建立任何文件,返回打开失败信息
    i o s : : n o r e p l a c e {\rm ios::noreplace} ios::noreplace 如果打开文件已存在 , 不进行覆盖 , 当文件存在时 , 返回错误信息 如果打开文件已存在,不进行覆盖,当文件存在时,返回错误信息 如果打开文件已存在,不进行覆盖,当文件存在时,返回错误信息
    i o s : : b i n a r y {\rm ios::binary} ios::binary 以二进制方式打开文件 , 在不指定的情况下默认以文本文件方式打开文件 以二进制方式打开文件,在不指定的情况下默认以文本文件方式打开文件 以二进制方式打开文件,在不指定的情况下默认以文本文件方式打开文件

    使用符号"|"将常量组合,控制打开文件的多种属性,如下:

    c++ 复制代码
    ios::in|ios::nocreate;		// 文件打开用于数据输入,若打开文件不存在,则返回打开失败信息;
    ios::out|ios::noreplace;	// 文件打开用于数据输出,若打开文件存在,则返回打开失败信息;
    ios::in|ios::out;			// 文件打开既用于数据输入,又用于数据输出;
    ios::app|ios::nocreate;		// 文件打开后指针在文件尾,用于追加数据,若打开文件不存在,返回打开失败信息;
    ios::in|ios::out|ios::binary;	// 文件以二进制方式打开,既用于数据输入,又用于输出输出;
  • 检查文件是否打开成功,使用 f a i l ( ) {\rm fail()} fail()成员函数判定,原型如下:

    c++ 复制代码
    bool fail();	// 打开失败,返回true;打开成功,返回false;
  • 文件操作实例:打开一个文件并向其中写入文本数据,如果文件不存在,则新建一个文件,如果文件存在,则以追加方式写入数据 ( e x a m p l e 12 _ 3. c p p ) ({\rm example12\_3.cpp}) (example12_3.cpp)。

    c++ 复制代码
    /**
     * 作者:罗思维
     * 时间:2024/04/01
     * 描述:打开一个文件并向其中写入文本数据。
     */
    #include <iostream>
    #include <fstream>
    
    using namespace std;
    
    int main() {
    	ofstream file_out;
    	file_out.open("./data.txt", ios::out | ios::app);
    
    	if (file_out.fail()) {
    		cerr << "file data.txt open fail!" << endl;
    		return 1;
    	}
    
    	file_out.close();		// 关闭文件;
    
    	return 0;
    }
  • 向文本文件输出数据的两种方法:一是使用插入操作符"<<",二是调用成员函数 ( p u t ) ({\rm put}) (put);

  • 文本文件数据输出实例 ( e x a m p l e 12 _ 4. c p p ) ({\rm example12\_4.cpp}) (example12_4.cpp):

    c++ 复制代码
    /**
     * 作者:罗思维
     * 时间:2024/04/01
     * 描述:文本文件的输出。 
     */
    #include <iostream>
    #include <fstream>
    
    using namespace std;
    
    int main() {
    	ofstream file_out("./data2.txt");
    
    	// 利用插入操作符进行数据输出; 
    	file_out << "A B C D E F G" << endl;
    	file_out << "H I J K L M N" << endl;
    	file_out << "O P Q R S T" << endl;
    	file_out << "U V W X Y Z" << endl;
    
    	// 利用put()成员函数进行数据输出。 
    	for (int i = 65; i <= 90; i++) {
    		file_out.put(i);			// 输出单个字母; 
    		file_out.put(32);			// 输出一个空格; 
    		switch (i) {
    			case 71:
    				file_out.put(10);	// 输出换行; 
    				break;
    			case 78:
    				file_out.put(10);
    				break;
    			case 84:
    				file_out.put(10);
    				break;
    			case 90:
    				file_out.put(10);
    				break;
    			default:
    				break;
    		}
    	}
    
    	file_out.close();
    
    	return 0;
    }
  • 将文本文件中的数据读入内存的三种方法:一是利用提取操作符">>",二是调用成员函数 g e t ( ) {\rm get()} get(),三是调用成员函数 g e t l i n e ( ) {\rm getline()} getline();

    c++ 复制代码
    // 1.利用提取操作符">>",原型如下:
    istream& operator >>(C++标准类型 &);
    
    // 2.调用成员函数get();
    int get();					// 读取字符;
    istream& get(char& c);		// 读取一个字符并存储到c中;
    
    // 3.调用成员函数getline();
    istream& getline(char * buffer,int len,char="\n");
    
    // 参数说明:
    // getline()用于读入文本文件中的一块文本;
    // 参数1:读入数据转存到的字符串,即将读入的字符存储到buffer;
    // 参数2:读入字符块的长度;
    // 参数3:默认为换行,当遇到换行时,则停止本次读取;
  • 利用提取操作符读取文本数据 ( e x a m p l e 12 _ 5. c p p ) ({\rm example12\_5.cpp}) (example12_5.cpp):

    c++ 复制代码
    /**
     * 作者:罗思维
     * 时间:2024/04/01
     * 描述:利用提取操作符读取文本数据。 
     */
    #include <iostream>
    #include <fstream>
    
    using namespace std;
    
    int main() {
    	ifstream file_in("./data3.txt", ios::in);			// 打开文件; 
    	
    	if (file_in.fail()) {			// 判断文件打开是否成功; 
    		cerr << "File data3.txt open fail!" << endl;
    	}
    
    	char cRead;
    	while (file_in >> cRead) {		// 读取单个字符; 
    		cout << cRead << " ";		// 输出单个字符; 
    	}
    
    	file_in.close();				// 关闭文件; 
    
    	return 0;
    }
  • 利用 g e t ( ) {\rm get()} get()成员函数读取文本数据 ( e x a m p l e 12 _ 6. c p p ) ({\rm example12\_6.cpp}) (example12_6.cpp):

    c++ 复制代码
    /**
     * 作者:罗思维
     * 时间:2024/04/01
     * 描述:利用get()成员函数读取文本数据。 
     */
    #include <iostream>
    #include <fstream>
    
    using namespace std;
    
    int main() {
    	ifstream file_in("./data3.txt", ios::in);			// 打开文件; 
    	
    	if (file_in.fail()) {			// 判断文件是否成功打开; 
    		cerr << "File data3.txt open fail!" << endl;
    	}
    
    	char cRead;
    	while (!file_in.eof()) {		// 判断文件内容是否结束,读取单个字符并输出到屏幕上; 
    		cRead = file_in.get();
    		cout << cRead;
    	}
    
    	file_in.close();				// 关闭文件; 
    
    	return 0;
    }
  • 利用提取操作符读取数据时,忽略空格、换行符,利用 g e t ( ) {\rm get()} get()成员函数读取数据时,会将空格、换行符全部读出;

  • 利用 g e t ( c h a r & c ) {\rm get(char\&\ c)} get(char& c)成员函数读取文本数据 ( e x a m p l e 12 _ 7. c p p ) ({\rm example12\_7.cpp}) (example12_7.cpp):

    c++ 复制代码
    /**
     * 作者:罗思维
     * 时间:2024/04/01
     * 描述:利用get(char& c)成员函数读取文本数据。 
     */
    #include <iostream>
    #include <fstream>
    
    using namespace std;
    
    int main() {
    	ifstream file_in("./data3.txt", ios::in);			// 打开文件; 
    	
    	if (file_in.fail()) {								// 判断文件打开是否成功; 
    		cerr << "File data3.txt open fail!" << endl;
    	}
    
    	char c;
    	while (!file_in.eof()) {		// 当没有读到文件尾部时,读取单个字符并输出到屏幕上; 
    		file_in.get(c);
    		cout << c;
    	}
    
    	file_in.close();				// 关闭文件; 
    
    	return 0;
    }
  • 利用 g e t l i n e ( ) {\rm getline()} getline()成员函数读取文本数据 ( e x a m p l e 12 _ 8. c p p ) ({\rm example12\_8.cpp}) (example12_8.cpp):

    c++ 复制代码
    /**
     * 作者:罗思维
     * 时间:2024/04/01
     * 描述:利用getline()成员函数读取文本数据。
     */
    #include <iostream>
    #include <fstream>
    #include <string.h>
    
    using namespace std;
    
    int main() {
    	ifstream file_in("./data3.txt", ios::in);		// 打开文件;
    
    	if (file_in.fail()) {			// 判断文件打开是否成功;
    		cerr << "File data3.txt open fail!" << endl;
    	}
    
    	char* buf = new char[128];
    	memset(buf, 0, 128);			// 清空buf内容;
    
    	while (!file_in.eof()) {
    		file_in.getline(buf, 128);	// 逐行读取数据到buf中;
    		cout << buf << endl;
    		memset(buf, 0x00, 128);		// 清空buf内容;
    	}
    
    	delete[] buf;					// 释放动态内存;
    	file_in.close();				// 关闭文件;
    
    	return 0;
    }
  • 向二进制文件写入数据是利用流类 o s t r e a m {\rm ostream} ostream的 w r i t e ( ) {\rm write()} write()函数实现,原型如下:

    c++ 复制代码
    ostream& write(const char * buffer,int len);
    
    // 参数说明:
    // 参数1:buffer用于存储需要写入文件的内容;
    // 参数2:len是设定写入数据的长度;
  • 向二进制文件写入数据实例 ( e x a m p l e 12 _ 9. c p p ) ({\rm example12\_9.cpp}) (example12_9.cpp):

    c++ 复制代码
    /**
     * 作者:罗思维
     * 时间:2024/04/02
     * 描述:向二进制写入数据。
     * 数据:1 2 3 4 5 6 7 8 9
     *  	A B C D E F G
     */
    #include <iostream>
    #include <fstream>
    
    using namespace std;
    
    int main() {
    	// 打开二进制文件; 
    	ofstream file_out("./data.dat", ios::out | ios::binary | ios::trunc);
    
    	// 如果文件打开失败,则显示错误信息; 
    	if (!file_out) {
    		cerr << "File data.dat open fail!" << endl;
    		exit(1);
    	}
    
    	// 写入1,2,3,4,5,6,7,8,9; 
    	for (int i = 49; i <= 57; i++) {
    		file_out.write((char*)&i, sizeof(int));
    	}
    
    	// 写入A,B,C,D,E,F,G 
    	for (int i = 65; i <= 70; i++) {
    		file_out.write((char*)&i, sizeof(int));
    	}
    
    	file_out.close();
    
    	return 0;
    }
  • 将二进制文件作为输入文件,则读取其数据时使用流类 i s t r e a m {\rm istream} istream的成员函数 r e a d ( ) {\rm read()} read(),函数原型为:

    c++ 复制代码
    istream& read(char * buffer,int len);
    
    // 参数说明:
    // 参数1:buffer用于存储需要读取的内容;
    // 参数2:len是设定读取数据的长度;
  • 读取二进制文件数据实例,每次只读一个数值 ( e x a m p l e 12 _ 10. c p p ) ({\rm example12\_10.cpp}) (example12_10.cpp):

    c++ 复制代码
    /**
     * 作者:罗思维
     * 时间:2024/04/02
     * 描述:从二进制读入数据。
     * 数据:1 2 3 4 5 6 7 8 9
     *  	 A B C D E F G
     */
    #include <iostream>
    #include <fstream>
    
    using namespace std;
    
    int main() {
    	ifstream file_in("./data.dat", ios::in | ios::binary);
    
    	if (!file_in) {
    		cerr << "File data.dat open fail!" << endl;
    		exit(1);
    	}
    
    	int n;
    	while (!file_in.eof()) {
    		file_in.read((char*)&n, sizeof(n));
    		cout << (char)n << " ";
    	}
    
    	file_in.close();
    
    	return 0;
    }
  • 读取二进制文件数据实例,一次性从文件中读出多个字节 ( e x a m p l e 12 _ 11. c p p ) ({\rm example12\_11.cpp}) (example12_11.cpp):

    c++ 复制代码
    /**
     * 作者:罗思维
     * 时间:2024/04/02
     * 描述:从二进制读入数据。
     * 数据:1 2 3 4 5 6 7 8 9
     *  	 A B C D E F
     */
    #include <iostream>
    #include <fstream>
    #include <string.h>
    
    using namespace std;
    
    // 定义函数,输出字符串; 
    void print(char* &str, int len) {
    	for (int i = 0; i < len; i++) {
    		cout << str[i];
    	}
    }
    
    int main() {
    	ifstream file_in("./data.dat", ios::in | ios::binary);
    
    	if (!file_in) {
    		cerr << "File data.dat open fail!" << endl;
    		exit(1);
    	}
    
    	char* buf = new char[11];		// 开辟空间; 
    	memset(buf, 0x00, 11);			// 清空空间内容; 
    
    	while (!file_in.eof()) {
    		file_in.read(buf, 10);		// 一次性读取10个字节; 
    		print(buf, 10);
    		memset(buf, 0x00, 10);		// 清空空间内容; 
    	}
    
    	delete[] buf;					// 释放内存; 
    	file_in.close();				// 关闭文件; 
    
    	return 0;
    }
  • 与文件相关联的指针:读取指针和写入指针。

    • 读取指针:指向文件当前要读取的数据的位置;
    • 写入指针:指向现在正要写入数据的位置;
  • 控制文件指针的两个函数:

    • 流类 i s t r e a m {\rm istream} istream的成员函数 s e e k g ( ) {\rm seekg()} seekg()用于把读文件指针移动到指定位置,函数原型:

      c++ 复制代码
      istream& seekg(long dis,seek_dir ref=ios::beg);
    • 流类 o s t r e a m {\rm ostream} ostream的成员函数 s e e k p ( ) {\rm seekp()} seekp()用于把写文件指针移动到指定位置,函数原型:

      c++ 复制代码
      ostream& seekp(long dis,seek_dir ref=ios::beg);
    • 参数 d i s {\rm dis} dis:文件指针需要移动的字节数,当其为正数时,表示向后移,向文件末尾,当其为负数时,表示向前移,向文件开头;

    • 参数 s e e k _ d i r {\rm seek\_dir} seek_dir: i o s {\rm ios} ios根基类中定义的枚举类型;

      c++ 复制代码
      enum seek_dir {beg = 0,cur = 1,end =2};
      
      // ios::beg:文件的开始位置;
      // ios::cur:当前位置;
      // ios::end:结束位置;
  • 通过文件指针移动把二进制文件数据显示到屏幕上 ( e x a m p l e 12 _ 12. c p p ) ({\rm example12\_12.cpp}) (example12_12.cpp):

    c++ 复制代码
    /**
     * 作者:罗思维
     * 时间:2024/04/02
     * 描述:从二进制读入数据。
     * 数据:1 2 3 4 5 6 7 8 9
     *  	 A B C D E F
     */
    #include <iostream>
    #include <fstream>
    
    using namespace std;
    
    int main() {
    	// 打开二进制文件; 
    	ifstream file_in("./data.dat", ios::in | ios::binary);
    
    	// 判断文件是否打开成功; 
    	if (!file_in) {
    		cerr << "File data.dat open fail!" << endl;
    		exit(1);
    	}
    
    	int n;
    	
    	// 移动指针,9x4个字节; 
    	file_in.seekg(9 * 4, ios::beg);
    
    	while (!file_in.eof()) {
    		file_in.read((char*)&n, sizeof(n));		// 读取二进制文件数据; 
    		cout << (char)n << " ";
    	}
    
    	file_in.close();							// 关闭文件; 
    
    	return 0;
    }
12.5 实战

项目需求 :编写一个学生信息管理系统,要求将学生数据保存在文件中,提供查询、修改、删除学生信息的功能。

代码实现 ( S t u M a n a g e I n f o S y s t e m ) {\rm (StuManageInfoSystem)} (StuManageInfoSystem):

  • S t u d e n t . h {\rm Student.h} Student.h

    c++ 复制代码
    /**
     * 作者:罗思维
     * 时间:2024/04/03
     * 描述:定义存储学生信息结构体。 
     */
    #pragma once
    #include <iostream>
    #include <iomanip>
    #include <fstream>
    #include <vector>
    
    using namespace std;
    
    #define NULL 0
    int const MAX_NUM = 20;
    #define LEN sizeof(struct student)
    
    // 定义一个学生考试信息的结构体;
    /* 	参数说明:
     * 	name:	姓名;
     *  sex:	性别;
     *  id:	准考证号;
     *  score:	分数;
     *  total:	总分数;
    */
    struct student {
    	char name[MAX_NUM];
    	char sex[MAX_NUM];
    	long int id;
    	int score[4];
    	int total;
    	struct student *next;
    };
  • I n f o r m a t i o n . h {\rm Information.h} Information.h

    c++ 复制代码
    /**
     * 作者:罗思维
     * 时间:2024/04/03
     * 描述:学生信息类定义头文件。 
     */
    #pragma once
    #include "Student.h"
    
    static int n = 0;
    class CInformation {
    	private:
    		student *p1, *p2, *p3, *head, st;
    	public:
    		CInformation();
    		virtual ~CInformation();
    		student* create();					// 建立链表函数; 
    		void output(student* head);			// 输出链表; 
    		int count(student* head);			// 函数count统计考生总数; 
    		student* insert(student* head);		// 指针函数insert添加考生信息; 
    		student* cancel(student* head, long int num);	// 指针函数cancel删除考生信息; 
    		student* find(student* head, long int num);		// 指针函数find查找考生信息; 
    		void sort(student* head);			// 考生总分从大到小排列输出; 
    		void average(student* head);		// 考生成绩平均分; 
    		void save(student* head);			// 保存信息函数; 
    		student* Read();					// 读取信息函数; 
    };
  • I n f o r m a t i o n . c p p {\rm Information.cpp} Information.cpp

    c++ 复制代码
    /**
     * 作者:罗思维
     * 时间:2024/04/03
     * 描述:学生信息各种接口函数实现。 
     */
    #include "Information.h"
    #include "Student.h"
    #include <string.h>
    
    CInformation::CInformation() {
    }
    
    CInformation::~CInformation() {
    }
    
    // 新建学生信息成员函数;
    student *CInformation::create(void) {
    	char ch[MAX_NUM];
    	n = 0;
    	p1 = p2 = (student *) malloc(LEN);
    	cout << "------<<请建立学生考试信息表>>------" << endl;
    	cout << "姓名:";
    	cin >> ch;
    	head = NULL;
    	while (strcmp(ch, "!") != 0) {
    		p1 = (student *)malloc(LEN);
    		strcpy(p1->name, ch);
    		cout << "性别:";
    		cin >> p1->sex;
    		cout << "准考证号(8位):";
    		cin >> p1->id;
    		cout << "数学成绩:";
    		cin >> p1->score[0];
    		cout << "物理成绩:";
    		cin >> p1->score[1];
    		cout << "英语成绩:";
    		cin >> p1->score[2];
    		cout << "C语言成绩:";
    		cin >> p1->score[3];
    		p1->total = p1->score[0] + p1->score[1] + p1->score[2] + p1->score[3];
    		if (n == 0) {
    			head = p1;
    		} else {
    			p2->next = p1;
    		}
    		p2 = p1;
    		n++;
    		cout << "姓名:";
    		cin >> ch;
    	}
    	p2->next = NULL;
    	return (head);
    }
    
    // 输出学生信息成员函数;
    void CInformation::output(student *head) {
    	if (head == NULL) {
    		cout << "这是一个空表,请先输入考生成绩.\n";
    	} else {
    		cout << "-------------------------\n";
    		cout << " *学生考试成绩信息表*\n";
    		cout << "-------------------------\n";
    		cout << "准考证号 姓名 性别 数学 物理 英语 C语言 平均分 总分\n";
    		p1 = head;
    		do {
    			cout << setw(8) << p1->id
    			     << setw(9) << p1->name
    			     << setw(8) << p1->sex
    			     << setw(8) << p1->score[0]
    			     << setw(9) << p1->score[1]
    			     << setw(9) << p1->score[2]
    			     << setw(9) << p1->score[3]
    			     << setw(9) << p1->total / 4.0
    			     << setw(9) << p1->total << endl;
    			cout << "-------------------------\n";
    			p1 = p1->next;
    		} while (p1 != NULL);
    	}
    }
    
    // 获得存储的学生数成员函数;
    int CInformation::count(struct student *head) {
    	if (head == NULL) {
    		return (0);
    	} else {
    		return (1 + count(head->next));
    	}
    }
    
    // 插入存储的学生信息成员函数;
    student  *CInformation::insert(student *head) {
    	cout << "\t---------------<<请输入新增学生成绩信息>>---------------\n" << endl;
    	p1 = (student *)malloc(LEN);
    	cout << "准考证号(8位):";
    	cin >> p1->id;
    	cout << "姓名:";
    	cin >> p1->name;
    	cout << "性别:";
    	cin >> p1->sex;
    	cout << "数学成绩:";
    	cin >> p1->score[0];
    	cout << "物理成绩:";
    	cin >> p1->score[1];
    	cout << "英语成绩:";
    	cin >> p1->score[2];
    	cout << "C语言成绩:";
    	cin >> p1->score[3];
    	p1->total = p1->score[0] + p1->score[1] + p1->score[2] + p1->score[3];
    	p2 = head;
    	if (head == NULL) {
    		head = p1;
    		p1->next = NULL;
    	} else {
    		while ((p1->id > p2->id) && (p2->next != NULL)) {
    			p3 = p2;
    			p2 = p2->next;
    		}
    		if (p1->id <= p2->id) {
    			if (head == p2) {
    				p1->next = head;
    				head = p1;
    			} else {
    				p3->next = p1;
    				p1->next = p2;
    			}
    		} else {
    			p2->next = p1;
    			p1->next = NULL;
    		}
    	}
    	n++;
    	cout << "\t---------------<<你输入的学生信息已经成功插入>>---------------\n" << endl;
    	return head;
    }
    
    // 删除某学生信息成员函数;
    student *CInformation::cancel(student *head, long int num) {
    	if (head == NULL) {
    		return (head);
    	} else {
    		p1 = head;
    		while (num != p1->id && p1->next != NULL) {
    			p2 = p1;
    			p1 = p1->next;
    		}
    		if (num == p1->id) {
    			if (p1 == head) {
    				head = p1->next;
    			} else {
    				p2->next = p1->next;
    			}
    			cout << "删除准考证号为" << num << "的学生\n";
    			n--;
    		}
    		return (head);
    	}
    }
    
    // 查找某学生信息成员函数;
    student *CInformation::find(student *head, long int num) {
    	if (head == NULL) {
    		cout << "这是一个空表,请先输入考生成绩.\n";
    		return (head);
    	} else {
    		p1 = head;
    		while (num != p1->id && p1->next != NULL) {
    			p1 = p1->next;
    		}
    		if (num == p1->id) {
    			cout << setw(8) << p1->id
    			     << setw(9) << p1->name
    			     << setw(8) << p1->sex
    			     << setw(8) << p1->score[0]
    			     << setw(9) << p1->score[1]
    			     << setw(9) << p1->score[2]
    			     << setw(9) << p1->score[3]
    			     << setw(9) << p1->total / 4.0
    			     << setw(9) << p1->total << endl;
    			cout << "-------------------------\n";
    		} else {
    			cout << "没找到准考证号为" << num << "的学生.\n";
    		}
    		return (head);
    	}
    }
    
    // 信息排序成员函数;
    void CInformation::sort(student *head) {
    	int i, k, m = 0, j;
    	student *p[MAX_NUM];
    	if (head != NULL) {
    		m = count(head);
    		cout << "-------------------------\n";
    		cout << "*学生考试成绩统计表*\n";
    		cout << "-------------------------\n";
    		cout << "准考证号 姓名 性别 数学 物理 英语 C语言 平均分 总分 名次\n";
    		cout << "-------------------------\n";
    		p1 = head;
    		for (k = 0; k < m; k++) {
    			p[k] = p1;
    			p1 = p1->next;
    		}
    		for (k = 0; k < m - 1; k++) {
    			for (j = k + 1; j < m; j++) {
    				if (p[k]->total < p[j]->total) {
    					p2 = p[k];
    					p[k] = p[j];
    					p[j] = p2;
    				}
    			}
    		}
    		for (i = 0; i < m; i++) {
    			cout << setw(8) << p[i]->id
    			     << setw(9) << p[i]->name
    			     << setw(6) << p[i]->sex
    			     << setw(7) << p[i]->score[0]
    			     << setw(7) << p[i]->score[1]
    			     << setw(7) << p[i]->score[2]
    			     << setw(7) << p[i]->score[3]
    			     << setw(8) << p[i]->total / 4.0
    			     << setw(7) << p[i]->total
    			     << setw(9) << i + 1 << endl;
    			cout << "-------------------------\n";
    		}
    	}
    }
    
    // 计算学生各科平均成绩成员函数;
    void CInformation::average(student *head) {
    	int k, m;
    	float arg1 = 0, arg2 = 0, arg3 = 0, arg4 = 0;
    
    	if (head == NULL) {
    		cout << "这是一个空表,请先输入考生成绩.\n";
    	} else {
    		m = count(head);
    		p1 = head;
    		for (k = 0; k < m; k++) {
    			arg1 += p1->score[0];
    			arg2 += p1->score[1];
    			arg3 += p1->score[2];
    			arg4 += p1->score[3];
    			p1 = p1->next;
    		}
    		arg1 /= m;
    		arg2 /= m;
    		arg3 /= m;
    		arg4 /= m;
    		cout << "*全班单科成绩平均分*\n";
    		cout << "-------------------------\n";
    		cout << "数学平均分:" << setw(7) << arg1
    		     << "物理平均分:" << setw(7) << arg2
    		     << "英语平均分:" << setw(7) << arg3
    		     << "C语言平均分:" << setw(7) << arg4 << endl;
    		cout << "-------------------------\n";
    	}
    }
    
    // 信息保存成员函数;
    void CInformation::save(student *head) {
    	ofstream out("data.txt", ios::out);
    	out << count(head) << endl;
    	while (head != NULL) {
    		out << head->name << "\t"
    		    << head->id << "\t" << "\t"
    		    << head->sex << "\t"
    		    << head->score[0] << "\t"
    		    << head->score[1] << "\t"
    		    << head->score[2] << "\t"
    		    << head->score[3] << "\t"
    		    << head->total << endl;
    		head = head->next;
    	}
    }
    
    // 学生信息读取成员函数;
    student *CInformation::Read() {
    	int i = 0;
    	p1 = p2 = (student *) malloc(LEN);
    	head = NULL;
    	ifstream in("data.txt", ios::out);
    	in >> i;
    	if (i == 0) {
    		cout << "data.txt文件中的数据为空,请先输入数据." << endl;
    		return 0;
    	} else {
    		cout << "-------------------------\n";
    		for (; i > 0; i--) {
    			p1 = (student *)malloc(LEN);
    			in >> st.name >> st.id >> st.sex
    			   >> st.score[0] >> st.score[1]
    			   >> st.score[2] >> st.score[3]
    			   >> st.total;
    			strcpy(p1->name, st.name);
    			p1->id = st.id;
    			strcpy(p1->sex, st.sex);
    			p1->score[0] = st.score[0];
    			p1->score[1] = st.score[1];
    			p1->score[2] = st.score[2];
    			p1->score[3] = st.score[3];
    			p1->total = st.total;
    			if (n == 0) {
    				head = p1;
    			} else {
    				p2->next = p1;
    			}
    			p2 = p1;
    			n++;
    			cout << " " << p1->name << "\t"
    			     << p1->id << "\t" << "\t"
    			     << p1->sex << "\t"
    			     << p1->score[0] << "\t"
    			     << p1->score[1] << "\t"
    			     << p1->score[2] << "\t"
    			     << p1->score[3] << "\t"
    			     << p1->total << endl;
    			cout << "-------------------------\n";
    		}
    		cout << "数据已经成功读取完毕." << endl;
    		p2->next = NULL;
    		return (head);
    	}
    }
  • m a i n . c p p {\rm main.cpp} main.cpp

    c++ 复制代码
    /**
     * 作者:罗思维
     * 时间:2024/04/03
     * 描述:程序主文件。 
     */
    #include "Student.h"
    #include "Information.h"
    
    vector<student> stu;
    
    int main() {
    	CInformation person;
    	student *head = NULL;
    	int choice;
    	long int i;
    
    	do {
    		cout << " 1.输入学生成绩" << endl;
    		cout << " 2.显示学生成绩" << endl;
    		cout << " 3.排序统计成绩" << endl;
    		cout << " 4.查找学生成绩" << endl;
    		cout << " 5.增加学生成绩" << endl;
    		cout << " 6.删除学生成绩" << endl;
    		cout << " 7.安全退出系统" << endl;
    		cout << " 请输入您的选择(0-7):";
    		cin >> choice;
    		switch (choice) {
    			case 0:
    				head = person.Read();
    				break;
    			case 1:
    				head = person.create();
    				break;
    			case 2:
    				person.output(head);
    				break;
    			case 3:
    				person.sort(head);
    				person.average(head);
    				cout << "参加考试的学生人数为:" << person.count(head) << "人\n";
    				break;
    			case 4:
    				cout << "请输入要查找的准考证号(8位):";
    				cin >> i;
    				person.find(head, i);
    				break;
    			case 5:
    				head = person.insert(head);
    				person.output(head);
    				break;
    			case 6:
    				cout << "请输入要删除的准考证号(8位):";
    				cin >> i;
    				head = person.cancel(head, i);
    				person.output(head);
    				break;
    			case 7:
    				person.save(head);
    				break;
    			default:
    				cout << "输入有误,请重新输入.\n";
    				break;
    		}
    	} while (choice != 7);
    }
相关推荐
blammmp6 分钟前
Java:数据结构-枚举
java·开发语言·数据结构
何曾参静谧19 分钟前
「C/C++」C/C++ 指针篇 之 指针运算
c语言·开发语言·c++
暗黑起源喵25 分钟前
设计模式-工厂设计模式
java·开发语言·设计模式
WaaTong30 分钟前
Java反射
java·开发语言·反射
Troc_wangpeng31 分钟前
R language 关于二维平面直角坐标系的制作
开发语言·机器学习
努力的家伙是不讨厌的33 分钟前
解析json导出csv或者直接入库
开发语言·python·json
Envyᥫᩣ1 小时前
C#语言:从入门到精通
开发语言·c#
童先生1 小时前
Go 项目中实现类似 Java Shiro 的权限控制中间件?
开发语言·go
lulu_gh_yu1 小时前
数据结构之排序补充
c语言·开发语言·数据结构·c++·学习·算法·排序算法
Re.不晚2 小时前
Java入门15——抽象类
java·开发语言·学习·算法·intellij-idea