一、C语言的输入与输出
scanf
和printf
是C语言中最为常用的输入输出。
scanf()
: 从标准输入设备(键盘)读取数据,并将值存放在变量中。
printf()
: 将指定的文字/字符串输出到标准输出设备(屏幕)。
注意宽度输出和精度输出控制。
C语言借助了相应的缓冲区来进行输入与输出。
对于输入输出缓冲区的理解:
1.可以屏蔽掉低级I/O的实现,低级I/O的实现依赖操作系统本身内核的实现,所以如果能够屏蔽这部分的差异,可以很容易写出可移植的程序。
2.可以使用这部分的内容实现“行”读取的行为,对于计算机而言是没有“行”这个概念,有了这部分,就可以定义“行”的概念,然后解析缓冲区的内容,返回一个“行”。
二、流是什么
可以类比河流中流水一样,“流”即是流动的意思,是物质从一处向另一处流动的过程,是对一种有序连续且具有方向性的数据(其单位可以是bit,byte,packet)的抽象描述。 C++流是指信息从外部输入设备(如键盘)向计算机内部(如内存)输入和从内存向外部输出设备(显示器)输出的过程。这种输入输出的过程被形象的比喻为“流”。
它的特性是:有序连续、具有方向性
为了实现这种流动,C++定义了I/O标准类库,这些每个类都称为流/流类,用以完成某方面的功能
三、C++IO流
读取文件大体上可以分为五步(之所以用in,读取文件这个行为对于程序来说相当于输入数据,所以用in,写文件的行为,是数据从程序中流出,所以用out。in,out都是相对于程序而言)。
读文件是in --> ifstream
写文件是out --> ofstream
C++系统实现了一个庞大的类库,其中ios为基类,其他类都是直接或间接派生自ios类
3.1 C++标准IO流
C++标准库提供了4个全局流对象cin
、cout
、cerr
、clog
- 使用
cout
进行标准输出,即数据从内存流向控制台(显示器)。 - 使用
cin
进行标准输入即数据通过键盘输入到程序中, - 同时C++标准库还提供了
cerr
用来进行标准错误的输出, - 使用
clog
进行日志的输出,
从上图可以看出,cout、cerr、clog是ostream类的三个不同的对象,因此这三个对象现在基本没有区别,只是应用场景不同,在使用时候必须要包含文件并引入std
标准命名空间
注意:
- cin为缓冲流。键盘输入的数据保存在缓冲区中,当要提取时,是从缓冲区中拿。如果一次输入过多,会留在那儿慢慢用,如果输入错了,必须在回车之前修改,如果回车键按下就无法挽回了。只有把输入缓冲区中的数据取完后,才要求输入新的数据。
- 输入数据类型必须与要提取的数据类型一致,否则出错。出错只是在流的状态字state中对应位置(置1),程序继续。
- 空格和回车都可以作为数据之间的分格符,所以多个数据可以在一行输入,也可以分行输入。但如果是字符型和字符串,则空格(ASCII码为32)无法用cin输入,字符串中也不能有空格。回车符也无法读入。使用getline()输入。
- cin和cout可以直接输入和输出内置类型数据,原因:标准库已经将所有内置类型的输入和输出全部重载了。
- 对于自定义类型,如果要支持cin和cout标准输入输出,需要重载<<和>>
- 在线OJ常用:
常常要求多组测试用例:
//C语言中
while(scanf("%d\n",&a)!=EOF)
{
//数据处理
//ctrl+z退出
}
//C++中
while(cin>>str)
{
//数据处理
}
对于C++中的cin>>str,调用operator>>重载,转换为operator>>(cin,str),输入接收是用的对象作为while循环的判断条件,这显然是不合常理的,但是这里却是一个特殊的函数重载operator.bool( )
举个例子:
istream& operator>>(istream& in, Date& d)
{
in >> d._year >> d._month >> d._day;
return in;
}
这里返回in,其实就是返回cin(这是一个对象)
cin中又提供了对于类型运算符的重载operator bool,它会返回一个是否设置了错误标志(failbit或badbit)。
while(对象)
{}
转换成
while(对象.operator bool())
{}
如下例子:
class B
{
public:
operator bool()
{
return _a != 0;
}
int _a;
};
int main()
{
B b;
/*while (b)*/ // 等价于
while (b.operator bool())
{
cin >> b._a;
}
return 0;
}
3.2 C++中文件读写操作
要求:往配置文件中写入和读取信息
struct ServerInfo
{
char _ip[32];
int _port;
};
class ConfigManager
{
public:
ConfigManager(const char* filename)
:_filename(filename)
{}
void WriteBin(const ServerInfo & info)
{
// out到文件中
ofstream ofs(_filename.c_str(),ios_base::out|ios_base::binary);
ofs.write((const char*)&info,sizeof(ServerInfo));
}
void ReadBin(const ServerInfo& info)
{
ifstream ifs(_filename.c_str(), ios_base::in | ios_base::binary);
ifs.read(( char*)&info, sizeof(ServerInfo));
}
void WriteText(const ServerInfo& info)
{
ofstream ofs(_filename.c_str());
ofs << info._ip <<" "<< info._port ;
}void ReadText(ServerInfo& info)
{
ifstream ifs(_filename.c_str());
ifs >> info._ip >> info._port;
}
private:
string _filename;
};
二进制读写
// 写入
void TestBinWite()
{
ServerInfo info = { "126.0.0.1",90 };
ConfigManager cm("config.bin");
cm.WriteBin(info);
}
// 读取
void TestBinRead()
{
ServerInfo info ;
ConfigManager cm("config.bin");
cm.ReadBin(info);
cout << info._ip <<":"<< info._port << endl;
}
结果展示:
文本读写
// 文本写入
void TestTextWrite()
{
ServerInfo info = { "126.0.0.1",90 };
ConfigManager cm("config.txt");
cm.WriteText(info);
}
// 文本读取
void TestTextRead()
{
ServerInfo info;
ConfigManager cm("config.txt");
cm.ReadText(info);
cout << info._ip << ":" << info._port << endl;
}
结果展示:
四、stringstream的简单介绍
主要用途:将一些数据转换成一个字符串,或者将字符串转为数字
struct Personinfo {
string _name;
int _age;
};
void Teststringstream()
{
// 序列化
Personinfo p = {"张三",18};
ostringstream oss;
oss << p._name <<" "<< p._age;
string str = oss.str();
cout << str<<endl;
// 反序列化
istringstream iss(str);
string name;
int age;
iss >> name>>age;
cout << name << " " << age << endl;
}
要使用stringstream,必须要包含头文件<sstream>
。在该头文件下,有三个类:
- ostringstream 输出操作
- istringstream 输入操作
- stringstream 输入操作+输出操作