输入、输出、文件

C++在头文件iostream和fstream定义了一系列的类,用于实现I/O。iostream和fstream是标准类库的一部分。
一个C++程序如何实现输入和输出?输入流和输出流!不关注源和目标的来源,只在意如何从输入流中抽取信息给程序,如何向输出流插入字节,输入流和输出流都是字节流。所以才说输入的来源不重要,使用的仅是输入流。但对于输入流来说,需要绑定输入的来源(设备还是文件),需要指明流向哪里(什么类型的变量)。但是仅靠流,可能存在速度不匹配的问题,比如说,程序每次只抽取一个字节处理,若是源类型为文件,那么要多次对文件进行操作,一次还只是读取一个字节。效率不是一般的低。对于磁盘的操作,通常会为其配个缓冲区,一次从文件中读取多个字节直到填满缓冲区,这时程序才来抽取字节。对于输入是键盘的设备来说,默认敲入回车才会使得程序前来抽取信息。也是有好处的:存在缓冲区中可以再修改数据,反正还没发给程序。输出到显示器也是一样的,先将输出的字节存入到缓冲区内,直到遇到输出的字符是回车,才使得输出设备逐字节的接收字节并显示。
在这里插入图片描述
具体来说,iostream文件中究竟有哪些类呢?他们又是如何管理缓冲、管理流的呢?
在这里插入图片描述比如,当创建ostream的对象cout时,程序便打开了输出流,并为其分配了缓冲区,我们也可以调用ostream类中的方法。
包含了iostream头文件将拥有8个流对象(4个窄字符流、4个宽字符流):
cout对象:标准输出流,选定输出设备为显示器
cin对象:标准输入流,选定输入设备为键盘
cerr对象:标准错误流,不带缓冲区,选定输出设备为显示器
clog对象:标准错误流,带缓冲区,选定输出设备为显示器
就拿cout对象来说吧,是ostream的对象,包含了许多数据成员(部分继承基类):字符宽度、小数位数、整数的计数方式、指向缓冲区streambuf的指针
cout<<“c++”; 想想内部如何实现的?ostream类重载了<<运算符,利用指向字符串的指针将该字符串存入到cout对象的所关联的缓冲区,直到换行符才会刷新缓冲区,将字节流输出到显示器上。
cout默认的输出设备是显示器,但操作系统为我们提供了重定向运算符<和>,使得cout关联的输出设备发生变化。

流是字节流,现在考虑输出的是一个double型的数据,在内部存储中至少需要6个字节,这就有问题,难道为了表示这个double型数据,我真的要使用这6个字节吗?那么对于char类型的数据,只需一个字节,那么我们就得找办法确定不同类型对应的具体字节数,好麻烦。。换种思路呢,不使用数据的内部表示,将数值字符串由多个单个字符构成。由于输入时会以空白符隔开,因此可通过空白符来确定数据界限。由此,字节流变成了字符流。为了实现字符流,类ostream、istream也是提供了不少的方法,比如<<、>>、get()、put()、write()等。

使用cout进行输出:

1、插入运算符<<重载方法:程序中的数据以字符形式插入到输出流中(实际是存入到输出流对应的缓冲区)
原型:int可替换为下述基本类型或是指针,指针又分为指向C风格字符串的指针(插入的是字符)和void *指针(插入的是地址值)
其实这里有个小问题,只有当缓冲区满了才将数据打印到显示器上。可是我们等不及啊,那样的话,对于一个需要进行交互的程序来说,还要怎么继续下去???cout实现了当换行符被发送到缓冲区后,立刻刷新缓冲区!一些编译器实现了在进行输入之前,会刷新缓冲区!此外,还提供给我两个控制符,flush和endl(插入一个换行符),使得缓冲区被立刻刷新!这两个控制符已经被<<运算符所重载了!
在这里插入图片描述
从原型可以看出函数返回类型是ostream类型的引用,因此支持拼接使用,因为所使用的是同一个缓冲区。将数据存在同一个缓冲区内。
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
2、ostream其他方法
put():显示字符,由于在比较老的编译器里,字符常量是由整形表示的,故而使用cout<<‘A’ 无法显示字符,而是其ASCII。因而设计了这个方法实现显示字符。但现在的编译已经不存在这个问题了,故而使用<<或是put方法都可以。

在这里插入图片描述

write():显示C风格字符串,与<<运算符重载方法不同,该方法遇到空白符不会停止打印而是根据第二个参数确定何时停止打印,但operator<<(char *)是根据空白符确定停止打印。
在这里插入图片描述
这里有点疑问???<< 是如何实现C风格字符串输出的?下面这个例子输出和我想不一样。。。

int main()
{
	using std::cout;
	const char *s1 = "C+++ ";
	const char *s2 = "i love you";
	const char *s3 = "come on !";
	std::cout << s1 << s2 << s3 << std::endl;
	for (int i = 1; i <= strlen(s2); i++)
		cout.write(s2, i)<<std::endl;
	for (int i = strlen(s2); i >0; i--)
		cout.write(s2, i)<<std::endl;
	cout.write(s2, strlen(s2) + 6);


	std::cin.get();
    return 0;
}


放一个有意思的测试图,我是觉得很美(感受一波内置函数的强大)
在这里插入图片描述
打印时的设置:
ios_base 是ios的基类,ios是ostream的基类,在ios_base类设定了数据输出的格式:字段宽度、计数系统、。
在输出时,针对不同的数据类型,默认有不同的输出格式,但无论什么类型,字段宽度等于数据的字符长度:
在这里插入图片描述(1)修改计数系统
<<运算符重载了hex 、oct控制符,使得改变整形数据的输出格式。除非再次改变计数系统,否则将一直有效。
(2)调整字段宽度,只影响一次的输出格式便立即恢复默认值,数据值默认采取右对齐,默认以空格填充。也可以调用成员函数fill,设定填充字符,这个设定将一直有效,除非更改。
在这里插入图片描述在这里插入图片描述(3)设置浮点数的显示的精度
默认是6位,如果没显示的指出采取定点表示法或是科学计数法表示浮点数,精度表示的是总位数,否则表示的是小数点后面的位数。一旦设置了,持续有效。
在这里插入图片描述
(4)setf()改变ios_base的标记位(显示小数点、显示计数系统、以大写表示十六进制、是否添加正号、对齐方式、计数系统、浮点数表示方法)。ios_base类中有些标记位,用于控制数据输出的格式,而setf()方法和ios_base中的常量,为我们提供了可以改变这些标记位的方法。
有两种原型,这主要是有的标记位可能有多个值,比如计数方式,当设置一个有效的时候需要确保其余位的设置是无效的。对于第二个函数原型,第二个参数会我们清除所有位,并将第一个参数所在位设置为有效。且返回先前的标记符,用以恢复。所以一旦设置了,就不会自动恢复,需要人工干预。
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述对于浮点数表示,若是希望使用默认的表示,可使用以下两种之一:
在这里插入图片描述
设置了标记位,希望复位可采用成员函数unsetf():
在这里插入图片描述
(5)控制符
C++提供了控制符,我们只需使用控制符,他会帮助我们调用相应的setf(),更简单好么!控制符还有一个好处!可以连接在插入运算符后。
在这里插入图片描述在这里插入图片描述除了上面无参的控制符,还为我们提供了带参数的控制符:setw()、setprecision()、setfill()。连在cout语句中,使得输出的控制更为方便!

使用cin进行输入:
一、格式化输入(转化数据类型+跳过空白字符)
如何给程序提供数据?
标准输入流连接的设备是键盘,输入流是字符流,通过istream类的重载运算符>>先将输入字符流存入到缓存中,根据operator>>()函数的参数,将字符流转化为合适的数据类型存入到内存。如何分辨界线呢?同种类型,从非空白字符开始读取,跳过空白字符,直到遇到第一个非空白字符终止,且流指针指向该非空白字符,再将字符变为合适的数据类型。对于出现不同类型的字符流来说,即便没有遇见空白字符,但发现了字符与输入数据的类型不符时,也将停止读取。
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
流状态成员:
在ios_base类有三个iostate类型元素组成:eofbit、failbit、badbit,并提供了访问、设置这些状态的方法:

在这里插入图片描述设置failbit位的几种情况:输入流中出现不匹配目标的数据类型;打开文件失败。特别地,有获取eofbit的方法,但没有failbit方法,因此为了更加明确输入停止的原因,可以使用fail()和!eof()来判定是failbit被设置了。
(1)修改流状态:clear()和setstate()
clear()不但修改指定的参数标记位,而且对其余两个标记进行复位处理。
setstate()只修改参数指定标记位。
(2)使得特定的标记位引发异常
出现了文件末尾、读入与预期类型不同的字符、无法打开文件、不知道的错误时,若是希望能过捕获这些异常,我们可以怎么做?调用istream类expection()方法,参数指明发生异常的类型。当流读取时发生这些异常,会调用clear()设置标记位,此时clear()方法内实现了与expection()返回值的比较,若是相同则引发ios::failure异常。ios::faliure异常类从expection异常类所派生得到。
引发异常语句的设置:
在这里插入图片描述`#include
#include
using namespace std;
int main()
{

cin.exceptions(ios_base::failbit);
int n;
int sum = 0;
try {
	while (cin >> n)
		//cout << n << endl;
		sum += n;
}
catch (ios_base::failure) {
	cout << "type dismatch!";
	
}
//cin.get();
cout << sum << endl;
cin.get();
return 0;

}
`
(3)流状态的影响
while(cin>>n);只有当三种流状态都是好的时候才会返回true。
一旦一种状态被修改了,则流会被关闭,无法在读取或是输出。为了正常读取和输出,需要手动调用clear(),恢复默认流状态。此外,当发生不匹配的数据类型时所导致的停止,该不匹配数据依旧在流中,需要读出该不匹配数据。

int main()
{
	int n;
	int sum=0;
	int test;
	while (cin >> n)
	{
		sum += n;
	}
	if (cin.fail() && !cin.eof())
	{
		cout << "type dismatch!"<<endl;
		cin.clear();
		while (cin.get() != '\n')
			continue;
		
	}
	else
		cout << "end of the file!";
	cin >> test;
	cout << sum + test << endl;
	}

二、非格式化输入函数(只读取字符类型,因而不发生类型转化!不跳过空白字符!)
单字符输入方法两种形式:
与标准格式的输入相比,可以保留空格!!!当需要空格信息使用get(),否则抽取运算符不然还得处理空格。
istream &get(char &)
int get(void)
区别在于第一个方法将输入流的字符输入到引用参数,第二个方法将字符转化为int型并返回给程序。读取空格的,何时暂停从流中读取字符,看个人需要来写while,一个个字符也没有分界符概念(标准输入,非空白字符开始读取结束于空白字符)。第二个区别,当读取到文件末尾时,第一个方法,while(cin.get(cr))返回false;第二个方法返回值为EOF。第三个区别,第一个方法返回istream对象的引用,因此可拼接,第二个方法返回的是int型,不可拼接。
在这里插入图片描述输入字符串:
标准输入方法中也重载了字符指针(指向字符串),抽取运算符最大的bug就是无法读取带有空格的字符串,因为忽略空格,这导致存在多个单词空格构成的字符串只能读取一个单词。为了能保留空格(单字符的get方法也是为了保留空格),改变了读取规则!函数参数有两个指示停止读取的标记:最大字符的个数,分界符。当达到最大个数或是读入到分界符停止读取。具体来说又分为get()和getline(),区别在于是否从流中抽取并舍弃分界符。这两个方法又有两个版本,当不指示分界符时,默认采取换行符作为分界符。
函数原型:
在这里插入图片描述
getline()实现思路:
读取字符到字符串内存空间中,直到达到文件末尾、到达最大字符个数的限制才会停止当前流的读取。对于达到文件末尾设置eofbit位,对于到达最大字符个数的限制,修改failbit位。这两种原因使得输入流暂停使用除非使用clear重启流正常工作。
get()实现思路:
读取字符到字符串内存空间中,先是判断是否达到字符字符个数的限制(这里体现最大字符个数),再测试文件末尾、下一个字符是否为换行符(流是正常的,能继续读取)、是否读入空行。对于文件末尾设置eofbit,对于读入空行设置failbit。这两种情况将导致流停止读取。
没有抽取任何字符,设置状态位(eofbit和failbit)
在这里插入图片描述
get()和getline()面对异常值时的处理:
1、从流中输入时遇见文件的末尾,get()和getline()设置eofbit标记位
2、从流中输入时遇见故障,get()和getline()设置badbit标记位
3、流中输入的是空行(空字符),get()和getline()面对空行处理不一样,导致检测条件不一样。
回顾一下:何时设置流中标记位?无法从流中读取或是写入字符!
get():
读取输入时,空行或是文件到达末尾,无法从流中抽取,因此改变流的状态以及向字符串中存入空值字符’\0’,cin.get(char *s,int n)返回false,可以使用此作为读入是空行条件判断。
getline():
与get()不同,当流中是空行时,还是会从流中抽取并舍弃换行符同时向字符串中存入空值字符,由于抽取了字符不设置状态位,cin.get(char *s,int n)返回true。但可以使用cin.get(char *s,int n)和s[0]!=’\0’作为条件判断读入的不是空行

istream其他方法:
(1)read(char *,int)
从流中读取指定字符个数的字符到字符串中,与get()不同不会为字符串末尾加入空值字符,因而不能将流中的字符转化为字符串。可以看出吧,用于文件输入的。键盘怎能会有指定字符个数一说呢?!
(2)peek()
返回int
查看流中将要读取的字符,仅仅查看不抽取!可用于单字符的输入,当满足一定条件时持续读出并显示;或是先判断将读入的字符是否满足条件,满足再从流中读出存入到字符数组中(相当是get(char *,int ,char)另一种实现)。
(3)gcount()
从流中读取了多少字符
(4)pushback(char)
向输入流最前面插入一个字符

文件输入、输出:
头文件fstream中包含ifstream类、ostream类、fstream类,前两个类继承与iostream类,因而可以使用iostream类中的方法对流进行读写操作。与iostream不同的是,输入设备、输出设备变了,但是对流的操作是一样的,因此可仍可调用从iostream继承来的方法、控制符对流进行操作。
与iostream不同处在于,需要为其分配输出、输入设备(关联文件),支持两张写法:先创建流对象,再绑定输入输出文件,利用open()方法;创建流对象的同时为其关联文件。
在这里插入图片描述
当输入流、输出流对象过期时,也会自动关闭流到文件的连接;同样也可以通过close()显示断开流和文件连接,但此时流及其缓冲区仍是存在的,可以重新关联新的文件,显示调用close方法的可以使得刷新缓冲区,写入到文件,或是读入到程序中。

#include<fstream>
#include<iostream>
#include<string>
using namespace std;
const int limit = 100;
int main()
{
	cout << "your filename:";
	string filename;
	cin >> filename;
	ofstream fout(filename.c_str());
	cout << " your name:";
	string name;
	cin >> name;
	fout << name<<endl;
	cout << "password:";
	cin >> name;
	fout << name<<endl;
	fout.close();
	ifstream fcin;
	fcin.open(filename.c_str());
	//while(fcin.get())
	char str[limit];
	while (fcin.getline(str, limit))
	{
		cout << str<<endl;
	}
	cin.get();
	cin.get();
    return 0;
}


文件流中状态位检测:
fstream类继承于iostream类,iostream类继承与ios_base类,因此fstream也包含查看修改状态位方法,可以通过这些方法判断最后一次流操作是否成功。但是对于文件来说,当打开文件失败时,会设置状态位failbit。当把流对象放在条件测试语句中,流对象会根据是否设置了状态位来返回bool值。除了查看流对象的状态来检测是否打开文件外,更好的方式为调用is_open()判断,能够判断不合适的文件模式。

补充一些额外知识!命令行参数:
输入命令的同时输入参数,原型为:第一个参数代表输入参数个数,包括命令;第二个参数指出将字符串存储的位置。argv[0]存储的是命令。
在这里插入图片描述

#include<iostream>
#include<fstream>
using namespace std;
int main(int argc, char* argv[]) {

	if (argc == 1)
	{
		cout << " no file" << endl;
		exit(EXIT_FAILURE);
	}
	ifstream fcin;
	int total = 0;
	char ch;
	for (int i = 1; i < argc; i++) {
		int count = 0;
		fcin.open(argv[i]);
		if (fcin.is_open())
		{
			while (fcin.get(ch))
			{
				cout << ch;
				count++;
			}
			total += count;
			cout << "this file has " << count << endl;
			//fcin.clear();
			fcin.close();  //这里出错了!!!!当使用一个文件流 关联多个文件 必须显示断开文件连接后再重新关联  
			
		}
		else
		{
			cout << "file open fail!" << endl;
		}

	}

	cout << "all characters:" << total << endl;
	cin.get();
	cin.get();
	return 0;
}


当流关联文件时,可以通过文件模式指出文件将如何被使用,这其实是open()方法或是初始化流对象时的第二个参数,ifstream构造函数默认值是ios_base::in(文件用于读取),ofstream类构造函数第二个参数默认值是ios_base::out|ios_base::trunc(文件用于写入),fstream类的构造函数不提供默认值,必须显示指出。


#include "stdafx.h"
#include<iostream>
#include<string>
#include<fstream>
using namespace std;
const int limit = 100;
int main()
{
	/*int x;
	int y;
	char xx;
	cin >> x;
	//cin >> y;
	//cin >> xx;
	char c;
	while ((c = cin.get()) == '\n')
		cout << c;*/
	//测试!标准的>>抽取符 跳过空格、制表符、换行 这是有多个输入数据的情况
	//原理:从流中读取字符流到指定的数据中,先抽取走非空白字符,直到找到第一个非空白字符开始读到内存中,遇到空白字符立即停止 当输入多个数据的时候 前面输入所留下的空白字符由于后面的输入而被抽取走
	//但是最后一个数据输入时,由于后面没有数据的输入了 换行符仍在流中 如果忘记从流中拿走这个换行符 将对后面的数据带来什么影响?如果之后还是标准输入还好 会被抽走 如果不是,对于get(char *)停止流输入   getline()存入的是空字符

	//被这个>>原理坑了了好久好久 没意识到这里有个换行符 而对于后面的getline()测试条件 使得长度大于0失效 因此一直无法进入循环
	char *filename = "1.txt";
	//string filename;
	char filename1[limit];
	//cin >> filename1;
	//cin.get(filename1, limit);
	//cout << char(cin.get());
	
	ifstream fcin;
	fcin.open(filename);
	char ch;
	if (fcin.is_open())
	{
		while (fcin.get(ch))
		{
			cout << ch;
		}
		fcin.close();
	}
	
	
	
	ofstream fout(filename,ios::out|ios::app);
	if (!fout.is_open())
	{
		cout << "fail open ";
		exit(EXIT_FAILURE);
	}
	cout << "input content or empty line to quit:";
	string contents;
	//cin >> contents;
	while (getline(cin, contents) && contents.size() > 0)
	{
		fout << contents << endl;
		
	}
	fout.close();
	
	
	fcin.open(filename);
	if (!fcin.is_open()) {
		cout << "fail open ";
		exit(EXIT_FAILURE);
	}
	while (fcin.get(ch))
	{
		cout << ch;
	}
	fcin.close();
	cin.get();
    return 0;
}

二进制文件
根据以何种方式来解释数据,可将文件分为二进制文件、文本文件,默认使用文本文件。比如,一个浮点数2.3433,在计算机内部的表示为64位的01二进制数,那么在二进制文件中存储的就是这64位01数;而对于文本文件,将其看成6个字符,存的是这6个字符的ASCII。文件最终都是以01二进制存储数据,但是解释数据方式不同。
二进制文件常用来存储类对象(数据成员),结构。省去一个个访问数据成员写入到输出流,而是直接利用对象的内存地址将对象内存表示写入到二进制文件。

以二进制方式写数据到二进制文件中:
1、通过write()方法,对于数值数据,流传输的是数值数据在计算机内部的二进制表示,第二个参数是在内存中存储所需的字节个数。
2、确保是文件读取模式为二进制
在这里插入图片描述
从二进制文件中读取二进制数据:
1、确保是文件读取模式为二进制
2、read(),第一个参数指出内存地址,第二个参数指出字节个数

//fstream ifstream ofstream类 关于缓冲区的指针使用有点问题

#include<iostream>
#include<fstream>
using namespace std;


struct plante {
	char name[20];
	int age;
};

int main()
{
	char *filename = "1.txt";
	ifstream fin;
	fin.open(filename, ios::binary | ios::in);
	plante p1;
	if (fin.is_open())
	{

		while (fin.read((char *)&p1, sizeof(p1)))
		{
			cout << p1.name << " " << p1.age << endl;
		}
		fin.close();

	}

	ofstream fout;
	fout.open(filename, ios::binary | ios::out | ios::app);
	if (!fout.is_open())
	{
		cout << "open fail!" << endl;
		exit(EXIT_FAILURE);
	}
	cout << "your name or empty line to quit:";
	while (cin.get(p1.name, 20))//当有空行 设置failbit 返回false
	{
		cin.get();//get()方法不抽取换行符
		cout << "your age";
		cin >> p1.age;
		cin.get();//输入年纪后有一个换行符在流中 这导致无法继续循环
		fout.write((char *)&p1, sizeof(p1));
		cout << "your name or empty line to quit:";

	}
	fout.close();
	fin.open(filename, ios::binary | ios::in);
	if (fin.is_open())
	{
		while (fin.read((char *)&p1, sizeof(p1)))
		{
			cout << p1.name << " " << p1.age << endl;

		}
		fin.close();
	}
    return 0;
}

随机存取:
适用于由多条记录组成的文件,由于每条记录的大小相同,故而可实现随机存取。
挪动流指针到指定的字节位置!
输入流调用seekg(streampos)参数显示了字节个数,从0开始取值,来设置指针位置
输出流调用seekp()设置字节流中指针位置
tellp()、tellg()可显示指针位置

#include<iostream>
#include<fstream>
using namespace std;
const int SIZE = 20;
struct plante {
	char name[SIZE];
	int age;

};

int main()
{
	char *filename = "1.txt";
	fstream finout;
	finout.open(filename, ios::binary | ios::in | ios::out);
	plante p1;
	int count = 0;
	if (finout.is_open())
	{
		while (finout.read((char *)&p1, sizeof(p1)))
		{
			cout << p1.name << " " << p1.age << endl;
			count++;

		}

		finout.clear();//很重要!由于文件末尾,使得状态位被修改,此时流不能使用了。。。。。
	}

	int n;
	cout << "input number:";
	cin >> n;
	cin.get();
	//nout.seekg(
	if (n<0 || n>count)
		cout << "ilegal " << endl;
	else
	{
		finout.seekg(n * sizeof(p1));
		finout.read((char *)&p1, sizeof(p1));
		cout << "current value:";
		cout << p1.name << " " << p1.age << endl;
		if (finout.eof())
			finout.clear();
		cout << "new name";
		cin.get(p1.name, SIZE);
		cin.get();
		cout << "new age:";
		cin >> p1.age;
		cin.get();
		finout.seekp(n * sizeof(p1));
		finout.write((char *)&p1, sizeof(p1));
		if (finout.eof())
			finout.clear();
		finout.seekg(0);
		while (finout.read((char *)&p1, sizeof(p1)))
		{
			cout << p1.name << " " << p1.age << endl;
			//unt++;

		}
		finout.clear();
		finout.close();
		cin.get();
	}
	


    return 0;
}


内核格式化:
程序中的数据读入、写出到string对象的过程称为内核格式化。
头文件sstream定义了ostringstream类和istringstream,将程序信息写入到ostringstream的缓冲中,利用方法str()可返回初始化为缓冲区内容的string对象。istringstream类的构造函数可使得缓冲区为构造函数的参数,利用istream中的方法可将数据读入到程序中。

#include<sstream>
#include<iostream>

#include<string>

int main()
{
	using std::cout;
	using std::string;
	int n = 30;
	std::ostringstream  sout;
	sout << n;
	string result = sout.str();
	char c;

	std::istringstream sin(result);
	sin >> c;
	cout << c << std::endl;
	cout << result;
	std::cin.get();
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值