C++文本文件的操作详细解析和代码分析(基础)

文件的操作

输入输出是指程序与外部设备交换信息
C++把输入输出看成是一个数据流
输入流:外围设备流向内存的数据
输出流:内存流向外围设备的数据
在C++中,输入输出不是语言所定义的部分,而是由标准库提供。
C++的输入输出分为:
基于控制台的I/O
基于文件的I/O
基于字符串的I/O

一.流与标准库

流其实是中间对象(可以调用函数,类类型 )

把键盘和屏幕连起来

二.输入输出缓冲

C++程序不能直接与输入输出设备交换信息。输入输出是通过一个对象实现的。对象是输入输出设备在程序中的代理
每个I/O对象管理一个缓冲区,用于存储程序读写的数据
外围设备与对象批量交换数据

!这里插入图片描述](https://img-blog.csdnimg.cn/202105261929445.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzUxMjY1NTI4,size_16,color_FFFFFF,t_70)在这里插入图片描述这里说的fstream 很明显第一个 f 表示 file ,文件。
所以 ifstream 表示写到文件里,(i表示in 写入)
ofstream 表示从文件中读出, (o表示out 读出)
因为都和文件相关,所以多了一个f

  • 当用户在键盘上输入数据时,键盘输入的数据是存储在输入缓冲区中,在输入回车后,输入数据被移到输入缓冲区。当执行“>>”操作时,从输入缓冲区中取数据存入变量,如缓冲区中无数据,则等待从外围设备取数据放入缓冲区。
    “<<”是将数据放入输出缓冲区。如有下列语句:
    cout << “please enter the value:”;
    系统将字符串常量存储在cout的缓冲区中
    这里缓冲区满了就会输出。

    在这里插入图片描述

  • 程序正常结束。作为main函数返回工作的一部分,将真正输出缓冲区的内容,清空所有的输出缓冲区;
    比如 " >>"从缓冲区中取出数据存入变量,如果缓冲区无数据则等待从外围设备取数据放入缓冲区。
    “ << ”是 将数据放入输出缓冲区。

  • 当缓冲区已满时,在写入下一个值之前,会刷新缓冲区;

  • 用标准库的操纵符,如行结束符endl,显式地刷新缓冲区;///这就是为什么cout<<endl;会换行

  • 可将输出流与输入流关联起来。在这种情况下,在读输入流时,将刷新其关联的输出缓冲区。在标准库中,将cout和cin关联在一起,因此每个输入操作都将刷新cout关联的缓冲区。

3.基于控制台的I/O

*标准的输入输出流对象
cin是类istream的对象,它与标准输入设备(通常指键盘)连在一起。
cout是类ostream的对象,它与标准输出设备(通常指显示设备)连在一起。
cerr是类osteam的对象,它与标准错误输出设备连在一起。
clog是类ostream的对象,它与标准错误输出设备连在一起。

这里都是键盘和程序的关系

输出流

在这里插入图片描述

在输入输出的时候,不用指定类型(优于C语言的一点。)

格式这里指(间隔,输出几个数字,开头和结尾要不要加东西)

如果输出的指针变量是一个指向字符的指针时,C++并不输出该指针中保存的地址,而是输出该指针指向的字符串。


如果确实想输出这个指向字符的指针变量中保存的地址值,可以用强制类型转换,将它转换成void*类型。

#include
using namespace std;
int main()
{ char ptr = “abcdef”;
cout << "ptr指向的内容为: " << ptr << endl;
cout << “ptr中保存的地址为:” << (void
)ptr
<< endl;
return 0;
}

流输出运算符<<

cout的成员函数

1.put()函数

put 将A显示,并且返回当前对象。
cout.put(‘A’).put(’\n’);

该语句在输出字符A后输出一个换行符。 (.)从左向右结合

2.write ()函数
  • 调用成员函数write可实现无格式输出。它有两个参数。第一个参数是一个指向字符的指针,第二个参数是一个整型值。这个函数把一定量的字节从字符数组中输出。这些字节都是未经任何格式化的,仅仅是以原始数据形式输出。

例如: char buffer[] =“HAPPY BIRTHDAY”;

cout.write(buffer, 10 );

输出buffer的10个字节

  • 函数调用: cout.write(“ABCDEFGHIJKLMNOPQRSTUVWXYZ”, 10); 显示了字母表中的前10个字母。
用于控制输出格式的流成员函数

流的成员函数 *** *** 与之相同作用的控制符

precision(n) setprecision(n) 设置精度为n位

width(n) setw(n) 设置字段宽度为n位

fill(c ) setfill(n ) 设置填充字符 c

 C++提供了大量的用于执行格式化输入/输出的流操纵算子和成员函数。

功能:
整数流的基数:dec、oct、hex和setbase
设置浮点数精度: precision、setprecision
设置域宽: setw、wth
设置域填充字符: fill、setfill

输入流 (流读取运算符>>)

  • 输入流最常用的操作是流读取运算符。

  • 流读取运算符通常会跳过输入流中的空格、tab键、换行符等空白字符。

  • 当遇到输入流中的文件结束符时,流读取运算符返回0(false); 否则,流读取运算符返回对调用该运算符的对象的引用。

  • 流读取运算符在读入EOF时返回0的特性使得它经常被用作为循环的判别条件,以避免选择特定的表示输入结束的值

  • EOF在各个系统中有不同的表示。在windows中是Ctrl+z

    *** 代码示例***

#include
using namespace std;
int main()
{
int grade, highestGrade = -1;
cout << "Enter grade (enter end-of-file to end): ";
while (cin >> grade) {
if (grade > highestGrade) highestGrade = grade;
cout << "Enter grade (enter end-of-file to end): ";
}
cout << "\n\nHighest grade is: " << highestGrade << endl;
return 0;
}

在这里插入图片描述

最后输入ctrl + z 的时候程序停止

此时cin>>grade 整个式子变为0 (即False)

成员函数get
  • get函数用于读入字符或字符串

  • get函数有三种格式:
    不带参数
    带一个参数
    带三个参数

1.不带参数的get函数从当前对象读入一个字符,包括空白字符以及表示文件结束的EOF,并将读入值作为函数的返回值返回。如下列语句

​ while( (ch = cin.get( )) !=EOF ) cout<< ch;
​ 将输入的字符回显在显示器上,直到输入EOF。

2.带一个参数的get函数

​ 带一个字符类型的引用参数,它将输入流中的下一字符(包括空白字符)存储在参数中,它的返回 值是当前对象的引用。
​ 例如,下面的循环语句将输入一个字符串,存入字符数组ch,直到输入回车。
​ cin.get(ch[0]);
​ for ( i = 0; ch[i] != ‘\n’; ++i) cin.get(ch[i+1]);
​ ch[i] = ‘\0’;

3.带三个参数的get 成员函数
  • 要输入一行字符,可用下列语句:
    cin.get(ch, 80, ’\n’); 或 cin.get(ch, 80);
  • 要输入一个以句号结尾的句子,可用下面的语句:
    cin.get(ch, 80, ’.’);
  • 当遇到输入结束符时,程序插入一个’\0’作为输入字符串的结束标记,输入结束符没有放在字符数组中,而是保留在输入流中,下一个和输入相关的语句会读入这个输入结束符。
  • 如对应于语句
    cin.get(ch, 80, ’.’);
    用户输入
    abcdef.↙
    则ch中保存的是字符串“abcdef”,而“.”仍保留在输入缓冲区中。如果继续调用
    cin.get(ch1); 或 cin >> ch1 ;
    则字符变量ch1中保存的是“.”。
read函数
  • 调用成员函数read可实现无格式输入。它有两个参数。第一个参数是一个指向字符的指针,第二个参数是一个整型值。这个函数把一定量的字节从输入缓冲区读入字符数组,不管这些字节包含的是什么内容。

  • 例如: char buffer[80] ;

    cin.read(buffer, 10 );

    读入10个字节,放入buffer

    如果还没有读到指定的字符数,遇到了EOF,则读操作结束。此时可以用成员函数gcount统计输入的字符个数

#include
using namespace std;
int main()
{
char buffer[80];
cout << “Enter a sentence:\n”;
cin.read(buffer, 20); //读键盘输入的数据 (放入缓冲区 )
cout << “\nThe sentence entered was:\n”;
cout.write(buffer, cin.gcount()); //从字符数组里写到屏幕上
cout << endl;
cout << “一共输入了” << cin.gcount() << “个字符\n”;
return 0;
}

在这里插入图片描述

输入输出流中的整型数默认为十进制表示。为了使流中的整型数不局限于十进制,可以插入hex操纵符将基数设为十六进制,插入oct操纵符将基数设为八进制,也可以插入dec操纵符将基数重新设为十进制
也可以通过流操纵符setbase来改变流的基数。该操纵符有一个整型参数,它的值可以是16,10或8,表示将整型数的基数设为十六进制,十进制或八进制
使用任何带参数的流操纵符,都必须包含头文件iomanip
流的基数值只有被显式更改时才会变化,否则一直沿用原有的基数。

#include
#include
using namespace std;
int main()
{
int n;
cout << "Enter a octal number: ";
cin >> oct >> n;
cout << “octal " << oct << n //octal 八进制
<< " in hexdecimal is:” << hex << n << ‘\n’; //转换位16进制
cout << “hexdecimal " << n
<< " in decimal is:” << dec << n << ‘\n’; //转换为十进制
cout << setbase(8) << “octal " << n
<< " in octal is:” << n << endl;
return 0;
}

  • 设置浮点数的精度(即,实型数的有效位数)可以用流操纵符setprecision或基类ios的成员函数precision来实现。
  • 一旦调用了这两者之中的某一个,将影响所有输出的浮点数的精度,直到下一个设置精度的操作为止。
  • 这个操纵符和成员函数都有一个参数,表示有效位数的长度。

格式化示例

#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
    double x = 123.456789, y = 9876.54321;

    for (int i = 9; i > 0; --i)
    {
        cout.precision(i); cout << x << '\t' << y << endl;
    }

    // 或  for (int i = 9; i > 0; --i)  
     //         cout << setprecision(i) << x << '\t' << y << endl;
    return 0;
}



在这里插入图片描述

设置域宽

  • 域宽是指数据所占的字符个数。
  • 设置域宽可以用基类的成员函数width,也可以用流操纵符(setw)。width和setw都包含一个整型的参数,表示域宽。
  • 设置域宽可用于输入,也可用于输出。设置宽度是适合于下一次输入或输出,之后的操作的宽度将被设置为默认值。
  • 当没有设置输出宽度时,C++按实际长度输出。如整型变量a=123,b=456,则输出
    cout << a << b;
    将输出123456。

设置域宽也可用于输入。当输入是字符串时,如果输入的字符个数大于设置的域宽时,C++只读入域宽指定的字符个数。如有定义
char a[9] , b[9] ;
执行语句
cin >> setw(5) >> a >> setw(5) >> b;
用户在键盘上的响应为
abcdefghijklm
则字符串a的值为“abcd”,字符串b的值为“efgh”。

tab键 就是缩进键,也可以重载
例如

ostream &操纵符名(ostream &os )
{
加入需要的操作
}

ostream & tab (ostream &os )
{return os<<‘\n’;}

自定义流操纵算子
  • 程序员可以定义自己的流操纵符
  • 例如,定义输出流操纵符格式如下:
    ostream &操纵符名(ostream &os)
    {需要执行的操作}

#include
using namespace std;
ostream &tab(ostream &os) {return os << ‘\t’;}
int main()
{ int a=5,b=7;
cout << a << tab << b <<endl;
return 0;
}

基于文件的I/O

在这里插入图片描述
文件是驻留在外存储器上、具有标识名的一组信息集合,用来永久保存数据。
与文件相关的概念有:
数据项(字段)
记录
文件
数据库
如在一个图书管理系统中,有一个数据库。这个数据库由书目文件、读者文件及其它辅助文件组成。书目文件中保存的是图书馆中的所有书目信息,每本书的信息构成一条记录。每本书需要保存的信息有:书名、作者、出版年月、分类号、ISBN号、图书馆的馆藏号以及一些流通信息。其中书名是一个字段,作者也是一个字段。


C++语言把每一个文件都看成一个有序的字节流(把文件看成n个字节)
每一个文件以文件结束符(end-of-file marker)结束
当打开一个文件时,该文件就和某个流关联起来
与这些对象相关联的流提供程序与特定文件或设备之间的通信通道

定义一个流对象
打开文件:将流对象与文件关联起来
访问文件
关闭文件 :切断流对象与文件的关联

C++有三个文件流类型:(当作三个对象 的类)
ifstream:输入文件流
ofstream:输出文件流
fstream:输入输出文件流
如:ifstream infile;

用流对象的成员函数open打开文件
用流对象的构造函数打开文件
无论是成员函数open还是通过构造函数,都有两个参数:
打开的文件名
文件打开模式
如果文件打开失败,返回0

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

用成员函数close
对于输出文件,close会将缓冲区中的内容写入文件
main函数执行结束时,会关闭所有打开的文件
良好的程序设计习惯:文件访问结束时,关闭文件

例题

将数字1到10写入文件file,然后从file中读取这些数据,把它们显示在屏幕上。
首先用输出方式打开文件file。如文件file不存在,则自动创建一个,否则打开磁盘上的文件,并清空。用一个循环依次将1到10用流插入符插入文件,并关闭文件。然后,再用输入方式打开文件file,读出所有数据,并输出到屏幕上。

#include <iostream>
#include <fstream>
using namespace std;
int main()
{
    int i = 0;
    fstream fs;  //1.定义一个流对象, fstream 可读可写
    fs.open("test.txt");  //同时打开文件test   //如果只想写文件 就  //fs.open("test.txt",ios::out) 
    if (!fs) { cerr << "create file error\n"; return 1; }  //在没有打开时有提示
    for (int i = 1; i <= 10; i++)
    {
        fs << i << ' ';//从左至右读   写入文件

    }
    fs.close();  //写好后关闭
    fs.open("test.txt");   //再打开读数据
    if (!fs) { cerr << "create file error\n"; return 1; }  //在没有打开时有提示
    while (fs >> i)  cout << i << ' ';
    fs.close();

    return 0;
}

注意这里写 fstream 是不能直接打开一个不存在的文件,也不会创一个文件,所以需要自己在项目文件里添加一个test.txt 文件

虽然名字是test

但是在打开的时候,要加txt 的文件类型

要在屏幕上显示 ,需要cout

直接用fs 是做不到的

#include <fstream>
#include <iostream>
using namespace std;
int main()
{
    ifstream fin("test.txt");
    char s[80];
    int i;
    float x;

    if (!fin) { cout << "cannot open input file\n"; return 1; }
    fin >> i >> x >> s; cout << i << " " << x << s;
    fin.close();

    return 0;
}






#### 文件定位指针

文件定位指针:是一个long类型的数据 ,指出当前读写的位置
C++文件有两个定位指针:读指针和写指针
当文件以输入方式打开时,读指针指向文件中的第一个字节。
文件以输出方式打开时,写指针指向文件中的第一个字节。
当文件以添加方式打开时,写指针指向文件尾。

指定文件定位指针的值,从任意指定位置开始读写。
获取文件定位指针的当前位置 :成员函数tellg和tellp 
设置文件定位指针的位置:成员函数seekg和seekp 

***成员函数  seekg 和seekp

ios::beg(默认):相对于流的开头
ios::cur:相对于流当前位置
ios::end:相对于流结尾



```c++
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
    fstream in("file.txt");
    int i;

    if (!in) { cerr << "open file error\n"; return 1; }
    in.seekp(10);   //指针定位10个
    in << 10;      //输入20 
    in.seekg(0);   //定位在文件头
    while (in >> i) cout << i << ' ';
    in.close();
    return 0;
}


立即访问到文件甚至大型文件中指定的记录。
可以在不破坏其他数据的情况下把数据插入到随机访问文件中。
也能在不重写整个文件的情况下更新和删除以前存储的数据。



```c++
#include <iostream>
#include <fstream>		
using namespace std;

int main()
{
    ofstream out("file.txt");	//定义输出流,并与文件file关联
    ifstream in;			//定义一个输入流对象
    fstream io;
    int i;

    //将1~10写到输出流对象
    if (!out) { cerr << "create file error\n"; return 1; }
    for (i = 1; i <= 10; ++i) out.write(reinterpret_cast<char*> (&i), sizeof(int));
    out.close();
}



二进制文本

内容是   人不认识的字符

如果是文本文件就是12345678910
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值