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

news2024/11/20 6:12:52

此专栏为移动机器人知识体系下的编程语言中的 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操作流类之间的关系图:

    17

  • 几个常用的流类:

    • 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} iosistreamostreamiostream等,还定义了 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)

    /**
     * 作者:罗思维
     * 时间: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} stdoutstderr
  • 流控制成员函数实例:用流控制成员函数控制数据输出格式 ( e x a m p l e 12 _ 2. c p p ) ({\rm example12\_2.cpp}) (example12_2.cpp)

    /**
     * 作者:罗思维
     * 时间: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 重载流运算符
  • 运算符重载的两种形式:重载为成员函数或重载为友元函数,重载插入运算符和提取运算符时,左边的参数是流,右边的参数是类的对象,因此,只能用友元函数的方式去重载插入运算符和提取运算符;

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

    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)

      /**
       * 作者:罗思维
       * 时间: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)

      /**
       * 作者:罗思维
       * 时间: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)

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

    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)

      /**
       * 作者:罗思维
       * 时间: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)

      /**
       * 作者:罗思维
       * 时间: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)

      /**
       * 作者:罗思维
       * 时间: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码作为内容来存储,每一个字符对应文件中的一个字节,这些字节按照顺序被存储在磁盘上;
    • 二进制文件:将其内容按照二进制编码的方式来存放的文件;
  • 文本文件和二进制文件存储举例:

    18

  • 在对文本文件进行 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} ifstreamofstreamfstream

    • 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:输入/输出文件流对象;
  • 打开文件前,先根据需要进行的操作声明流对象:

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

    // 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 以二进制方式打开文件 , 在不指定的情况下默认以文本文件方式打开文件 以二进制方式打开文件,在不指定的情况下默认以文本文件方式打开文件 以二进制方式打开文件,在不指定的情况下默认以文本文件方式打开文件

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

    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()成员函数判定,原型如下:

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

    /**
     * 作者:罗思维
     * 时间: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)

    /**
     * 作者:罗思维
     * 时间: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()

    // 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)

    /**
     * 作者:罗思维
     * 时间: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)

    /**
     * 作者:罗思维
     * 时间: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)

    /**
     * 作者:罗思维
     * 时间: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)

    /**
     * 作者:罗思维
     * 时间: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()函数实现,原型如下:

    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)

    /**
     * 作者:罗思维
     * 时间: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(),函数原型为:

    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)

    /**
     * 作者:罗思维
     * 时间: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)

    /**
     * 作者:罗思维
     * 时间: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()用于把读文件指针移动到指定位置,函数原型:

      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()用于把写文件指针移动到指定位置,函数原型:

      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根基类中定义的枚举类型;

      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)

    /**
     * 作者:罗思维
     * 时间: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

    /**
     * 作者:罗思维
     * 时间: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

    /**
     * 作者:罗思维
     * 时间: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

    /**
     * 作者:罗思维
     * 时间: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

    /**
     * 作者:罗思维
     * 时间: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);
    }
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1566897.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

GitLab 新项目创建和使用

一、下载 Git 客户端 Git - Downloading Package (git-scm.com) 二、打开 Git Bash 配置 gitlab 账户 下面的信息可以登录gitlab查看 git config --global user.name "yourname"git config --global user.email "youremailXX.com" 生成ssh_key ssh-k…

Pycharm+Neo4j红楼梦人物关系图谱

欢迎来到我的主页~【蜡笔小新..】 本篇收录于专栏【Python】 如果对你有帮助&#xff0c;希望点赞收藏加关注啦~ 目录 前言 neo4j基础知识 Pycharm及代码实现 py2neo 数据集获取 代码介绍 前言 Python实验课时&#xff0c;老师提到用知识图谱构建红楼梦的人物关系图&…

陪玩社交系统开发(现成,可定制)-线下线上陪玩达人入驻服务系统软件开发(APP小程序公众号搭建)

陪源码的公司优势 1、长时间的陪玩源码开发经验&#xff0c;始终坚持从客户的实际需求出发 2、提供安全的陪玩系统源码开发解决方案&#xff0c;众多开发案例可供参考 3、需求定制不走弯路&#xff0c;即可使用约单APP开发成品直接使用&#xff0c;也支持二次开发 4、追求精…

基于单片机便携式测振仪的研制系统设计

**单片机设计介绍&#xff0c;基于单片机便携式测振仪的研制系统设计 文章目录 一 概要二、功能设计三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机便携式测振仪的研制系统设计概要主要涉及利用单片机作为核心控制器件&#xff0c;结合测振原理和技术&#x…

如何保护IP地址?安全匿名上网的方法

当互联网成为每个家庭的重要组成部分后&#xff0c;IP地址就成了你的虚拟地址。您的请求从该地址开始&#xff0c;然后 Internet 将消息发送回该地址。那么&#xff0c;您担心您的地址被泄露吗&#xff1f; 对于安全意识高或者某些业务需求的用户&#xff0c;如果您正在寻找保护…

如何在本地使用Ollama运行开源LLMs

本文将指导您下载并使用Ollama&#xff0c;在您的本地设备上与开源大型语言模型&#xff08;LLMs&#xff09;进行交互的强大工具。 与像ChatGPT这样的闭源模型不同&#xff0c;Ollama提供透明度和定制性&#xff0c;使其成为开发人员和爱好者的宝贵资源。 我们将探索如何下载…

Django路由分发的三种方式以及命名空间namespce——附带源码解析

目录 1. 前言 2. include常规路由分发 3. include源码解析 4. 路由分发的第二种写法 5. 路由分发的第三种写法 6. 小结 7. 有关namespace 8. 最后 1. 前言 本篇文章主要是讲解路由分发的三种方式。当然&#xff0c;你可能在想&#xff0c;一般做路由分发只需要一个incl…

Kimi精选提示词,总结PPT内容

大家好&#xff0c;我是子云&#xff0c;最近真是觉得Kimi这个大模型&#xff0c;产品体验很棒&#xff0c;能力也是不错&#xff0c;感觉产品经理用心了。 发现一个Kimi 一个小技巧&#xff0c;可以学习到很多高级提示词。 Kimi输入框可以配置常用提示词&#xff0c;同时也可…

内存和网卡压力测试

1.内存压力测试 1.1测试目的 内存压力测试的目的是评估开发板中的内存子系统性能和稳定性&#xff0c;以确保它能够满足特定的应用需求。开发板通常用于嵌入式系统、物联网设备、嵌入式智能家居等场景&#xff0c;这些场景对内存的要求通常比较高。 其内存压力测试的主要目的…

C++设计模式:TemplateMethod模式(一)

1、概念定义 定义一个操作中的算法的骨架结构&#xff08;稳定&#xff09;&#xff0c;而将一些步骤延迟&#xff08;变化&#xff09;到子类中。Template Method使得子类可以不改变&#xff08;复用&#xff09;一个算法的骨架结构即可重定义&#xff08;override重写&#x…

AMRT3D数字孪生引擎

产品概述 AMRT3D引擎是由眸瑞网络科技自主研发、拥有完全自主知识产权的一款全球首款轻量化3D图形引擎&#xff0c;引擎以核心的轻量化技术及AMRT轻量格式为支柱&#xff0c;专为数字孪生项目开发打造。 AMRT3D引擎提供一整套完善的数字孪生解决方案&#xff0c;在数据处理方…

基于ssm的轻型卡车零部件销售平台(java项目+文档+源码)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于ssm的轻型卡车零部件销售平台。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 轻型卡车零部件销售平台…

easyExcel 模版导出 中间数据纵向延伸,并且对指定列进行合并

想要达到的效果 引入maven引用 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.2.1</version></dependency> 按照要求创建模版 备注 : 模板注意 用{} 来表示你要用的变量 如果本…

Delphi 是一种内存安全的语言吗?

上个月&#xff0c;美国政府发布了 "回到基石 "报告&#xff1a; 通往安全和可衡量软件之路 "的报告。该报告是美国网络安全战略的一部分&#xff0c;重点关注多个领域&#xff0c;包括内存安全漏洞和质量指标。 许多在线杂志都对这份报告进行了评论&#xff0…

RT-Thread下使用NTP服务器获取时间并同步到硬件RTC

单片机:STM32F407VET6 实现功能:通过ntp服务器获取时间并同步到硬件RTC上 1.配置NTP相关参数 1.1打开netutils相关软件包 1.2 关闭软件RTC相关配置 参考资料:RT-Thread中使用NTP自动更新时间_rtthread ntp-CSDN博客 2.配置硬件RTC 2.1 在ENV里面使能硬件RTC 2.2使用STM32C…

医疗器械5G智能制造工厂数字孪生可视化平台,推进行业数字化转型

医疗设备5G智能制造工厂数字孪生可视化平台&#xff0c;推进行业数字化转型。在数字化浪潮的推动下&#xff0c;医疗设备行业正迎来一场深刻的变革。5G技术的崛起&#xff0c;智能制造工厂的兴起&#xff0c;以及数字孪生可视化平台的出现&#xff0c;正在共同推动医疗设备行业…

【数据分析实战】印尼雅加达咖啡市场分析:品牌排名与市场趋势解读

目录 背景介绍数据展示数据分析可视化1. 各市咖啡店占比&#xff1a;1.1 可视化代码1.2 可视化结果1.3 浅薄解读 2. 品牌市场份额排名&#xff1a;2.1 可视化结果1.2 浅薄解读 3. 品牌消费者满意指数&#xff1a;3.1 可视化代码3.2 可视化结果3.3 浅薄解读 写在最后 背景介绍 …

数据结构——二叉树(堆)

大家好我是小峰&#xff0c;今天我们开始学习二叉树。 首先我们来学习什么是树&#xff1f; 树概念及结构 树是一种 非线性 的数据结构&#xff0c;它是由 n &#xff08; n>0 &#xff09;个有限结点组成一个具有层次关系的集合。 把它叫做树是因 为它看起来像一棵倒挂的…

canal部署

定义 canal组件是一个基于mysql数据库增量日志解析&#xff0c;提供增量数据订阅和消费&#xff0c;支持将增量数据投递到下游消费者&#xff08;kafka&#xff0c;rocketmq等&#xff09;或者存储&#xff08;elasticearch,hbase等&#xff09;canal感知到mysql数据变动&…

AI学习-线性回归推导

线性回归 1.简单线性回归2.多元线性回归3.相关概念熟悉4.损失函数推导5.MSE损失函数 1.简单线性回归 ​ 线性回归&#xff1a;有监督机器学习下一种算法思想。用于预测一个或多个连续型目标变量y与数值型自变量x之间的关系,自变量x可以是连续、离散&#xff0c;但是目标变量y必…