C++核心编程 day09 类型转换、异常、输入输出流

C++核心编程 day09 类型转换、异常、输入输出流

  • 1. 类型转换
  • 2. 异常
    • 2.1 异常语法
    • 2.2 C++标准异常库
  • 3. 输入输出流
    • 3.1 输入输出流概念以及流类库
    • 3.2 标准输入流
    • 3.3 标准输出流
    • 3.4 文件读写

1. 类型转换

C++中的类型转换有四类,分别是静态转换、动态转换、常量转换、重新解释转换。

静态转换使用static_cast<typename>进行转换。主要用于类层次中的父类和子类之间指针或引用的转换。向上类型转换的时候是安全的,但是向下类型转换的时候由于没有动态类型检查,所有是不安全的。静态转换也支持内置基本的数据类型之间的转换。

动态类型转换是使用dynamic_cast<typename>进行转换。动态类型转换用于类层次之间的向上类型转换和向下类型转换。向上类型转换的时候效果和静态类型转换的效果是一样的。在使用向下类型转换的时候,会比static_cast更加安全。动态类型转换不支持内置基本数据类型转换。

常量转换是用于修改类型的const属性,使用const_cast<typename>来进行转换。需要注意的是常量转换不能直接对非指针和非引用的变量去移除他们的const属性。

重新解释转换是使用reinterpret_cast<typename>来进行转换。这是C++中最不安全的一种转换机制,最有可能出问题。主要用于将一种数据类型转换为另一种数据类型,比如指针可以转换为一个整数,一个整数也可以转换为一个指针。

关于四种类型转换的示例代码如下所示。

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

//静态类型转换
void test01()
{
	//允许内置数据类型之间转换
	char a = 'a';
	double d = static_cast<double>(a);
	cout << d << endl;
}

class Base{ virtual void func(){} };
class Son : public Base { virtual void func(){} };
class Other {};

void test02()
{
	Base *base = NULL;
	Son *son = NULL;

	//语法:static_cast<目标数据类型>(原对象)
	//父子之间的指针或者引用可以转换
	//将base转为Son * 父转子 向下类型转换 不安全
	Son *son2 = static_cast<Son *>(base);

	//son转为Base * 子转父 向上类型转换 安全
	Base *base2 = static_cast<Base *>(son);

	//base转为Other *
	//Other *other = static_cast<Other *>(base); //转换无效
}

// 动态类型转换 dynamic_cast
void test03()
{
	//不允许内置数据类型之间转换
	//char c = 'c';
	//double d = dynamic_cast<double>(c);
}

void test04()
{
	Base *base = new Base;
	Son *son = NULL;

	//将base转为Son * 父转子 不安全 如果发生了多态,那么转换总是安全的
	Son *son2 = dynamic_cast<Son *>(base);

	//son转为Base * 子转父 安全
	Base *base2 = dynamic_cast<Base *>(son);

	//base转Other *
	//Other *other = dynamic_cast<Other *>(base); //无法转换
}


//常量转换 const_cast
void test05()
{
	//不可以将非指针或者费引用做const_cast转换
	const int *p = NULL;
	int *pp = const_cast<int *>(p);

	const int *ppp = const_cast<const int *>(pp);

	//const int a = 10;
	//int b = const_cast<int>(a);

	int num = 10;
	int &numRef = num;

	const int &num2 = const_cast<const int &>(numRef);
}

//重新解释转换 reinterpret_cast 最不安全一种转换,不建议使用
void test06()
{
	int a = 10;
	int *p = reinterpret_cast<int *>(a);

	Base *base = NULL;
	//base转Other *
	Other *other = reinterpret_cast<Other *>(base);
}

int main()
{
	test01();

	system("pause");
	return 0;
}

2. 异常

2.1 异常语法

异常处理就是处理程序中出现的错误,所谓的错误就是程序运行过程中发生的一些异常事件。比如除0移除、数组下标越界、空指针等错误。在C语言中我们对错误的处理主要是两个方法,第一种是使用整型的返回值标识错误,二是使用errno宏去记录错误。在C++中仍然可以使用这两种方法。

上述的两种处理方法虽然说可以处理错误,但是最大的缺陷就会出现不一致的错误。比如有些函数返回1表示成功,返回0表示错误;而有些函数返回0表示成功,返回非0表示错误。除此之外,还有一个缺陷就是函数的返回值只有一个,当你通过函数的返回值代表错误代码的时候,那么函数就不能返回其他的值。比如下面的函数你就不能分清该函数返回的-1究竟是程序出错还是程序的执行结果。也就是函数的返回值有多重语义。

int myDivision(int a, int b)
{
	if(b == 0)
	{
		return -1;
	}
	return a / b;
}

而我们使用C++的异常处理就不会出现这种情况。在C语言中处理异常的时候可能函数的调用者会忘记处理异常,而C++中如果你没有处理异常程序就会中断。异常的处理也可以在处理跳级,也就是有多个函数在调用栈中出现了某个错误,而C++中只需要在其中的某一处进行处理即可,不需要每一级的函数都进行处理。C++中如果有异常,我们使用throw操作创建一个异常对象并抛出。我们将可能抛出异常的程序段放在try代码块中,如果在try程序段执行期间没有引起异常,那么跟在try代码段后的catch字句就不会执行。catch子句会根据出现的先后顺序被检查,匹配的catch语句捕获并处理异常,或者也可以继续抛出异常。如果匹配的处理未找到,则会自动调用terminate函数,其缺省功能调用abort终止程序。对于处理不了的异常,也可以在最后一个catch分支使用throw继续上抛。下面给出一段关于异常的基本语法的示例代码。

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#include <string>

class MyException
{
public:
	void printError()
	{
		cout << "我自己的异常" << endl;
	}
};

class Person
{
public:
	Person()
	{
		cout << "Person的默认构造函数调用" << endl;
	}
	~Person()
	{
		cout << "Person的析构函数调用" << endl;
	}
};

int myDivision(int a, int b)
{
	if (b == 0)
	{
		//return -1;
		//throw 1; //抛出int类型异常
		//throw 'a'; //抛出char类型异常
		//throw 3.14; //抛出double类型异常
		//string str = "abc";
		//throw str;

		//从try代码开始,到throw抛出异常之前,所有栈上的数据都会被释放掉
		//释放的顺序和创建的顺序相反,这个过程我们称为栈解旋
		Person p1;
		Person p2;

		throw MyException(); //抛出MyException的匿名对象
	}
	return a / b;
}

void test01()
{
	int a = 10;
	int b = 0;

	//C语言处理异常有缺陷,返回值不统一,返回值只有一个,无法区分是结果还是异常
	//int ret = myDivision(a, b);
	//if (ret == -1)
	//{
	//	cout << "异常" << endl;
	//}

	try
	{
		myDivision(a, b);
	}
	catch (int)
	{
		cout << "int类型异常捕获" << endl;
	}
	catch (char)
	{
		cout << "char类型异常捕获" << endl;
	}
	catch (double)
	{
		//捕获到了异常,但是不想处理,继续向上抛出这个异常
		//异常必须有函数进行处理,如果没有任何处理,程序自动调用terminate函数,让程序中断
		throw;
		cout << "double类型异常捕获" << endl;
	}
	catch (MyException e)
	{
		e.printError();
	}
	catch (...)
	{
		cout << "其他类型异常捕获" << endl;
	}

}

int main()
{
	try
	{
		test01();
	}
	catch (double)
	{
		cout << "main函数中double类型异常捕获" << endl;
	}
	catch (...)
	{
		cout << "main函数中其它类型异常捕获" << endl;
	}

	system("pause");
	return 0;
}

C++中异常机制和函数机制并不互相干涉,但是捕获方式是通过严格的类型匹配。

异常被抛出后,从进入try代码块开始,到异常被抛掷之前,这期间在栈上构造的所有对象都会被自动析构。析构的顺序与构造的顺序相反,这一过程被称为栈的解旋。

为了加强程序的可读性,我们也可以在函数申明中列出可能抛出的异常的所有类型,例如void func() throw(A, B, C);这个函数func只能抛出类型为A、B、C以及其子类类型的异常。如果throw后的括号为空,则这个函数不能够抛出任何异常。如果函数的声明中没有包含结构声明,则函数可以抛出任意类型的异常。如果一个声明了接口的函数抛出了不允许抛出的异常,则会调用unexpected函数,该函数的默认行为是调用terminate函数中断程序。异常变量也是有声明周期的,声明周期和之前将的普通变量是类似的。异常变量也是有生命周期的,示例代码如下。

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#include <string>

class MyException
{
public:
	MyException()
	{
		cout << "MyException默认构造函数调用" << endl;
	}

	MyException(const MyException &e)
	{
		cout << "MyException拷贝构造函数调用" << endl;
	}

	~MyException()
	{
		cout << "MyException析构函数调用" << endl;
	}
};

void doWork()
{
	throw new MyException();
}

int main()
{
	try
	{
		doWork();
	}
	//抛出的是throw MyException(); catch(MyException e) 调用拷贝构造函数,效率低
	//抛出的是throw MyException(); catch(MyException &e) 只调用默认构造,效率高推荐
	//抛出的是 throw &MyException(); catch(MyException *e) 对象会提前释放掉,不能在非法操作
	//抛出的是 new MyException(); catch(MyException *e) 只调用默认构造函数 自己要管理释放
	catch (MyException *e)
	{
		cout << "自定义异常捕获" << endl;
		delete e;
	}

	system("pause");
	return 0;
}

异常也是有多态的,父类的引用或者指针指向子类的对象。使用的方法和前面类中的多态是一样的,在异常捕获的时候,我们常用这个,示例代码如下。

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

//异常的基类
class BaseException
{
public:
	virtual void printError() = 0;
};

//空指针异常
class NULLPointerException :public BaseException
{
public:
	virtual void printError()
	{
		cout << "空指针异常" << endl;
	}
};

//越界异常
class OutOfRangeException :public BaseException
{
public:
	virtual void printError()
	{
		cout << "越界异常" << endl;
	}
};

void doWork()
{
	//throw NULLPointerException();
	throw OutOfRangeException();
}

int main()
{
	try
	{
		doWork();
	}
	catch (BaseException &e)
	{
		e.printError();
	}
	system("pause");
	return 0;
}

2.2 C++标准异常库

除了前面的自己编写的异常之外,标准库中也为我们提供了很多的异常类,他们是通过类的继承组织起来的。

在这里插入图片描述
所有的异常都继承一个公共的基类exception。每个类都有提供的了构造函数、拷贝构造函数和赋值操作。其中在logic_error类与runtime_error类以及它们的子类都有一个构造函数接收一个string类型参数用于描述异常的信息。所有的异常都有一个what()函数,该函数是一个常函数,返回的是const char *类型的值用于描述异常的信息。所有我们要输出异常的信息可以通过打印异常类中的what()函数的返回值即可。下面给出一些标准异常类的描述:

异常名称描述
exception所有标准异常类的基类
bad_alloc在堆区开辟内存失败的时候,比如使用new或者new[]申请空间
bad_exception当函数的接口声明了抛出bad_exception异常,而函数中抛出了接口没有声明的异常,调用unexpected函数若抛出异常,则无论什么异常都会被自动替换被bad_exception类型异常
bad_cast使用动态类型转换引用失败的时候抛出的异常
ios_base::failureIO操作过程中出现错误
logic_error逻辑错误,也在运行前检测的错误
length_error试图生成一个操作该类型最大长度的对象抛出的异常
domain_error参数的值域错误,主要出现在数学函数中
out_of_range超出有效范围
invalid_argument参数不合适
runtime_error运行时错误,在运行时才能检测到的错误
range_error计算结果超出了有意义的值域范围
overflow_error算术计算上溢
underflow_error算术计算结果下溢

例如下面代码就是用了系统标准库提供的异常类。

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

class Person
{
public:
	Person(int age)
	{
		if (age < 0 || age > 150)
		{
			//throw out_of_range("年龄必须在 0 ~ 150 之间");
			throw length_error("年龄必须在 0 ~ 150 之间");
		}
		else
		{
			this->age = age;
		}
	}

	int age;
};

int main()
{
	try
	{
		Person p(151);
	}
	catch (out_of_range &e)
	{
		cout << e.what() << endl;
	}
	catch (length_error &e)
	{
		cout << e.what() << endl;
	}

	system("pause");
	return 0;
}

标准库中的异常类是有限的,有时候我们也需要自己的异常类。在自己写异常类的时候,最好自己写的类要继承标准库中的异常类,同时也应该重载父类中的what函数,示例代码如下。

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#include <string>

class MyOutOfRangeException :public exception
{
public:
	MyOutOfRangeException(const char *str)
	{
		//const char * 可以隐式类型转换为string, 反之不可以
		this->errorInfo = str;
	}

	MyOutOfRangeException(string str)
	{
		this->errorInfo = str;
	}

	virtual const char * what() const
	{
		//将string转为const char *
		return this->errorInfo.c_str();
	}

	string errorInfo;
};

class Person
{
public:
	Person(int age)
	{
		if (age < 0 || age > 150)
		{
			throw MyOutOfRangeException("年龄必须在0到150之间");
		}
		else
		{
			this->age = age;
		}
	}

	int age;
};

int main()
{
	try
	{
		Person p(1000);
	}
	catch (exception &e)
	{
		cout << e.what() << endl;
	}

	system("pause");
	return 0;
}

3. 输入输出流

3.1 输入输出流概念以及流类库

程序的输入是指从文件将数据输入给程序,而程序的输出是将数据从程序输出给文件。C++中的输入分为三类,分别是标准I/O、文件I/O、串I/O。标准I/O是对系统指定的标准设备的输入与输出,比如鼠标键盘显示器等。文件I/O是以外存磁盘文件为对象进行输入输出。串I/O是对内存中指定的空间进行输入和输出,通常以字符数组作为空间的形式。

C++中提供了用于标准输入输出的iostream类库,也提供了用于文件输入输出的fstream类库以及串输入输出的strstream类库。

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

我们来详细看一下其中的常见类名。

类名 |作用 | 头文件
ios | 抽象基类 | iostream
istream | 通用输入流和其它输入流的基类 | iostream
ostream | 通用输出流和其它输出流的基类 | iostream
iostream | 通用输入输出流和其它输入输出流的基类 | iostream
ifstream | 输入文件流类 | fstream
ofstream | 输出文件流类 | fstream
fstream | 输入输出文件流类 | fstream
istrstream | 输入字符串流类 | strstream
ostrstream | 输出字符串流类 | strstream
strstream | 输入输出字符串流类 | strstream

iostream类库中的不同的类声明放在不同的头文件中,用户在自己的程序中#include相关的头文件就相当于声明了所需要用到的类。常用的头文件有以下:

  • iostream:包含了对输入输出流进行操作的所需基本信息。
  • fstream:用于用户管理的文件的I/O操作。
  • strstream:用于字符串流I/O。
  • stdiostream:用于混合使用C和C++的I/O机制。
  • iomanip:在使用格式化I/O时应该包含此头文件。

iostream中有四种流对象,分别如下:

对象含义设备对应的类C语言中对应的标准文件
cin标准输入流键盘istream_withassignstdin
cout标准输出流屏幕ostream_withassignstdout
cerr标准错误流屏幕ostream_withassignstderr
clog标准错误流屏幕ostream_withassignstderr

cout是console output的缩写,意思是在控制台输出。cout不是关键字,而是ostream的一个对象,在iostream中有定义。在cout中重载了左移运算符用于输出。使用<<的时候用户可以不必考虑是什么类型,系统自动判断数据的类型并且根据其类型调用与之匹配的运算符重载函数。而在C语言中输出是非常麻烦的,因为需要记住很多格式字符。cout流在内存中对一个开辟了一个缓冲区,用于存放流中的数据,当cout流插入一个endl时,无论缓冲区是否满,都会立即输出流中的所有数据,然后插入一个换行符并清空缓冲区。

cerr流对象是标准错误流,cerr流被指定为与显示器相关联。作用是在标准错误设备上输出相关出错信息。cerr与标准输出流cout的作用和用法差不多,但是有一点不同。cout通常是传送到显示屏输出,也可以被重定向输出到磁盘文件,而cerr流中的信息只能在显示器上输出。

clog流对象也是标准错误流,是console log的缩写。它的作用和cerr的作用相同,都是在显示器终端上输出出错信息。但是不同的是cerr是不经过缓冲区直接向显示器输出有关信息,而clog中的信息放在缓冲区中,缓冲区满后或者遇到endl时向显示器输出。

3.2 标准输入流

标准输入流对象是cin,在里面有几个比较重点的函数。

  • cin.get() :一次从输入缓冲区内读取一个字符。
  • cin.get(n):一次从输入缓冲区内读取n个字符。
  • cin.get(buf, n):一次从输入缓冲区内读取长度为n个字符存入buf中。
  • cin.getline(buf, n):一次从输入缓冲区内读取一行的数据将最多n个存入buf中。
  • cin.ignore():忽略一个字符,如果括号有参数就是忽略n个字符。
  • cin.peek():偷窥输入缓冲区的第一个字符。
  • cin.putback(ch):将字符ch放回输入缓冲区。
  • cin.clear():清空缓冲区。
  • cin.sync():重置标志位,标志位为1表示异常,为0表示正常。
  • cin.fail():获取缓冲区标志位,返回的结果为0或者1

上述标准输入流对象的函数示例如下。

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

/*
cin.get() //一次只能读取一个字符
cin.get(一个参数) //读取一个字符
cin.get(两个参数) //可以读字符串
cin.getline()
cin.ignore()
cin.peek()
cin.putback()
*/

void test01()
{
	char c = cin.get();

	cout << "c = " << c << endl;

	c = cin.get();
	
	cout << "c = " << c << endl;

	c = cin.get();
	
	cout << "c = " << c << endl;

	c = cin.get();

	cout << "c = " << c << endl;
}

void test02()
{
	char buf[1024] = { 0 };
	cin.get(buf, 1024);

	char c = cin.get();
	//利用cin.get获取字符串的时候,换行符遗留在缓冲区中
	if (c == '\n')
	{
		cout << "换行符遗留在缓冲区" << endl;
	}
	else
	{
		cout << "换行符不在缓冲区" << endl;
	}
	cout << buf << endl;
}

void test03()
{
	char buf[1024] = { 0 };
	//利用cin.getline获取字符串时候,换行符不会被取走,也不再缓冲区中,而是直接扔掉
	cin.getline(buf, 1024);
	
	char c = cin.get();
	if (c == '\n')
	{
		cout << "换行符遗留在缓冲区" << endl;
	}
	else
	{
		cout << "换行符不在缓冲区" << endl;
	}
	cout << buf << endl;
}

//cin.ignore忽略 默认忽略一个字符,如果填入参数X,代表忽略X个字符
void test04()
{
	cin.ignore(2);
	char c = cin.get();
	cout << "c = " << c << endl;
}

//cin.peek 偷窥
void test05()
{
	char c = cin.peek();
	cout << "c = " << c << endl;
	
	c = cin.get();
	cout << "c = " << c << endl;

	c = cin.get();
	cout << "c = " << c << endl;
}

//cin.putback() 返回
void test06()
{
	char c = cin.get();
	cin.putback(c);

	char buf[1024] = { 0 };

	cin.getline(buf, 1024);
	cout << buf << endl;
}

//案例1. 判断用户输入的内容是字符串还是数字
void test07()
{
	cout << "请输入一个字符串或者数字" << endl;
	char c = cin.peek();

	if (c >= '0' && c <= '9')
	{
		int num;
		cin >> num;
		cout << "您输入的是数字 为: " << num << endl;
	}
	else
	{
		char buf[1024] = { 0 };
		cin >> buf;
		cout << "您输入的是字符串 : " << buf << endl;
	}
}

// 案例2 用户输入0~1之间的数字,如果输入有误重新输入
void test08()
{
	cout << "请输入0 ~ 10之间的数字" << endl;
	int num;
	while (true)
	{
		cin >> num;
		if (num >= 0 && num <= 10)
		{
			cout << "输入正确, 输入的值为: " << num << endl;
			break;
		}

		// 清空缓冲区,重置标志位
		cin.clear();
		//cin.sync();
		//cin.ignore(); //vs2013以上版本加入
		// 如果标志位为0,代表缓冲区正常 如果标志位为1,缓冲区异常
		cout << "cin.fail() = " << cin.fail() << endl;
		cout << "输入有误,请重新输入:" << endl;
	}
}


int main()
{
	//test01();
	//test02();
	//test03();
	//test04();
	//test05();
	//test06();
	//test07();
	test08();

	system("pause");
	return 0;
}

3.3 标准输出流

C++中的输出是通过标准输出流cout完成的。以下是关于标准输出流的函数。

  • cout.flush():刷新缓冲区。
  • cout.put(ch):向缓冲区中写入字符ch
  • cout.write(str, n):向缓冲区中输出str字符串的前n个字符。

在C语言中我们使用格式字符通过printf去控制输出的格式,在C++中我们通过流对象cout中用于控制输出格式的成员函数来控制输出格式。常见控制输出格式的成员函数如下:

流成员函数与之相同作用的控制符作用
precision(n)setprecision(n)设置实数的精度为n位
width(n)setw(n)设置字段宽度为n位
fill(c)setfill(c)设置填充字符c
setf()setiosflags()设置输出格式状态,括号中应给出格式状态,内容与控制符setiosflags括号中的内容相同
unsetf()resetiosflags()终止已设置的输出格式状态,在括号中应指定内容

流成员函数setf和控制符setiosflags库昊中的参数表示格式状态,它是通过格式标志来指定的。格式标志在类ios中被定义为枚举值,因此在引用这些的时候需要加上类名ios以及作用域运算符::,下面是格式标志。

格式标志作用
ios::left输出数据在本域宽范围内向左对齐
ios::right输出数据在本域宽范围内向右对齐
ios::internal数值的符号为应在域宽内左对齐,数值右对齐,中间由填充字符填充
ios::dec设置整数的基数为10
ios::oct设置整数的基数为8
ios::hex设置整数的基数为16
ios::showbase强制输出整数的基数(八进制数以0开头,十六进制数以0x开头)
ios::showpoint强制输出浮点数的小数和尾数0
ios::uppercase在以科学计数法格式E和以十六进制输出字母时以大写表示
ios::showpos对正数显示+号
ios::scientific浮点数以科学计数法格式输出
ios::fixed浮点数以定点格式(小数形式)输出
ios::unitbuf每次输出之后刷新所有的流
ios::stdio每次输出之后清除stdoutstderr

关于控制符如下:

控制符作用
dec设置数值的基数为10
hex设置数值的基数为16
oct设置数值的基数为8
setfill(c)设置填充字符c,c可以是字符常量或者字符变量
setprecision(n)设置浮点数的精度为n位。在以十进制小数形式输出时,n代表有效数字。在以fixed(固定小数位数)形式和scientific(指数)形式输出时,n为小数位数
setw(n)设置字段宽度为n位
setiosflags(ios::fixed)设置浮点数以固定的小数位显示
setiosflags(ios::scientific)设置浮点数以科学计数法(即指数形式) 显示
setiosflags(ios::left)输出数据左对齐
setiosflags(ios::right)输出数据右对齐
setiosflags(ios::skipws)忽略前导的空格
setiosflags(ios::uppercase)数据以十六进制形式输出时字母以大写表示
setiosflags(ios::lowercase)数据以十六进制形式输出时字母以小写表示
setiosflags(ios::showpos)输出正数时给出+号

需要注意的是如果使用了控制符,在程序开头还需要添加iomanip.h头文件。成员函数width(n)和控制符setw(n)只对其后面的第一个输出项有效。 在上面设置数值基数的只能选择其中一种来使用,它们是相互排斥的。对于成员函数setf和控制符setiosflags设置的输出格式状态,如果想要改变使用另一个状态,应该调用成员函数unsetf或者控制符resetiosflags终止原来设置的状态。在使用setf函数设置格式状态时,可以包含两个甚至多个状态,这些格式标志在ios类中被定义为枚举值,每一个格式标志都是以一个二进制位表示的,因此可以使用位运算符|组合多个格式标志。

关于输出流对象的示例代码如下。

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#include <iomanip> // 控制符格式化输出 头文件

/*
cout.put() //想缓冲区写入字符
cout.write() //从buffer中写num个字节到当前输出流中
*/
void test01()
{
	//cout.put('h').put('e');

	char buf[] = "hello world";
	cout.write(buf, strlen(buf));

	cout << "hello world" << endl;
}

// 1.通过流成员函数格式化输出
void test02()
{
	int number = 99;
	cout.width(20); // 指定宽度为20
	cout.fill('*'); // 填充
	cout.setf(ios::left); // 左对齐
	cout.unsetf(ios::dec); // 卸载十进制
	cout.setf(ios::hex); // 安装十六进制
	cout.setf(ios::showbase); // 显示基数
	cout << number << endl;
}

// 2. 使用控制符格式化输出
void test03()
{
	int number = 99;
	cout << setw(20)	// 设置宽度
		<< setfill('~') // 设置填充
		<< hex // 显示16进制
		<< setiosflags(ios::showbase) // 显示基数
		<< setiosflags(ios::left) // 左对齐
		<< number << endl;
}

int main()
{
	//test01();
	//test02();
	test03();

	system("pause");
	return 0;
}

3.4 文件读写

文件读写主要在fstream.h头文件中被定义,这里面定义了三个类,分别是ifstreamofstreamfstream,它们之间的继承关系如下。

在这里插入图片描述
进行文件读写的第一步就是要先打开文件,打开文件有两种方法。

第一种是调用文件流对象的open成员函数,该函数的第一个参数文件路径,第二个参数是打开的方式。第二种方式使用文件流定义的参数构造函数,如下。

// 第一种方法
ofstream ofs;
ofs.open("./demo1.txt", ios::out);

// 第二种方法
ofstream ofs("./demo1.txt", ios::out);

文件输入输出的方式设置值如下。

方式作用
ios::in以输入方式打开文件
ios::out以输出方式打开文件(这是默认方式),如果已经有此名字的文件,则将其原有内容直接全部清空
ios::app以输出方式打开文件,写入的数据添加在文件末尾
ios::ate打开一个已有的文件,文件指针指向文件末尾
ios::trunc打开一个文件,如果文件已存在,则删除其中全部数据,若文件不存在,则建立新文件。如已经使用ios::out方式打开,而未指定ios::appios::ateios::in,则同时默认此方式
ios::binary以二进制方式打开一个文件,如不指定此方式默认为ASCII方式
ios::nocreate打开一个已有的文件,如文件不存在,则打开失败。
ios::noreplace如果文件不存在则简历新文件,如果文件已存在则操作失败
ios::in | ios::out以输入和输出方式打开文件,文件可读可写
ios::out | ios::binary以二进制方式打开一个输出文件
ios::in | ios::binary以二进制方式打开一个输入文件

上面的输入输出方式设置可以使用位运算符中的或|进行组合。如果打开操作失败,open函数的返回值为假,如果是调用构造函数的方式打开文件,则流对象的值为0。

对文件读写操作完成后,应该关闭相关的文件。关闭用成员函数close完成。关闭就是解除改文件与文件流的关联,同样原来设置的工作方式也会失效,这样就不能再通过文件流对该文件进行输入和输出。

C++中对ASCII文件读写操作也可以使用左移或者右移运算符。<<表示刘插入运算符,而>>表示流提取运算符。这两用法和cin以及cout是一样的。在文件流中,常使用putgetgetline等成员函数进行字符的输入输出。put()输出单个字符,get()读取一个字符,getline()读取一行字符。

关于文件读写的示例代码如下。

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#include <fstream>
#include <string>

void test01()
{
	// 写文件 输出
	ofstream ofs("./test.txt", ios::out | ios::trunc);

	//ofs.open("./test.txt", ios::out | ios::trunc); 设置打开方式 以及路径

	if (!ofs.is_open())
	{
		cout << "文件打开失败" << endl;
		return;
	}

	ofs << "姓名:孙悟空" << endl;
	ofs << "年龄:999" << endl;
	ofs << "性别:女" << endl;

	// 关闭文件
	ofs.close();
}

void test02()
{
	// 读文件
	ifstream ifs;
	ifs.open("./test.txt", ios::in);

	if (!ifs.is_open())
	{
		cout << "文件打开失败" << endl;
		return;
	}

	// 第一种方式
	//char buf[1024] = { 0 };

	//while (ifs >> buf)
	//{
	//	cout << buf << endl;
	//}
		
	// 第二种方式
	//char buf[1024] = { 0 };
	//while (ifs.getline(buf, 1024))
	//{
	//	cout << buf << endl;
	//}
	
	// 第三种方式
	//string buf;
	//while (getline(ifs, buf))
	//{
	//	cout << buf << endl;
	//}

	// 第四种方式
	char c;
	while ((c = ifs.get()) != EOF)
	{
		cout << c;
	}
}

int main()
{
	test01();
	test02();

	system("pause");
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/155198.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

STM32 ADC介绍和应用

目录 1.ADC是什么&#xff1f; 2.ADC的性能指标 3.ADC特性 4.ADC通道 5.ADC转换顺序 6.ADC触发方式 7.ADC转化时间 8.ADC转化模式 扫描模式 单次转换/连续转换 9.ADC实验 使用ADC读取烟雾传感器的值 代码实现思路&#xff1a; 核心代码示例&#xff1a; 1.ADC是什…

算法---相等行列对

题目 给你一个下标从 0 开始、大小为 n x n 的整数矩阵 grid &#xff0c;返回满足 Ri 行和 Cj 列相等的行列对 (Ri, Cj) 的数目。 如果行和列以相同的顺序包含相同的元素&#xff08;即相等的数组&#xff09;&#xff0c;则认为二者是相等的。 示例 1&#xff1a; 输入&…

Windows10电脑没有微软商店的解决方法

在Windows10电脑中用户可以打开微软商店&#xff0c;下载自己需要的应用程序。但是&#xff0c;有用户反映自己Windows10电脑上没有微软商店&#xff0c;但是不清楚具体的解决方法&#xff0c;接下来小编给大家详细介绍关于解决Windows10电脑内微软商店不见了的方法&#xff0c…

01Urllib

1.什么是互联网爬虫&#xff1f; 如果我们把互联网比作一张大的蜘蛛网&#xff0c;那一台计算机上的数据便是蜘蛛网上的一个猎物&#xff0c;而爬虫程序就是一只小蜘蛛&#xff0c;沿着蜘蛛网抓取自己想要的数据 解释1&#xff1a;通过一个程序&#xff0c;根据Url(http://www.…

linux清理僵尸进程

当你top看到这个&#xff0c;或者按M后看到内存吃的很多&#xff0c;那你看下有没有&#x1f9df; 二选一查看是什么进程 ps aux | egrep "Z|defunct" ps -aux | awk {if($8"Z"){print $2,$11}}没用直接杀杀杀 kill -9 查到的PID号可中断下载文件 wget…

【Linux网络】工作环境救急——关于yum安装的5个花式操作

目录 1、只下载不安装&#xff0c;离线安装软件 2、自行打包创建元数据 第一步&#xff1a;先准备好nginx的软件包&#xff0c;放在一个文件夹下 第二步&#xff1a;在本地下载createrepo命令软件&#xff0c;用于创建元信息&#xff0c;这个一定是对包的上一级目录使用命令…

【整顿C盘】pycharm、chrome等软件,缓存移动

C盘爆了&#xff0c;特来找一下巨大的软件缓存&#xff0c;特此记录&#xff0c;跟随的各大教程&#xff0c;和自己的体会 一、爆炸家族JetBrains 这个适用于pycharm、idea、webstorm等等&#xff0c;只要是JetBrains家的&#xff0c;2020版本以上&#xff0c;都是一样的方法 p…

python内置模块subprocess 模块,创建和管理子进程

一、简介 subprocess 是 Python 标准库中的一个模块&#xff0c;用于创建和管理子进程。它提供了一种在 Python 程序中启动新进程、连接到它们的输入/输出/错误管道以及获取它们的返回值的方法。 使用 subprocess 模块&#xff0c;你可以在 Python 程序中执行外部命令、调用其…

Linux_/proc目录_查看处理器的信息/proc/cpuinfo

1、cat /proc/cpuinfo_查看/proc/cpuinfo文件的内容 可以看到板卡有4个处理器&#xff0c;剩下的信息emmm...... 2、BogoMIPS_反映CPU运算速率 MIPS是millions of instructions per second(百万条指令每秒)的缩写&#xff0c;其代表CPU的运算速率。 BogoMIPS是Linux大致计算…

Python开源项目GPEN——人脸重建(Face Restoration),模糊清晰、划痕修复及黑白上色的实践

无论是自己、家人或是朋友、客户的照片&#xff0c;免不了有些是黑白的、被污损的、模糊的&#xff0c;总想着修复一下。作为一个程序员 或者 程序员的家属&#xff0c;当然都有责任满足他们的需求、实现他们的想法。除了这个&#xff0c;学习了本文的成果&#xff0c;或许你还…

springcloud失物招领网站源码

开发技术&#xff1a; jdk1.8&#xff0c;mysql5.7&#xff0c;idea&#xff0c;nodejs&#xff0c;vscode springcloud springboot mybatis vue elementui 功能介绍&#xff1a; 用户端&#xff1a; 登录注册 首页显示搜索失物&#xff0c;轮播图&#xff0c;最新发布的…

OpenCV快速入门:基本操作

文章目录 1. 像素操作1.1 像素统计1.2 两个图像之间的操作1.2.1 图像加法操作1.2.3 图像加权混合 1.3 二值化1.4 LUT&#xff08;查找表&#xff09;1.4.1 查找表原理1.4.2 代码演示 2 图像变换2.1 旋转操作2.1.1 旋转的基本原理2.1.2 代码实现 2.2 缩放操作2.3 平移操作2.3.1 …

基于SSM的宠物综合服务平台的设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…

Topaz Video AI:引领视频质量革命,让您的内容焕发新生

随着数字媒体的日益普及&#xff0c;视频质量的重要性日益凸显。无论是个人用户还是专业团队&#xff0c;都需要确保他们的视频内容具有最佳的质量。但是&#xff0c;由于各种原因&#xff0c;如设备限制、环境干扰等&#xff0c;往往导致视频质量不尽如人意。这时&#xff0c;…

【ISP图像处理】Demosaic去马赛克概念介绍以及相关方法整理

1. 基本定义 使用彩色滤光器阵列(CFA)的数码相机需要一个去马赛克程序来形成完整的RGB图像。一般的相机传感器都是采用彩色滤光片阵列(CFA)放置在光感测单元上&#xff0c;在每个像素处仅捕获三种原色成分中的一种。 去马赛克方法主要关注于复原非常规区域&#xff0c;比如边缘…

mybatis之主键返回

1.在mybatis的xml中加入 <insert id"insertUser" keyProperty"id" useGeneratedKeys"true" parameterType"com.UserAndOrder"> insert into Tuser(userName,passWord) values (#{userName},#{passWord} ) </insert&…

内存管理中的一级指针和二级指针

目录 写在前面指针和内存管理正常使用类型转换中的使用分割内存块建立链接 新知识收获(void *)0和null(void*&#xff09;和 * 写在前面 关于指针的内容&#xff0c;在本科的时候有学过&#xff0c;但是仅限于学过&#xff0c;使用起来那是跟没学过一样&#xff0c;最近读操作…

HashMap

JDK1.7: ArrayList 内部是数组结构 LinkeList内部是链表结构 TreeMap 是二叉树结构 HashMap 是数组链表 读取(put)慢 会读取所有的key 确定有没有重复,通过hash优化 Hash碰撞时两个不同的Key 取模时 得到了一样的key 通过链表进行存放 初始大小 1 << 4 ,16 ------和满载…

text/xml和application/xml

困惑 在http消息中&#xff0c;同样是传送xml信息&#xff0c;有的时候看到Content-Type的值是text/xml&#xff0c;有的时候值是application/xml&#xff0c;感到困惑。 例如&#xff0c;用Postman发送http消息给Tomcat中的基于JAX-WS的 web服务&#xff1a; 请求中传送了xm…

YOLOv8改进 | ICLR 2022 |ODConv附修改后的C2f、Bottleneck模块代码

论文地址&#xff1a;论文地址点击即可跳转阅读 代码地址&#xff1a;文末提供复制粘贴的代码块 一、本文介绍 这篇文章给大家带来的是发表于2022年ICLR的ODConv(Omni-Dimensional Dynamic Convolution)中文名字全维度动态卷积&#xff0c;该卷积可以即插即用&#xff0c;可…