【c++笔记十三】c++中的输入、输出和文件操作

2015年2月6日
周五 晴

很快到星期五了,感觉这一个星期都是在复习,这两周过后我觉得c++的基本知识应该掌握的差不多了,可以追求一点更高层次的东西了。

今天讲一讲c++中的输入、输出和文件操作,差不多c++基本语法就结束了。可能以后的笔记中不会再强调基本的语法知识。

——————————————分割线——————————————

其实在c语言中,我们就已经学习了基本的输入输出和文件操作,像什么printf、scanf、fopen、fclose之类的。其实c++的IO和文件都和c差不多,只不过吧c中的这些都封装到类中去了。我们先看一张图:

这张图就是所有与c++相关的IO和文件的操作了。我们一起来简单的认识一下他们:

(1)标准的输入输出:

istream ostream  --> #include <iostream>

标准对象有cin cout

(2)操作字符串的流:

istringstream ostringstream  --> #include <sstream>

(3)操作文件的流:

ifstream  ofstream  fstream    --> #include <fstream>

图中外圈的七个流(类),是我们c++中会经常用到的。现在我为大家一一介绍一下他们:

一.标准的输入输出

1.标准的输入输出对象

C++的iostream文件中自动创建了8个流对象但我们一般用到其中4个:cin、cout、cerr、clog

还有四个是用于宽字符流的:wcin、wcout、wcerr、wclog。(知道即可)

cin和cout代表标准的输入输出流,cerr和clog代表标准错误流。他们之间是有区别的,cout是带有缓冲的并且可以重定向,而cerr和clog是没有缓冲且不可重定向的。我们先看代码再解释什么是重定向:

#include <iostream>
using namespace std;
int main()
{
    cout<<"hello";
    clog<<" world";
    cerr<<"test";
    return 0;
}

这是一个简单的io小程序,分别用cout clog cerr在屏幕上输出几个单词:

但是,如果我们用到重定向的话,输出的结果就不是这样了。我们先把编译后生成的.exe(可执行文件)复制一份放到c盘的根目录下,以管理员身份运行命令提示符(cmd)。操作如图:

先切换到根目录(cd /),再运行01io.exe(上面那段代码的可执行文件) > a.txt 。这“>”就是一个输出重定向符,将01io.exe的输出结果输出到a.txt这个文件中去。(一开始输出都是输出在屏幕上的,但是现在我将输出的目标改为文件,所以叫重定向(重新设定输出的方向))。我们发现屏幕上只输出了“ worldtest”,cout输出
 的“hello”不见了。我们再打开a.txt这个文件看看里面的内容:

我们发现a.txt文件中只有“hello”。这就说明了只有cout能够重定向,cerr和clog是不具备重定向的。所以用cerr和clog的好处就是无论什么情况下我都能输出任何信息(一般是错误信息)。cin类似cout,这里就不再重复了。

2.其他IO类方法

除了上面要讲的四个对象之外,还有给大家补充几点常用IO类的方法(成员函数)。

(1)get和put

get是得到一个字符,put是输出一个字符。我们看看c++帮助手册里面关于它们的介绍:

get方法是用在输入流中的(input stream),put方法是用在输出流中(out stream)。put使用方法很简单,就是把一个字符写到输出流中而已。而get我们一般用它的前两个方法:

A.int get();

该方法是读入一个字符,并且返回这个字符的整数值(ACSII码)。

#include <iostream>
using namespace std;
int main()
{
    int c;
    while(1){
        c = cin.get();
        cout<<"c="<<c<<endl;
        cout.put(c);
        cout<<endl;
    }
    return 0;
}

我们用get获取了“0、a、A”这三个字符的ACSII码并用cout输出,我们又用put输出了这个三个整数的字符形式。并且我们可以发现一个很奇怪的现象,每两次输入之间空了好几行,为什么呢?

因为,get把我们回车也输入进去了。所以我们可以看到,“回车”的ACSII码是10,put输出的是回车,cout<<endl又回了一次车,所以每两次输入间空了两行。

如果get读取到的字符是EOF(end of file,文件结束符),就会自动结束。

 B.istream& get( char& ch );

这个get方法读入一个字符的引用,返回的却是一个输入流。当get遇到EOF的时候,返回的istream变为0。例子可以参考c++帮助手册的那个例子。

(2)getline

这种方法是用来读取一行数据的。

A.istream& getline( char* buffer, streamsize num );

这种方式的getline将一行字符串放在buffer中,读取num-1个字符或遇到换行或EOF时结束。如:

#include <iostream>
using namespace std;
int main()
{
    char str[20];
    cin.getline(str,10);
    cout<<str<<endl;
    return 0;
}

规定读入10个字符,实际只读入9个字符(还有一个‘\0’)。

B.istream& getline( char* buffer, streamsize num, char delim );

这种方法和上面的方法类似,但是输入了num-1个字符或遇到delim字符或遇到EOF时就终止输入,就算换行也不会影响它的输入。如:

#include <iostream>
using namespace std;
int main()
{
    char str[20];
    cout<<"请输入:";
    cin.getline(str,10,'$');
    cout<<"输入的字符串是:"<<str<<endl;
    return 0;
}

我们可以看见,中间我们换了一行可是输入还在继续,直到读到’$’符号的时候,getline才结束,且不会把‘$’字符读进去。

但是有一点一定要特别注意:如果我们输入的字符串长度超过num-1,就会发生流对象错误,拒绝IO访问。所以遇到流对象发生错误的时候我们要有所处理:

首先,我们需要纠正流(复位),用到.clear()方法。接着,我们还需要清理缓冲区,用到.ignore()方法。特别需要注意ignore的用法:

istream& ignore( streamsize num=1, int delim=EOF )

我们先来看看这种流对象错误,并且是不是真正的拒绝了IO访问。

#include <iostream>
using namespace std;
int main()
{
    char str[20];
    cout<<"cin="<<cin<<endl;
    cin.getline(str,10);
    cout<<str<<endl;
    cout<<"cin="<<cin<<endl;
    int num=100;
    cin>>num;
    cout<<"num="<<num<<endl;
    return 0;
}

cin既然是一个对象,它重载了>>运算符,我们就可以输出cin的值看看。我们发现一开始cin的值正常的,可是一旦我们getline的字符串超出了10个字符,cin流就发生错误,值变为了0。并且cin>>num操作也没有进行(拒绝了输入操作),直接输出num的值(num值没有输入,因此不会改变)。

我们再来更正这个程序:

#include <iostream>
using namespace std;
int main()
{
    char str[20];
    cout<<"cin="<<cin<<endl;
    cin.getline(str,10);
    cout<<str<<endl;
    cout<<"cin="<<cin<<endl;
    if(!cin){
        cin.clear();
        cin.ignore(100,'\n');
    }
    cout<<"cin="<<cin<<endl;
    int num=100;
    cin>>num;
    cout<<"num="<<num<<endl;
    return 0;
}

在getline超出10个字符后cin的值变为0,我们通过纠正cin流并清除输入缓冲区,cin的值又变为正常的值了,最后输入num的值使num值发生改变。

二.字符串的IO类

C中你可能用过sprintf和sscanf这样的格式化字符串操作(不懂的自己百度学习哦)。我们可以一起写个程序回顾一下这两个函数:

#include <stdio.h>
int main()
{
    char str[100];
    char name[5]="zm";
    int age=20;
    double height=1.75;
    sprintf(str,"%s:%d:%lf",name,age,height);
    printf("%s\n",str);

    int num;
    sscanf("12345","%d",&num);
    printf("%d\n",num);
    return 0;
}

在c++中对应的两个类分别是istringstream,ostringstream。因为c++中使用string代替字符数组,所以一般字符串类型都是在操作string。ostringstream是将数据写入字符串,istringstream是把数据从字符串中读出

看代码就很快学会了:

#include <iostream>
#include <sstream>
#include <iomanip>
using namespace std;
class Date{
    int year;
    int month;
    int day;
public:
    Date(int year=0,int month=0,int day=0)
        :year(year),month(month),day(day){}
    friend ostream& operator<<(ostream&,const Date&);
};
ostream& operator<<(ostream& os,const Date& date){
    return os<<setfill('0')<<date.year<<"-"
        <<setw(2)<<date.month<<"-"<<setw(2)<<date.day;
}
int main()
{
    string name="zm";
    int age=20;
    double height=1.75;
    ostringstream ostr1;
    ostr1<<name<<":"<<age<<":"<<height;
    string str = ostr1.str();
    cout<<str<<endl;

    ostringstream ostr2;
    ostr2<<Date(2015,2,6);
    str=ostr2.str();
    cout<<str<<endl;

    istringstream istr("zhc 21 1.76");
    istr>>name>>age>>height;
    cout<<"姓名:"<<name<<endl;
    cout<<"年龄:"<<age<<endl;
    cout<<"身高:"<<height<<endl;
    return 0;
}

ostringstream类的ostr1用来拼接name(string),age(int),height(double),像普通流对象一样的使用<<运算符(注意观察和sprintf的区别)。.str()方法,是将stringstream对象转换为string字符串。ostr2对象是用来是输出类Date的,我们在Date类中重载了运算符>>,所以ostringstream对象用起来一样没有问题。

最后我们定义了istringstream类对象istr,用来从字符串“zhc 21 1.76”读出name,age,height(注意和sscanf的区别,<<会自动识别类型)。

三.文件操作

C++中我们使用ifstream创建文件读取流(读文件),用ofstream创建文件输出流(写文件):

ifstream( const char *filename, openmode mode );

ofstream( const char *filename, openmode mode );

第二个参数mode,是指打开文件的模式。基本的打开模式如下图:

我们使用write方法写文件,read方法读文件:

istream& read( char* buffer, streamsize num );

ostream& write( const char* buffer, streamsize num );

我们经常还会用到:int gcount(); 获取实际读取的字节数。

文件操作基本和c类似,基本知识还不是很熟悉的自己学哦(百度),我只是讲讲c++中怎么操作而已。

我们一起来做一道题,把一个结构体整体为单位写入文件中,再从文件中读取出来:

#include <iostream>
#include <fstream>
using namespace std;
struct Date{
    int year;
    int month;
    int day;
    Date(int year=0,int month=0,int day=0)
        :year(year),month(month),day(day){}
    void show(){
        cout<<year<<"-"<<month<<"-"<<day<<endl;
    }
};
int main()
{
    ofstream ofs("a.txt");
    Date date1(2015,2,6);
    ofs.write((const char*)&date1,sizeof(Date));
    ofs.close();

    ifstream ifs("a.txt");
    Date date2;
    ifs.read((char*)&date2,sizeof(Date));
    ifs.close();
    date2.show();
    return 0;
}

我们可以看见,我们先把结构体强制类型转换为const char*后写入文件中(按字节写入),最后再用char* 方式从文件中读出来(按字节读)。大家一定要体会,用类的方法去操作文件,原来使用的什么wirte、open都是文件流的成员函数。

最后,我们再综上所有的知识点做一道题目:现在有一个配置文件(如下图),从文件中读出各参数的值。

我们一起来看代码:

#include <iostream>
#include <fstream>
using namespace std;
int main()
{
    string name;
    int price;
    string cpu;
    double size;
    ifstream ifs("xiaomi.txt");
    char temp[20];
    ifs.getline(temp,20,'=');
    ifs>>name;
    ifs.getline(temp,20,'=');
    ifs>>price;
    ifs.getline(temp,20,'=');
    ifs>>cpu;
    ifs.getline(temp,20,'=');
    ifs>>size;
    cout<<name<<endl;
    cout<<"价格:"<<price<<"元"<<endl;
    cout<<"CPU:"<<cpu<<endl;
cout<<"屏幕大小:"<<size<<endl;
ifs.close();
    return 0;
}

运用>>运算符自动识别类型的特性,我们很轻松读取出来了所有值。注意本程序中getline的用法,遇到”=”停止。

————————————————结束语———————————————————

C++中的IO和文件操作,就是用类的思想,把所有以前c中使用的各种函数全部转换为成员函数。大家一定要体会,先建立IO流和文件流再操作的思想。

总结一下:我们主要是讲了c++中基本IO、字符串IO和文件操作的方式,体验和c中这些操作的区别。学会用面向对象(类)的思维去体会这些操作。

时间: 2024-10-06 13:50:40

【c++笔记十三】c++中的输入、输出和文件操作的相关文章

java输入输出流及文件操作

*Author:Yuanhonglong *Date:2013-11-29 *1948281915package mine.file.Read_Write;import java.io.BufferedReader;import java.io.File;import java.io.FileInputStream;import java.io.FileReader;import java.io.IOException;import java.io.InputStream;import java

dedecms中提取的zip压缩文件操作类zip.class.php

从织梦DeDeCMS中提取的zip压缩文件操作类,包含zip文件压缩.解压缩.添加文件到压缩包中等多个实用的函数,注释详细方便使用. 下载:dedecms中提取的zip压缩文件操作类zip.class.php 包含的函数和简单的使用方法: 1.函数get_List($zip_name) ,函数作用:获取zip文件中的文件列表.函数参数 $zip_name  zip文件名.返回值 文件列表数组. 2.函数Add($files,$compact),函数作用:增加文件到压缩文件.函数参数 $files

Java输入输出流和文件操作

操作系统中的文件和目录概念 文件与文件系统 文件是信息的一种组织形式,是存储在外部存储介质上的具有标志名的一组相关信息集合. 文件系统用文件概念来组织和管理存放在各种介质上的信息.文件系统提供目录机制实现文件的"按名存取". 目录结构与文件检索 目录是文件系统组织和管理文件的基本单位,目录中保存它所管理的每个文件的基本属性信息(成为文件目录项或文件控制块).除了文件外,目录中还可以包含子目录和文件,子目录中还可以再有子目录和文件,由此构成目录的多级树状结构.文件是这种树状结构的叶子节点

python学习笔记-(七)python基础--集合、文件操作&amp;函数

本节内容 1.集合操作 2.文件操作 3.字符编码与转码 4.函数操作 1.集合操作 集合是一个无序的.不重复的数据组合: 1.1 常用操作 它的作用是: 1)自动去重:列表变成集合,自动去重: 1 2 3 4 >>> list_1 = [1,4,4,5,6,7,9,10] >>> list_1 =set(list_1) >>> print(list_1) {1, 4, 5, 6, 7, 9, 10} 2)关系测试:测试两组数据之间的关系,交集.并集.

在Node.js中使用ejsexcel输出EXCEL文件

1.背景 在Nodejs应用程序中输出Excel,第一印象想到的一般是node-xlsx,这类插件不仅需要我们通过JS写入数据,还需要通过JS进行EXCEL显示样式的管理. 这是个大问题,不仅代码冗余,而且非常不易于维护,假设业务需要合并一个单元格,或者换个颜色,我们都需要重新修改代码. 反思这个问题的核心,跟使用JS来操作页面样式其实是同一个道理,我们没有把数据/业务逻辑操作和样式分离. 在WEB开发中,我们有了CSS来分离处理样式的问题. 现在,在输出EXCEL的时候,我们也希望将样式问题分

c# 中crystal report输出PDF文件

工程中引入以下crystal report的类库crystaldecisions.crystalreports.enginecrystaldecisions.reportsourcecrystaldecisions.sharedcrystaldecisions.web 将CrystalReportViewer控件添加入页面.同时为他的初始化添加代码System.Data.DataTable dt = ....;//这里写得到数据表的方法.可以根据自己的爱好写方法,但是一定要得到DataTable

C#中基于流的XML文件操作笔记

System.Xml.XmlReader和System.Xml.XmlWriters是两个抽象类,XmlReader提供了对于XML数据的快速,非缓存,只进模式的读取器,XmlWriter表示一个编写器,该编写器提供快速,非缓存,只进的方式来生成包含XML数据的流文件. XmlReader与SAX读取器类似,不过前者是提取模式(只进只读的游标),后者是推送模式(将事件推送到应用程序). XmlReader的优点是: 1. 简化状态管理 2.可以有多个输入流 3.提取模式可以作为推送模式的基础,反

Servlet中服务器端接收上传文件操作

最近老大分了一个任务,客户端使用C#实现了文件上传,要求我实现服务器端的文件接收保存到本地并且接收成功返回给客户端一个标识.在网上看了一下,大多数都是客户端的文件上传操作,很少有写服务端的处理,借此次机会,分享一下自己在服务端实现的处理逻辑.废话不多说,直接上代码 public class ReciveServlet extends HttpServlet { private static final long serialVersionUID = 1Lpublic ReciveServlet(

Windowsforms 中对话框,流、文件操作

对话框: 1.颜色选择控件——ColorDialog //显示颜色选择器 colorDialog1.ShowDialog(); //把取到的颜色赋值给panel panel1.BackColor = colorDialog1.Color; 2.文件夹选择控件——FolderBrowserDialog //显示文件夹选择器 folderBrowserDialog1.ShowDialog(); //把取到的文件夹用label1显示 label1.Text = folderBrowserDialog1