c++-I/O与文件操作

I/O

继承关系

/*
          ios
         /   \
    istream  ostream
    /     \   /    \
ifstream  iostream  ofstream
            |
            fstream
*/

istream // 从键盘接收输入的数据
ostream // 将数据输出到屏幕上
ifstream // 读取文件中的数据
ofstream // 想文件读取数据
iostream // 输入输出
fstream // 文件读取写入

cin/cout

相当于数据流
cin istream类对象, cin 对于输入的内存时根据数据类型进行判断的
cout ostream类对象, <<表示把字符串发送给cout,表示插入运算符,即把字符串插入到输出流当中
属于内置对象

注意:编译器会根据上下文,判断被重载的运算符的含义

#include <iostream>
int x;
float y;
cin>>x;

// 连续接收数据
cin>>x>>y;

// endl
cout<<x<<endl; // 等价于\n
cout<<x<<"\n";

cin

cin 输入

  • cinscanf
    scanf

字符串类型,有两种定义方式,对应在内存中有两种 读取与写入权限
在读取时,要求字符串有写入权限,因此只能用char s[]="string"这种形式
scanf在读取字符串的时候,在输入时,以空格当做输入结束标志
实际上,scanf 遇到空白字符就停止读取,\t \n 空格

char s[30];

scanf("%s",s) // 这里不需要取地址符&

cin

char*char []都可以用cin赋值,同样遇到空白字符就停止读取,\t \n 空格
cin返回的数据不包括\n等空白符

char buf[20];
char *buf2;
cin>>buf;
cout<<buf<<endl;
cin>>buf2;
cout<<buf2<<endl;

cin读取

cin基本读取形式下,正常读取返回true;遇到结束标注返回false – 没看完,返回值 看 运算符重载一章
标准输入输出设备下:ctrl+D 表示输入结束(\n回车不是
重定向文件输入下:EOF文件末尾表示输入结束

/* 例子1 */
#include <iostream>
using namespace std;

int main()
{
    int n;
    int maxN=0;

    // 输入没有结束,cin就返回true
    while (cin>>n)
    {
        if(maxN<n)
        {
            maxN=n;
        }
    }

    cout<<maxN<<endl;
    
    return 0;
}
/* 例子2  文件重定向 */
/*
    在这种情况下,test.txt 文件中并不需要包含 Ctrl+Z,只要有用空格或回车隔开的若干个正整数即可
    cin 读到文件末尾时,cin>>n就会返回 false,从而导致程序结束
*/
#include <iostream>
using namespace std;

int main()
{
    int n;
    int maxN=0;

    // 输入没有结束,cin就返回true
    freopen("test.txt","r",stdin);
    while (cin>>n)
    {
        if(maxN<n)
        {
            maxN=n;
        }
    }

    cout<<maxN<<endl;

    return 0;
}

cin控制输入

  • cin.get()

从输入流中读入一个字符,返回该字符的ASCII
如果读到输入的末尾,则返回EOF
不会跳过空格、制表符等特殊字符

/*
    c为int类型,而不是char类型,在输入流中碰到ASCII等于0xff的字符时,cin.get()返回0xff,0xff赋值给c,此时如果c是char类型的,那么其值就是-1(因为符号位1代表负数),即等于EOF
    而在c为int类型的情况下,将0xff赋值给c,c的值是255(因为符号位为0,是正数),而非-1,除非读到输入末尾,c的值都不可能是-1
*/
int main()
{
    int c;
    while ((c=cin.get())!=EOF)
    {
        cout.put(c);
    }
}
  • cin.getline()
/*
    从输入流读取bufsize-1个字符到缓冲区buf,直到遇到\n为止,函数会自动在buf中读入数据的结尾添加\0
    函数不会读入\n
*/
istream & getline(char* buf,int bufsize) 

/*
    从输入流读到delim字符为止,\n或delim都不会被读入buf,但是会被从输入流中取走

    函数会自动在buf中读入数据的结尾添加\0
    函数不会读入\n
*/
istream & getline(char* buf,int bufsize,char delim)

// 上面两个函数,如果输入流中\n或delim之前的字符个数达到或超过bufsize,就会导致读入出错,结果就是,本次读入完成但是之后的输入都会失败

getline 与 >>符号 的区别

>> 遇到空格和制表符就会停止
getline遇到\n停止

#include <iostream>
using namespace std;
const int MAX_LINE_LEN=100; // 一行最多多少个字符

int main()
{
    char buf[MAX_LINE_LEN+1];
    
    /*
        cin重定向
    */
    freopen("test.txt","r",stdin);

    // 函数会自动在buf中读入数据的结尾添加\0
    while (cin.getline(buf,MAX_LINE_LEN+1))
    {
        cout<<buf<<endl;
    }

    return 0;
}
  • cin.clear

清楚cin内部的错误标记,使之恢复正常

  • cin.ignore

用于跳过输入中的无用部分,提取有用部分

/*
    跳过输入流中的n个字符,或者跳过delim及其之前的所有字符
*/
cin.ignore(int n=1,int delim=EOF);
int main()
{
    int n;
    cin.ignore(5,'A'); // 跳过输入中的5个字符,或输入中'A'及其假面的字符,其余内容被当做整数输入n中
    cin>>n;
    cout<<n;

    return 0;
}
  • cin.peek – 有一个例子没看完P442

此函数返回输入流中的下一个字符,但是并不将该字符从输入流中取走
该函数不会跳过输入流中的空格、回车符
在输入流已经结束的情况下,返回EOF

cout

连续输出

具体见 ‘面向对象 - 友元函数 - 例子4’

cout<<a<<b;
// 会被变异成
(cout<<a)<<b; // 其中(cout<<a) 会返回一个ostream对象 
ostream & operator<<(ostream &os , clsType &obj)
{
	os<<obj.val;
	return os;
}

cout控制输出

cout 输出的默认是以10进制显示

cout<<hex; // 以十六进制显示
cout<<data<<endl;

// cout<<oct // 以八进制显示 
  • cout.put()

(想输出流缓冲区)输出单个字符(可以是字符或字符的ASCII)
ostream &put(char c)

cout.put(71);
cout.put(71+2);
cout.put('a');
cout.put(ch);
  • cout.write()

(想输出流缓冲区)输出字符串
ostream &write(cosnt char* s,streamsize n) s为要输出的字符串或字符数组,n表示要输出的前n个字符串,n 可小不可大,且不能包含结尾\0

const char* str="helloworld";
cout.write(str,4); // n 可小不可大,且不能包含结尾\0
  • cout.tellp()

put() 和 write() 都是先放到输出流缓冲区,待缓冲区刷新才会输出到指定位置
在缓冲区内的对象,仍然可以修改

tellp() 获取当前输出流缓冲区中最后一个字符所在的位置
返回0:输出流缓冲区没有任何数据
返回-1: 操作失败

从1开始

  • cout.seekp()

用于指定心下一个进入输出缓冲区的字符所在的位置
实现缓冲区数据的覆盖与跳转

从0开始

#include <iostream>
#include <fstream>
#include <string>
#include <cstring>

using namespace std;

int main()
{
    ofstream outfile; 

    outfile.open("test.txt");

    const char* str="hello world";

    for (int i=0;i<strlen(str);i++)
    {
        outfile.put(str[i]);
    }
    cout<<outfile.tellp()<<endl;

    outfile.seekp(6);
    cout<<outfile.tellp()<<endl;

    const char* str2="qt";
    outfile.write(str2,2);

    outfile.close();

    return 0;
}

endl

endl是一个特殊的c++符号
endl插入到输出流中,将导致屏幕光标移动到下一行的开头

cout<<endl;

endl\n

cout<<"some str\n";

// 等价于
cout<<"some str" <<endl;

//等价于
cout<<"some str"; 
cout<<endl;

cerr/clog

用法和cin\cout一样

cerr

用来输出警告和错误信息

clog

用来输出程序执行过程中的日志信息(一般普通用户看不到日志)

cout/cerr/clog

cout可以重定向,将数据输出到指定文件中
cerr/clog,不支持重定向,只能将数据输出到屏幕上
cout/clog都没有缓冲区(缓冲区:输出数据时,先将数据放到缓冲区,等缓冲区满或者手动换行,才会将数据全部显示到屏幕上)
cerr没有缓冲区,直接想数据输出到屏幕上

输入输出重定向

freopen

#include <cstdio>
可以用于scanf printf等重定向


#include <iostream>
#include <string>
#include <cstdio>
using namespace std;

int main()
{
    string name,language;

    // 从文件到数据
    freopen("ini.txt","r",stdin);
    cin>>name>>language; // 从ini输出到变量

    cout<<name<<" "<<language<<endl;

    // 从数据到文件 
    freopen("out.txt","w",stdout);
    cout<<name<<"\n"<<language; // string name 不包括\0 更不包括\n

    return 0;
}

rdbuf – 还没看

控制台输出 – 实验有问题 没有\n

cmd>: file.exe <infile >outfile

输入输出错误处理 – 有一个例子没看P448

流状态:输入输出的错误情况
一旦输入输出流发生错误,对应的标志位(流状态)就会发生改变

  • badbit <- bad() 发生严重错误

致命错误,流不能继续使用

  • eofbit <- eof() 达到输入末尾或文件末尾
  • failbit <- fail() 发生错误

I/O 操作失败,非法数据的使用

  • goodbit <- good() 操作成功没有发生任何错误
int main()
{
    int i=0;
    cin>>i;

    if (!cin)
    {
        if (cin.bad())
        {
            // 发生严重故障
        }
        else if (cin.eof())
        {
            // 读取结束
        }
        else if (cin.fail())
        {
            // 遇到一些意外情况

            cin.clear(); // 清除、恢复流状态
        }
        
        
    }

    return 0;
}

文件操作

继承关系

/*
          ios
         /   \
    istream  ostream
    /     \   /    \
ifstream  iostream  ofstream
            |
            fstream
*/

ifstream // 读取文件中的数据
ofstream // 想文件读取数据
fstream // 文件读取写入
open()
is_open()
close()
swap()
>>
gcount()
get()
getline()
ignore()
peek()
putback()
<<
put()
write()
tellp()
seekp()
flush()
good()
eof()

文本与二进制

只是编辑器是否能够自动进行编码转换的区别

打开方式

ios::in // 读取,且文件必须存在
ios::out // 写入,文件不存在则新建;存在则清空再写入
ios::app // 追加写,文件则存在则创建
ios::binary // 以而二进制文件方式打开,默认是文本方式打开

ios::in | ios::binary // 二进制读去
ios::out | ios::binary // 二进制写入

使用open(成员)函数打开

例子

#include <iostream>
#include <fstream>
using namespace std;

int main()
{
    const char* name="zhangsan";

    // fstream file; // 定义文件流对象
    // file.open("test2.txt",ios::out); // 将文件与文件流关联
    // 等价于
    ofstream file2;
    file2.open("test3.txt",ios::out);

    if (file2.is_open()) // 还可以直接为文件名 if (file2)
    {
        cout<<"DONE file is open!"<<endl;
        file2.write(name,8); // n 可小不可大,且不能包含结尾\0
        file2.close();
    }
    else
    {
        cout<<"ERROR file not open!"<<endl;
    }

    return 0;
}

使用流类构造函数打开

例子

#include <iostream>
#include <fstream>
using namespace std;

int main()
{
    const char* name="zhangsan";

    ofstream file3("test4.txt",ios::out);

    if (file3.is_open()) // 还可以直接为文件名 if (file2)
    {
        cout<<"DONE file is open!"<<endl;
        file3.write(name,8); // n 可小不可大,且不能包含结尾\0
        file3.close();
    }
    else
    {
        cout<<"ERROR file not open!"<<endl;
    }

    return 0;
}

文件缓冲区

使用flush 及时刷新输出流缓冲区

const char* name="zhangsan";

ofstream file3("test4.txt",ios::out);

if (file3.is_open()) // 还可以直接为文件名 if (file2)
{
    cout<<"DONE file is open!"<<endl;
    file3.write(name,8); // n 可小不可大,且不能包含结尾\0
    file3.flush(); // 刷新输出流缓冲区
    file3.close();
}
else
{
    cout<<"ERROR file not open!"<<endl;
}

文本读写

>><<读写文件
fstream 或 ifstream + ios::in 使用>> 运算符实现文件读取
fstream 或 ofstream + ios::out 使用<< 运算符实现文件写入

例子

#include <iostream>
#include <fstream>

using namespace std;

int main()
{
    int x,sum=0;

    ifstream srcFile("ini.txt",ios::in); // 以文本模式打开

    if (!srcFile.is_open())
    {   
        cout<<"ERROR: failed to open file"<<endl;
        return 0;
    }

    ofstream dstFile("out.txt",ios::out); // 以文本方式打开
    if (!dstFile.is_open())
    {
        srcFile.close();
        cout<<"ERROR: faile to open file"<<endl;
        return 0;
    }

    while (srcFile>>x)
    {
        sum+=x;
        dstFile<<x<<" ";
    }

    cout<<sum<<endl;
    dstFile.close();
    srcFile.close();

    return 0;
}

二进制读写

read() wirte()读写文件
ostream & write(char *buffer,int count) buffer指定要写入文件的二进制数据的起始位置,count指定写入字节的个数。注意:write()方法会从文件写指针指向的位置将二进制数据写入(文件打开默认写指针指向文件的开头)
istream & read(char *buffer,int count) buffer指定读取字节的起始位置,count指定读取字节的个数,该方法返回一个调用该方法的对象的引用。注意read()方法从文件读指针指向的位置开始读取若干字节

#include <iostream>
#include <fstream>

using namespace std;

class Student
{
public:
    char name[20];
    int age;
};

int main()
{
    Student stu;
    ofstream outFile("students.dat",ios::out | ios::binary);

    while (cin>>stu.name>>stu.age)
    {
        outFile.write((char*)&stu,sizeof(stu)); // ??? 强制转换
    }
    outFile.close();

    Student stu2;
    ifstream inFile("students.dat",ios::in | ios::binary);

    if (!inFile.is_open())
    {
        cout<<"ERROR: failed to open file!"<<endl;
        return 0;
    }

    // 因为是按字节读取的
    while (inFile.read((char *)&stu2,sizeof(stu2)))
    {
        cout<<stu2.name<<" "<<stu2.age<<endl;
    }
    inFile.close();
    
    return 0;
}

get/put

逐个字符读写
ostream & put(char)

int get() 返回读取到的字符的ASCII码,遇到末尾则返回EOF
istream &get(char &c) 将读取到的字符赋值给定义的字符变量
get()需要注意的一点:操作系统在接收到get()方法的请求后,哪怕只读取一个字符,也会一次性从文件中将很多数据(通常至少是512个字节)读到一块内存空间中间中(文件流输入缓冲区),当读取下一个字符时,就不需要再访问硬盘中的文件,直接从该缓冲区中读取即可

#include <iostream>
#include <fstream>
using namespace std;

int main()
{
    char c;
    ofstream outFile("out.txt",ios::out|ios::binary);

    if (!outFile.is_open())
    {
        cout<<"ERROR: failed open file"<<endl;

        return 0;
    }

    while (cin>>c)
    {
        outFile.put(c);
    }

    outFile.close();
    return 0;
}
int main()
{
        char c2;
    
    ifstream inFile("out.txt",ios::out | ios::binary);

    if (!inFile.is_open())
    {
        cout<<"ERROR: failed open file"<<endl;
        return 0;
    }

    while ((c2=inFile.get()) && c!=EOF)
    {
        cout<<c2;
    }

    inFile.close(); 

    return 0;
}

读写指针和获取指针

  • 设置指针

seekg 读指针(ifstream fstream)ostream & seekp(int offset,int mode)
seekp 写指针(ofstream fstream)istream & seekg(int offset,int mode)

mode表示读写指针的设置模式

  • ios::beg 从0(文件开头)向后offset个字节,只能是正数
  • ios::cur 从当前指针向前(负数)向后(整数)offset个字节
  • ios::end 从(文件结尾)向前offset个字节,只能是负数
  • 获取指针

tellg 读指针的位置,(ifstream fstream),int tellg()
tellp 写指针的位置,(ofstream fstream),int tellp()

  • 获取文件长度
    seekg + tellg
#include <iostream>
#include <fstream>

using namespace std;

class Student
{
public:
    char name[20];
    int age;
};

int main()
{
    Student stu;

    // 读写打开
    fstream ioFile("students.dat",ios::in | ios::out);

    if (!ioFile.is_open())
    {
        cout<<"ERROR: failed to open file!"<<endl;
        return 0;        
    }

    ioFile.seekg(0,ios::end); // 读指针到文件末尾

    int L=0;
    int R=ioFile.tellg()/sizeof(Student)-1;

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值