分享

看了大量外界禁传的资源,才发现输入输出是这样的意思

 zeroxer2008 2022-01-13

大家好,我是行码棋。

我翻看了大量的内部资料,终于对输入输出有了一个更深的了解。

今天我们来说一下C++的输入输出流,输出输出是任何语言都会出现的问题,大概的思路基本都一样,接下来是正文。

1.输入输出流简介

I/O的形式:

  • 以标准I/O设备为对象(标准I/O)
    • 「从键盘、鼠标得到的数据是进入内存的,该数据是输入数据。」
    • 「将数据从内存送到显示器、打印机等,该数据是输出数据。」
  • 以外存磁盘文件为对象(文件I/O)
    • 「内存中的数据存储到磁盘文件中,叫输出文件;」
    • 「从磁盘文件中的数据装载入内存中,叫输入文件。」
  • 以内存中指定的空间作为对象(串I/O)
    • 「常指定一个字符数组作为存储空间,这种I/O称为字符串的输入输出。」

I/O流模板层次图:

图片

各种输入输出流库:

图片

输入输出流的示意图:

图片图片

「预先定义的输出和输入流对象:」

  • cout 标准输出
  • cerr 标准错误输出,没有缓冲,发送给它的内容立即被输出。
  • clog 类似于cerr,但是有缓冲,缓冲区满时被输出。
  • cin标准输入

2.标准输出流

即流向标准输出设备(显示器)的数据

  • 「clog流对象」——在控制台(显示器)输出错误信息.

    用流插入运算符**“<<”**向clog输出数据.

    如:  「clog << “除数为0, 出错!” << endl;」

clog的用法与cerr类似.

  • clog流对象与cerr流对象的不同之处 cerr是不经过缓冲区,直接向显示器输出有关信息。
  • clog的输出先存储在缓冲区,遇到缓冲区满或遇到endl时才向显示器输出。

「使用流对象的成员进行格式输出:」

图片

cout.width(n)   //与格式控制符setw(n)相似
cout.fill(c)    // 与格式控制符setfill(c)相似
cout.setf(ios::dec); 
cout.unsetf(ios::hex);
点击并拖拽以移动
#include <iostream>
#include <iomanip> //不要忘记包含此头文件
using namespace std;
int main()
 
int a;
   cout<<'input a:';
   cin>>a;
   cout<<'dec:'<<dec<<a<<endl//以上进制形式输出整数
   cout<<'hex:'<<hex<<a<<endl//以十六进制形式输出整数a
   cout<<'oct:'<<setbase(8)<<a<<endl;//以八进制形式输出整数a
   char *pt='xingmaqi';         //pt指向字符串”China”
   cout<<setw(10)<<pt<<endl//指定域宽为10,输出字符串
   cout<<setfill('*')<<setw(10)<<pt<<endl;//指定域宽10,输出字符串,空白处以“*”填充
   double pi=22.0/7.0//计算pi值
   cout<<setiosflags(ios::scientific)<<setprecision(8);//按指数形式输出,8位小数
   cout<<'pi='<<pi<<endl//输出pi值
   cout<<'pi='<<setprecision(4)<<pi<<endl;//改为4位小数
   cout<<'pi='<<setiosflags(ios::fixed)<<pi<<endl;//改为小数形式输出
   return 0
}
点击并拖拽以移动

结果:

input a:1
dec:1
hex:1
oct:1
  xingmaqi
**xingmaqi
pi=3.14285714e+000
pi=3.1429e+000
pi=3.143
点击并拖拽以移动

3.标准输入流

标准输入流——从标准输入设备(键盘)流向程序的数据.

cin流对象

int  a,b; 
cin >> a >> b;   // 从键盘输入2个整数 
点击并拖拽以移动

当输入时 遇到无效的字符 或遇到文件结束符 ctrl+z 时 cin处于出错状态,无法正常提取数据(cin=false).

 if (!cin
     cout<<“输入出错”;
点击并拖拽以移动

4.输入流和输出流常用的成员函数

istream公有成员函数:

函数「功能」
read「无格式输入指定字节数」
get「从流中提取字符,包括空格」
getline「从流中提取一行字符」
ignore「提取并丢弃流中指定字符」
peek「返回流中下一个字符,但不从流中删除」
gcount「统计最后输入的字符个数」
eatwhite「忽略前导空格」
seekg「移动输入流指针」
tellg「返回输入流中指定位置的指针值」
int  get();
istream& getchar& rch );//读取一个字符
istream& getchar* pch, int nCount, char delim = '\n' );//读取一行字符,不提取'\n'
点击并拖拽以移动

「ostream公有成员函数:」

「函数」「功能」
put「ostream& put( char ch );无格式,插入一个字节」
write「ostream& write( const char* pch, int nCount );从无格式,插入一字节序列」
flush「刷新输出流」
seekp「移动输出流指针」
tellp「返回输出流中指定位置的指针值」

字符输入的流函数:

  • cin.get()
    • 从输入流中得到一个字符
    • 遇到文件结束符返回值为EOF,即-1
  • cin.get(c)
    • 提取一个字符将其赋给c
    • 遇到文件结束符,返回值为0
  • cin.get(字符数组, 字符个数n, 终止字符)或cin.get(字符指针, 字符个数n, 终止字符)
  • 从输入流中提取n-1个字符放到字符数组中
  • 遇到文件结束符,返回值为0
  • cin.getline(字符指针, 字符个数n, 终止字符)
  • 从输入流中提取n-1个字符放到字符数组中
  • 遇到文件结束符,返回值为0
  • cin.get()和cin.getline()的区别
    • 当读到终止字符时,
    • cin.getline()  将指针移到「终止字符之后」
    • cin.get()   将指针移到「终止字符处」
    • 下次继续读取时的位置就不同。
    • 从输入流提取n-1个字符放入数组,
    • 遇到文件结束符时,返回值为0.
    • 相同点
    • 不同点

5.文本文件操作

文本文件:以ASCII码形式存储,文件的每个字节都是用ASCII代码存放数据,即每个字节存放一个字符,这种文件又称字符文件

文件流的体系:

图片

操作步骤:

图片

「5.1.打开文件:」

#include<iostream>
#include<fstream>
using namespace std;
int main()
{
    //数据写入文件
    ofstream out('1.txt',ios::out);//如果没有该文件,会自动创建文件,并打开
    //或者 ofstream out;
    //     out.open('1.txt',ios::out);//相当于调用out对象的构造函数
    if(!out) //如果打开不成功,out返回数值为0
        cerr<<'Can't open the file!\n';
    
    //数据读出文件
    ifstream in('1.txt',ios::in);//如果文件不存在,不能自动创建
    // 或者 ifstream in;
    //      in.open('1.txt',ios::in);
    if(!in)
        cerr<<'Can't open the file\n';
    return 0;
}
点击并拖拽以移动

打开文件的第二个参数:

「标识常量」「意义」
「ios::in」「读方式打开文件」
「ios::out」「写方式打开文件,(会覆盖之前的内容)」
「ios::ate」「打开文件时,指针指向文件尾」
「ios::app」「追加方式」
「ios::trunc」「删除文件现有内容」
「ios::binary」「二进制方式打开,默认为文本方式」
「ios::in|ios::out」「以输入和输出方式打开文件,文件可读写」
「ios::out|ios::binary」 「或者」 「ios::in|ios::binary」「以二进制方式打开一个输出(或者输入)文件」

5.2.读/写文件

文件写入:

#include<iostream>
#include<fstream>
using namespace std;
int main()
{
    //数据写入文件
    ofstream out('1.txt',ios::out);//如果没有该文件,会自动创建文件,并打开
    //或者 ofstream out;
    //     out.open('1.txt',ios::out);//相当于调用out对象的构造函数
    if(!out) //如果打开不成功,out返回数值为0
        cerr<<'Can't open the file!\n';
    out<<'xingmaqi zaizheli\n'<<'1314\n';
    out.close();
    return 0;
}
点击并拖拽以移动

结果:

图片

从文件读数据:

假设文件里面有数据 1 2 3 4 5 6 7 8 9

#include<iostream>
#include<fstream>
using namespace std;
int main()
{
    //数据读出文件
    ifstream in('1.txt',ios::in);//如果文件不存在,不能自动创建
    // 或者 ifstream in;
    //      in.open('1.txt',ios::in);
    if(!in)
        cerr<<'Can't open the file\n';
    int num[9];
    for(int i = 0 ; i < 9 ; i++)
    {
        in >> num[i];
    }
    for(int i = 0;i < 9;i++)
    {
        cout<<num[i]<<' ';
    }
    in.close();
    return 0;
}
//输出结果为
//1 2 3 4 5 6 7 8 9
点击并拖拽以移动

6.二进制文件顺序读写操作

打开二进制文件用ios::binary方式,它的读写方式由程序控制。

//文件读写成员函数
//将第二个参数指定的字节数读入到以第一个参数为起始地址的内存中。
istream & istream::readchar *, int );
istream & istream::readunsigned char *, int );
istream & istream::readsigned char *, int );

//从第一个参数为起始地址的内存开始,将连续的第二个参数指定字节数的
//内容写到文件中。 
ostream & ostream::writeconst  char *, int );
ostream & ostream::writeconst unsigned char *, int );
ostream & ostream::writeconst signed char *, int );
点击并拖拽以移动

由于 C++ 不支持指向字节的指针,因此 write和read 函数原型将指向缓冲区的地址是指向 char 的指针,「二进制的读写并不能查看二进制文件的内容。」

6.1.打开文件

以二进制模式打开文件,其方法是使用 ios::binary 标志

    ofstream out('1.txt',ios::binary|ios::out);
    ifstream in('1.txt',ios::in|ios::binary);
点击并拖拽以移动

或者:

    ofstream out;
    out.open('1.txt',ios::binary|ios::out);
    ifstream in;
    in.open('1.txt',ios::in|ios::binary);
点击并拖拽以移动

6.2.文件读写操作

强制转换表达式:将某个值强制转化为目标类型。

reinterpret_cast<TargetType>(value);
点击并拖拽以移动

当调用 write 和 read 函数时,需要告诉编译器将缓冲区的地址解释为指向 char 的指针。要做到这一点,可以将每个元素使用强制转换运算符将值转换为指向char的指针类型,也可以使用称为 reinterpret_cast 的特殊形式的类型转换。

#include<iostream>
#include<fstream>
using namespace std;
int main()
{
    ofstream out('1.txt',ios::out|ios::binary);
    int num[] = {1,2,3,4,5,6,7,8,9,10};
    // int size = sizeof(num)/sizeof(num[0]);
    for(int i=0;i<10;i++)
    {//将每个数组元素的地址强制转换为指向char的指针类型
        out.write((char*)(&num[i]),sizeof(int));
    }
    //或者 直接利用相关的强制转化符将数组的地址类型强制转换为char型指针
    // out.write(reinterpret_cast<char*>(num),sizeof(num));
    out.close();

    ifstream in('1.txt',ios::binary|ios::in);
    int temp[10];
    for(int i=0;i<10;i++)
    {
        in.read((char*)(&temp[i]),sizeof(int));
    }
    in.close();
    //或者
    // in.read(reinterpret_cast<char*>(temp),sizeof(temp));

    //输出验证
    for(int i=0;i<10;i++)
    {
        cout<<temp[i]<<' ';
    }
    return 0;
}
点击并拖拽以移动

上述代码实现了将num[10]={1,2,3,4,5,6,7,8,,9,10}数组的10个数据写入到文件中,然后将文件中的数据读入到临时创建的数组temp中,最后输出temp数组中的数据进行验证。

「注意:」

  • 强制转换的类型是指针类型,转换的目标类型是char型指针

7.二进制文件随机读写操作

读写指针:

图片

随机访问流:

「ios::seek_dir 的值:」

cur   相对于当前读指针所指定的当前位置

beg   相对于流的开始位置

end   相对于流的结尾处

enum  ios::seek_dir { beg = 0 ; cur = 1 , end = 2 }  ;

「istream类操作类指针的成员函数」

istream & istream :: seekg ( long  pos) ;
//读指针从流的起始位置向后移动由pos指定字节
istream & istream :: seekg ( long off,  ios::seek_dir ) ;
//读指针从流的seek_dir位置移动 off 指定字节
long & istream :: tellg () ;
//返回读指针当前所指位置值
点击并拖拽以移动

「ostream类操作类指针的成员函数」

 ostream & ostream :: seekp ( long  pos ) ;
 //写指针从流的起始位置向后移动由参数指定字节
 ostream & ostream :: seekp ( long  off ,  ios::seek_dir ) ;
 //写指针从流的seek_dir位置移动由 off 指定字节
 ostream & ostream :: tellp ( ) ;
 //返回写指针当前所指位置值
点击并拖拽以移动

「注意:」

「文件读写指针是以字节为单位进行移动的。如果要移动读写指针,需要根据所存储类型的内存大小为单位进行移动,而获取内存大小可以通过sizeof()实现。」

「下面的程序实现:」

「使用二进制方式将10个int型整数读进文件中,并使用随机读写的方式输出奇数位。」

#include<iostream>
#include<fstream>
using namespace std;
int main()
{
    ofstream ofile;
    ofile.open('1.txt',ios::out|ios::binary);
    if(!ofile)
    {
        cerr<<'Can't open the file!';
        exit(1);
    }
    cout<<'输入10个整数:';
    for(int i = 1;i <= 10; i ++)
    {
        int num;
        cin>>num;
        ofile.write((char*)(&num),sizeof(int)) ;
    }
    ofile.close();

    int a[10];
    ifstream ifile;
    ifile.open('1.txt',ios::in|ios::binary);
    if(!ifile)
    {
        cerr << 'Can't open the file!\n';
        exit(1);
    }
    for(int i=0;i<10;i += 2)
    {
        //文件指针每次从文件开始处向后移动i个int型字节单位处
        ifile.seekg(sizeof(int)*i,ios::beg);
        //从文件指针当前所处的位置开始读数据
        ifile.read((char*)(&a[i]),sizeof(a[0]));
        //输出读的数据
        cout<< a[i]<< ' ';
    }
    return 0;
}
点击并拖拽以移动

测试结果:

输入:1 2 3 4 5 6 7 8 9 10
输出:1 3 5 7 9
点击并拖拽以移动

今天的介绍就到这里,欢迎点赞关注哦!


    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多