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

news2024/11/28 22:44:33

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;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Meta降本增效大招之:弃用产品

今晚无意间进入去哪儿技术沙龙的直播间&#xff0c;听到他们要删除50%的代码和停掉50%的服务。我就想起Meta公司最近写的这篇博客&#xff1a;Automating product deprecation。 这篇博客对于效能平台的建设非常具有指导意义。文章最后有原文链接和我个人的总结。 这是一个系列…

51单片机的基础知识汇总

文章目录 51单片机之点灯51单片机之灯的亮灭交替51单片机之灯的流水灯51单片机之数码管数字显示51单片机之数码管数字的流水灯51单片机之数码管数字的0-7显示51单片机之蜂鸣器的鸣叫与关闭51单片机之DS1302时钟设置51单片机之读出温度传感器温度代码模块化汇总主函数main.c 延时…

XD6500S— LoRa SIP模块

XD6500S是一系列LoRa SIP模块&#xff0c;集成了射频前端和LoRa射频收发器SX1262系列&#xff0c;支持LoRa和FSK调制。收发器SX1262系列&#xff0c;支持LoRa和FSK调制。LoRa技术是一种扩频协议&#xff0c;针对LPWAN 应用的低数据速率、超远距离和超低功耗通信进行了优化。通信…

【Mysql】Mysql内置函数介绍

&#x1f308;欢迎来到Python专栏 &#x1f64b;&#x1f3fe;‍♀️作者介绍&#xff1a;前PLA队员 目前是一名普通本科大三的软件工程专业学生 &#x1f30f;IP坐标&#xff1a;湖北武汉 &#x1f349; 目前技术栈&#xff1a;C/C、Linux系统编程、计算机网络、数据结构、Mys…

Jupyter Notebook的下载安装与使用教程_Python数据分析与可视化

Jupyter Notebook的下载安装与使用 Jupyter简介下载与安装启动与创建NotebookJupyter基本操作 在计算机编程领域&#xff0c;有一个很强大的工具叫做Jupyter。它不仅是一个集成的开发环境&#xff0c;还是一个交互式文档平台。对于初学者来说&#xff0c;Jupyter提供了友好的界…

阿里云ESSD云盘、高效云盘和SSD云盘介绍和IOPS性能参数表

阿里云服务器系统盘或数据盘支持多种云盘类型&#xff0c;如高效云盘、ESSD Entry云盘、SSD云盘、ESSD云盘、ESSD PL-X云盘及ESSD AutoPL云盘等&#xff0c;阿里云服务器网aliyunfuwuqi.com详细介绍不同云盘说明及单盘容量、最大/最小IOPS、最大/最小吞吐量、单路随机写平均时延…

LayoutLMv3 : 基于统一文本和带Masking图像的文档AI预训练【论文翻译】

文章目录 专业名词统计文档智能多模态预训练模型LayoutLMv3&#xff1a;兼具通用性与优越性LayoutLMv3 &#xff1a; 基于统一文本和带Masking图像的文档AI预训练ABSTRACT1 INTRODUCTION2 LAYOUTLMV32.1 Model Architecture&#xff08;模型架构&#xff09;2.2 Pre-training O…

向量以及矩阵

0.前言 好了那我们新的征程也即将开始&#xff0c;那么在此呢我也先啰嗦两句&#xff0c;本篇文章介绍数学基础的部分&#xff0c;因为个人精力有限我不可能没一字一句都讲得非常清楚明白&#xff0c;像矩阵乘法之类的一些基础知识我都是默认你会了&#xff08;还不会的同学推…

QTableWidget 设置列宽行高大小的几种方式及其他常用属性设置

目录 效果&#xff1a; 1.列宽、行高自动分配 2.固定值 3.随内容分配列宽 随内容分配行高 4.水平方向标签拓展剩下的窗口部分&#xff0c;填满表格 5.列宽是自动分配的&#xff0c;但是第一列可手动调整宽度&#xff0c;而表格整体的列宽仍是自动分配的。第二、三列办法调…

休闲娱乐 - 挂耳咖啡

公司有一个小的茶歇间&#xff0c;平时去喝个咖啡、放松身心、锻炼下身体。咖啡机是现磨咖啡豆的&#xff0c;喝喝就习惯了。 而我旁边一位同事习惯每天早上来自己泡一杯挂耳咖啡&#xff0c;再配上牛奶&#xff0c;感觉挺高级的。 关于挂耳咖啡就查了一下资料&#xff0c;介绍…

UE基础篇六:音频

导语: 通过实现一个小游戏,来学会音频,最后效果 入门 下载启动项目并解压缩。通过导航到项目文件夹并打开SkywardMuffin.uproject来打开项目。 按播放开始游戏。游戏的目标是在不坠落的情况下触摸尽可能多的云。单击鼠标左键跳到第一朵云。 游戏很放松,不是吗?为了强调…

【C++面向对象】15. 模板

文章目录 【 1. 函数模板 】【 2. 类模板 】 模板是泛型编程的基础&#xff0c;泛型编程即以一种独立于任何特定类型的方式编写代码。模板是指创建泛型类或函数的蓝图或公式。库容器&#xff0c;比如迭代器和算法&#xff0c;都是泛型编程的例子&#xff0c;它们都使用了模板的…

php接口api数据签名及验签

api数据签名作用&#xff1a;通过使用签名可以验证数据在传输过程中是否被篡改或修改。接收方可以使用相同的签名算法和密钥对接收到的数据进行验证&#xff0c;如果验证失败则表明数据被篡改过 1、数据发送方进行接口签名并传输签名字段 <?php // 请求URL $url "h…

利用回溯绕过正则表达式

目录 利用strpos的特性拿到flag 利用回溯绕过正则表达式 利用回溯次数绕过正则表达式并且实现文件上传 使用回溯绕过正则表达式waf拿到flag 本篇会讲解三个实验来分别绕过正则表达式&#xff0c;python的正则表达式和Javascript的正则表达式大致相同如果有正则表达式不太懂…

String字符串性能优化的几种方案

原创/朱季谦 String字符串是系统里最常用的类型之一&#xff0c;在系统中占据了很大的内存&#xff0c;因此&#xff0c;高效地使用字符串&#xff0c;对系统的性能有较好的提升。 针对字符串的优化&#xff0c;我在工作与学习过程总结了以下三种方案作分享&#xff1a; 一.优…

MySql 数据库初始化,创建用户,创建数据库,授权

登录MySQL&#xff08;使用管理员账户&#xff09; mysql -u root -p 设置用户 -- 创建用户并设置密码 CREATE USER user_name% IDENTIFIED BY user_password;-- 删除用户 drop user user_name; 设置数据库 -- 创建数据库 CREATE DATABASE database_name;-- 删除数据库 DR…

web前端开发网页设计课堂作业/html练习《课程表》

目标图&#xff1a; 代码解析&#xff1a; 代码解析1<table border"3" align"center"><输入内容(的) 边界"3px" 位置"居中">2<tr><td colspan"7" align"center">课程表</td><t…

YOLOv8改进 | 如何在网络结构中添加注意力机制、C2f、卷积、Neck、检测头

一、本文介绍 本篇文章的内容是在大家得到一个改进版本的C2f一个新的注意力机制、或者一个新的卷积模块、或者是检测头的时候如何替换我们YOLOv8模型中的原有的模块&#xff0c;从而用你的模块去进行训练模型或者检测。因为最近开了一个专栏里面涉及到挺多改进的地方&#xff…

Python | 机器学习之PCA降维

​&#x1f308;个人主页&#xff1a;Sarapines Programmer&#x1f525; 系列专栏&#xff1a;《人工智能奇遇记》&#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 目录结构 1. 机器学习之PCA降维概念 1.1 机器学习 1.2 PCA降维 2. PCA降维 2.1 实验目的 2…

Linux(多用户下)查看cuda、cudnn版本、查看已经安装的cuda版本相关命令

查看已经安装的CUDA多个版本 linux 中cuda默认安装在/usr/local目录中&#xff1a; -可以使用命令&#xff1a; ls -l /usr/local | grep cuda查看该目录下有哪些cuda版本&#xff1a; 如果输出&#xff1a; lrwxrwxrwx 1 root root 21 Dec 17 2021 cuda -> /usr/loc…