JSON的解析与反解析

JSON.h

 

#ifndef __JSON__H__
#define __JSON__H__

#include <stdio.h>

typedef enum  //表示7种数据类型
{
	LEPT_NULL,  //null
	LEPT_FALSE, //false
	LEPT_TRUE,  //true
	LEPT_NUMBER,//数字
	LEPT_STRING,//字符串
	LEPT_ARRAY, //数组
	LEPT_OBJECT //对象
}lept_type;

enum // 
{
	LEPT_PARSE_OK = 0,  //表示没有错误
	LEPT_PARSE_EXPECT_VALUE,//表示一个json只含有空白
	LEPT_PARSE_INVALID_VALUE,//一个值之后,在空白之后还有其他字符
	LEPT_PARSE_ROOT_NOT_SINGULAR,//不属于那7种类型
	
	//double数字越界错误码
	LEPT_PARSE_NUMBER_TOO_BIG,   //数字过大
	
	//字符串中丢失双引号错误码
	LEPT_PARSE_MISS_QUOTATION_MARK,  //丢失双引号

	//转义符号后边出现了非转义字符的错误码
	LEPT_PARSE_INVALID_STRING_ESCAPE,//转义错误
	
	//出现了0x20一下的字符的错误码
	LEPT_PARSE_INVALID_STRING_CHAR,//字符错误

	//0xXXXX字符转义中的错误码
	LEPT_PARSE_INVALID_UNICODE_HEX,// \u 后不是 4 位十六进位数
	LEPT_PARSE_INVALID_UNICODE_SURROGATE, //只有高代理项而欠缺低代理项,或是低代理项不在合法码点范围

	//解析数组出现错误
	LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET,//丢失逗号或者右中括号
	//解析对象出现错误
	LEPT_PARSE_MISS_KEY,  //丢失key
	LEPT_PARSE_MISS_COLON, //丢失冒号
	LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET  //丢失逗号或者右大括号
};

typedef struct lept_value lept_value;
typedef struct lept_member lept_member;

struct lept_value //节点的数据结构
{
	union
	{
		struct{ lept_member* m; size_t size; }o; //对象数组
		struct{ lept_value* e; size_t size; }a; //数组
		struct{ char* s; int length; }s;//字符串
		double n;
		
	}u;
	lept_type _type;

}e;

struct lept_member
{
	char* kstr;
	size_t klen;
	lept_value value;
};

int lept_parse(lept_value* v/*根节点指针*/, const char* json);//解析json字符串
char* lept_stringify(const lept_value* v, size_t* length);

lept_type lept_get_type(const lept_value* v);//获取v节点的数据类型

void lept_init(lept_value* v); //初始化类型
void lept_free(lept_value* v); //释放string空间

#define lept_set_null(v) lept_free(v)

int lept_get_boolean(const lept_value* v); //获取bool值
void lept_set_boolean(lept_value* v, int b);//设置bool值

double lept_get_number(const lept_value* v);//获取数字大小
void lept_set_number(lept_value* v, double n); //设置数字大小

const char* lept_get_string(const lept_value* v);//获取字符串
size_t lept_get_string_length(const lept_value* v);//获取字符串长度
void lept_set_string(lept_value* v, const char* s, size_t len);//设置字符串

size_t lept_get_array_size(const lept_value* v);
lept_value* lept_get_array_element(const lept_value* v, size_t index);

size_t lept_get_object_size(const lept_value* v);
const char* lept_get_object_key(const lept_value* v, size_t index);
size_t lept_get_object_key_length(const lept_value* v, size_t index);
lept_value* lept_get_object_value(const lept_value* v, size_t index);

#endif //__JSON__H__

 

JSON.c

 

#define _CRT_SECURE_NO_WARNINGS 1

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <math.h>
#include <string.h>

#include "JSON.h"

#define ISDIGIT(ch)         ((ch) >= '0' && (ch) <= '9')
#define ISDIGIT1TO9(ch)     ((ch) >= '1' && (ch) <= '9')
#define PUTC(c, ch)         do { *(char*)lept_context_push(c, sizeof(char)) = (ch); } while(0)
#define PUTS(c, s, len)     memcpy(lept_context_push(c, len), s, len)


typedef struct  //解析器
{
	const char* json;  //指向未解析字符串的开始位置
	//维护一个栈
	char* stack;
	size_t size; //栈的空间大小
	size_t top; //栈顶
}lept_context;

static void* lept_context_push(lept_context* c, size_t size)
{
	void* ret;
	assert(size > 0);

	if (c->top + size >= c->size) //栈放不下了
	{
		if (c->size == 0) //表示栈为空
		{
			c->size = 256;
		}

		while (c->top + size >= c->size)
		{
			c->size += c->size >> 1;//将大小扩大为当前的1.5倍;
		}

		c->stack = (char*)realloc(c->stack,c->size);
	}

	ret = c->stack + c->top;
	c->top += size;
	return ret; //返回栈顶的指针
}

static void* lept_context_pop(lept_context* c, size_t size)
{
	assert(c->top >= size);
	return c->stack + (c->top -= size); //返回栈顶的指针
}


static void lept_parse_whitespace(lept_context* c)//忽略空白字符
{
	const char* cur = c->json;
	while (*cur == ' ' || *cur == '\n' || *cur == '\t' || *cur == 'r')
		++cur;

	c->json = cur;
}

static int lept_parse_null(lept_context* c, lept_value* v) //判断是否为null
{
	assert(*c->json == 'n');

	c->json++;
	if (c->json[0] != 'u' || c->json[1] != 'l' || c->json[2] != 'l') 
		return LEPT_PARSE_INVALID_VALUE;//表示不符合特定数据类型
	c->json += 3;
	v->_type = LEPT_NULL;

	return LEPT_PARSE_OK;
}

static int lept_parse_true(lept_context* c, lept_value* v)  //判断是否为true
{
	assert(*c->json == 't');

	c->json++;
	if (c->json[0] != 'r' || c->json[1] != 'u' || c->json[2] != 'e')
		return LEPT_PARSE_INVALID_VALUE;
	c->json += 3;
	v->_type = LEPT_TRUE;

	return LEPT_PARSE_OK;
}

static int lept_parse_false(lept_context* c, lept_value* v)  //判断是否为false
{
	assert(*c->json == 'f');

	c->json++;
	if (c->json[0] != 'a' || c->json[1] != 'l' || c->json[2] != 's' || c->json[3] != 'e')
		return LEPT_PARSE_INVALID_VALUE;
	c->json += 4;
	v->_type = LEPT_FALSE;

	return LEPT_PARSE_OK;
}

static int lept_parse_number(lept_context* c, lept_value* v)  //判断是否为数字
{
	const char* p = c->json;
	if (*p == '-')//整数部分
		++p;
	if (*p == '0')
		++p;
	else
	{
		if (!ISDIGIT1TO9(*p))
			return LEPT_PARSE_INVALID_VALUE;
		for (p++; ISDIGIT(*p); p++);
	}
	if (*p == '.') //小数部分
	{
		++p;
		if (!ISDIGIT(*p))  //有可能小数点后边没有数字
			return LEPT_PARSE_INVALID_VALUE;
		for (p++; ISDIGIT(*p); p++);
	}
	if (*p == 'e' || *p == 'E') //指数部分
	{
		++p;
		if (*p == '+' || *p == '-')
			++p;
		if (!ISDIGIT(*p))  //有可能没有指数
			return LEPT_PARSE_INVALID_VALUE;
		for (p++; ISDIGIT(*p); p++);//跳过数字(指数部分第一个为0是合法的)
	}

	errno = 0;
	v->u.n = strtod(c->json, NULL);
	if (errno == ERANGE && (v->u.n == HUGE_VAL || v->u.n == -HUGE_VAL))  //处理越界
		return LEPT_PARSE_NUMBER_TOO_BIG;
	v->_type = LEPT_NUMBER;
	c->json = p;

	return LEPT_PARSE_OK;
}

static const char* lept_parse_hex4(const char* p, unsigned* u)  //判断\u后是不是4位十六进位数
{
	int i;
	*u = 0;
	for (i = 0; i < 4; i++) {
		char ch = *p++;
		*u <<= 4;
		if (ch >= '0' && ch <= '9')  *u |= ch - '0';
		else if (ch >= 'A' && ch <= 'F')  *u |= ch - ('A' - 10);
		else if (ch >= 'a' && ch <= 'f')  *u |= ch - ('a' - 10);
		else return NULL;
	}
	return p;
}

static void lept_encode_utf8(lept_context* c, unsigned u)  //UTF-8编码  U+0000 ~ U+10FFFF
{
	if (u <= 0x7f)
	{
		PUTC(c, u & 0xFF);
	}
	else if (u <= 0x7FF)
	{
		PUTC(c, 0xC0 | (u >> 6) & 0xFF);
		PUTC(c, 0x80 | (u)      & 0x3F);
	}
	else if (u <= 0xFFFF)
	{
		PUTC(c, 0xE0 | (u >> 12) & 0xFF);
		PUTC(c, 0x80 | (u >> 6)  & 0x3F);
		PUTC(c, 0x80 | (u)       & 0x3F);
	}
	else 
	{
		assert(u <= 0x10FFFF);
		PUTC(c, 0xF0 | ((u >> 18) & 0xFF));
		PUTC(c, 0x80 | ((u >> 12) & 0x3F));
		PUTC(c, 0x80 | ((u >> 6) & 0x3F));
		PUTC(c, 0x80 | (u & 0x3F));
	}
}

#define STRING_ERROR(error) do{ c->top = head; return error; }while(0)

static int lept_parse_string_raw(lept_context* c, char** str, size_t* len)
{
	assert(*c->json == '\"');
	c->json++;

	unsigned u;
	unsigned u2;
	size_t head = c->top;

	const char* p = c->json;
	while (1)
	{
		char ch = *p++;
		switch (ch)
		{
		case '\"':  //表示当前字符串到结尾
			*len = c->top - head;
			*str = (char*)lept_context_pop(c, *len);  //pop只是修改了top的值,原本存储的数据并不会丢失
			c->json = p;
			return LEPT_PARSE_OK;
		case '\\': //表示下一个字符是转义字符
			switch (*p++)
			{
			case '\"': PUTC(c, '\"'); break;
			case '\\': PUTC(c, '\\'); break;
			case '/':  PUTC(c, '/'); break;
			case 'b':  PUTC(c, '\b'); break;
			case 'f':  PUTC(c, '\f'); break;
			case 'n':  PUTC(c, '\n'); break;
			case 'r':  PUTC(c, '\r'); break;
			case 't':  PUTC(c, '\t'); break;
			case 'u':
				if (!(p = lept_parse_hex4(p, &u)))
					STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX);
				if (u >= 0xD800 && u <= 0xDBFF)//代理对(高代理项)
				{
					if (*p++ != '\\')
						STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE);
					if (*p++ != 'u')
						STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE);
					if (!(p = lept_parse_hex4(p, &u2)))
						STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX);
					if (u2<0xDC00 || u2> 0xDFFF)
						STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE);
					u = (((u - 0xD800) << 10) | (u2 - 0xDC00)) + 0x10000;
				}
				lept_encode_utf8(c, u);
				break;
			default:
				STRING_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE);
			}break;
		case '\0':
			STRING_ERROR(LEPT_PARSE_MISS_QUOTATION_MARK);
		default:  //表示为普通字符
			if ((unsigned char)ch < 0x20) {
				STRING_ERROR(LEPT_PARSE_INVALID_STRING_CHAR);
			}
			PUTC(c, ch);
		}
	}
}

static int lept_parse_string(lept_context* c, lept_value* v)  //判断是否为字符串   
{
	int ret;
	char* s;
	size_t len;
	if ((ret = lept_parse_string_raw(c, &s, &len)) == LEPT_PARSE_OK)
		lept_set_string(v, s, len);
	return ret;
}

static int lept_parse_value(lept_context* c, lept_value* v);//声明

static int lept_parse_array(lept_context* c, lept_value* v)
{
	assert(*c->json == '[');
	c->json++;

	int i;
	size_t size = 0; //数组元素个数
	int ret;

	lept_parse_whitespace(c);//处理空白
	if (*c->json == ']')  //[]数组为空
	{
		c->json++;

		v->_type = LEPT_ARRAY;
		v->u.a.size = 0;
		v->u.a.e = NULL;
		return LEPT_PARSE_OK;
	}

	while (1) 
	{
		lept_value e;
		lept_init(&e);

		if ((ret = lept_parse_value(c, &e)) != LEPT_PARSE_OK)
			break;

		memcpy(lept_context_push(c,sizeof(lept_value)),&e,sizeof(lept_value)); //e为一个有效的对象,将其压栈
		++size;

		lept_parse_whitespace(c);//处理空白
		if (*c->json == ',')
		{
			c->json++;
			lept_parse_whitespace(c);
		}
		else if (*c->json == ']')
		{
			c->json++;
			v->_type = LEPT_ARRAY;
			v->u.a.size = size;

			size *= sizeof(lept_value);

			memcpy(v->u.a.e = (lept_value*)malloc(size),lept_context_pop(c,size),size);//出栈
			return LEPT_PARSE_OK;
		}
		else
		{	
			ret =  LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET;
			break;
		}
	}

	
	for (i = 0; (size_t)i < size; i++)
		lept_free((lept_value*)lept_context_pop(c, sizeof(lept_value)));
	return ret;
}

static int lept_parse_object(lept_context* c, lept_value* v)
{
	assert(*c->json == '{');
	c->json++;

	int ret;
	int i;
	lept_member m;
	size_t size;

	lept_parse_whitespace(c);
	if (*c->json == '}')  //有可能存在空对象
	{
		c->json++;
		v->_type = LEPT_OBJECT;
		v->u.o.size = 0;
		v->u.o.m = NULL;
		return LEPT_PARSE_OK;
	}

	m.kstr = NULL;
	size = 0;

	while (1)
	{
		//先处理key
		char* str;
		lept_init(&m.value);

		if (*c->json != '"') //丢失key
		{
			ret = LEPT_PARSE_MISS_KEY;
			break;
		}

		if ((ret = lept_parse_string_raw(c, &str, &m.klen)) != LEPT_PARSE_OK) //获取key值
			break;

		memcpy(m.kstr = (char*)malloc(m.klen + 1), str, m.klen); //给key申请空间
		m.kstr[m.klen] = '\0';

		lept_parse_whitespace(c);
		if (*c->json != ':')  //判断有无冒号
		{
			ret = LEPT_PARSE_MISS_COLON;
			break;
		}
		c->json++;
		lept_parse_whitespace(c);
		//处理value
		if ((ret = lept_parse_value(c, &m.value)) != LEPT_PARSE_OK) //获取value值
			break;
		memcpy(lept_context_push(c,sizeof(lept_member)),&m,sizeof(lept_member)); //需要压栈
		++size;

		m.kstr = NULL; //已经压如栈中,因此可以置空

		lept_parse_whitespace(c);
		if (*c->json == ',')
		{
			c->json++;
			lept_parse_whitespace(c);
		}
		else if (*c->json == '}')
		{
			size_t s = sizeof(lept_member)*size;
			memcpy(v->u.o.m = (lept_member*)malloc(s),lept_context_pop(c,s),s);//出栈并分配空间
			v->_type = LEPT_OBJECT;
			v->u.o.size = size;
			c->json++;
			return LEPT_PARSE_OK;
		}
		else
		{
			ret = LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET;
			break;
		}
	}

	//解析出现错误就会到这
	//释放之前的内存
	free(m.kstr);
	for (i = 0; (size_t)i < size; ++i)
	{
		lept_member* m = (lept_member*)lept_context_pop(c, sizeof(lept_member));
		free(m->kstr);
		lept_free(&m->value);
	}
	v->_type = LEPT_NULL;
	return ret;
}

static int lept_parse_value(lept_context* c, lept_value* v)
{
	switch (*c->json)
	{
	case 'n':return lept_parse_null(c, v);
	case 't':return lept_parse_true(c, v); 
	case 'f':return lept_parse_false(c, v);
	case '\0':return LEPT_PARSE_EXPECT_VALUE;
	case '"':return lept_parse_string(c, v);
	case '[':return lept_parse_array(c, v);
	case '{':return lept_parse_object(c, v);
	default:return lept_parse_number(c, v);
	}
}

int lept_parse(lept_value* v/*根节点指针*/, const char* json)//解析json字符串
{
	assert(v);

	lept_context c;
	int ret;
	//初始化c的成员
	c.json = json;
	c.stack = NULL;
	c.top = c.size = 0;
	//初始化类型
	lept_init(v);

	//处理空白
	lept_parse_whitespace(&c);
	
	if ((ret = lept_parse_value(&c, v)) == LEPT_PARSE_OK)//处理value
	{
		lept_parse_whitespace(&c);//处理空白
		if (*c.json != '\0')
		{
			v->_type = LEPT_NULL;
			ret = LEPT_PARSE_ROOT_NOT_SINGULAR;
		}		
	}

	assert(c.top == 0);//保证栈中数据已经处理完
	free(c.stack);

	return ret;
}


static void lept_stringify_string(lept_context* c, const char* s, size_t len)
{
	assert(c && s);
	size_t i = 0;

	PUTC(c, '"');
	for (i; i < len; ++i)
	{
		unsigned char ch = (unsigned char)s[i];
		switch (ch) {
		case '\"': PUTS(c, "\\\"", 2); break;
		case '\\': PUTS(c, "\\\\", 2); break;
		case '\b': PUTS(c, "\\b", 2); break;
		case '\f': PUTS(c, "\\f", 2); break;
		case '\n': PUTS(c, "\\n", 2); break;
		case '\r': PUTS(c, "\\r", 2); break;
		case '\t': PUTS(c, "\\t", 2); break;
		default:
			if (ch < 0x20) {
				char buffer[7];
				sprintf(buffer, "\\u%04X", ch);
				PUTS(c, buffer, 6);
			}
			else
				PUTC(c, s[i]);
		}
	}


	PUTC(c, '"');
}

static void lept_stringify_value(lept_context* c, const lept_value* v)
{
	size_t i;
	switch (v->_type) {
	case LEPT_NULL:   PUTS(c, "null", 4); break;
	case LEPT_FALSE:  PUTS(c, "false", 5); break;
	case LEPT_TRUE:   PUTS(c, "true", 4); break;
	case LEPT_NUMBER: c->top -= 32 - sprintf(lept_context_push(c, 32), "%.17g", v->u.n); break;
	case LEPT_STRING: lept_stringify_string(c, v->u.s.s, v->u.s.length); break;
	case LEPT_ARRAY:
		PUTC(c, '[');
		for (i = 0; i < v->u.a.size; ++i)
		{
			if (i>0)
				PUTC(c, ',');
			lept_stringify_value(c, &v->u.a.e[i]);
		}
		PUTC(c, ']');
		break;
	case LEPT_OBJECT:
		PUTC(c, '{');
		for (i = 0; i < v->u.o.size; ++i)
		{
			if (i>0)
				PUTC(c, ',');
			lept_stringify_string(c, v->u.o.m[i].kstr, v->u.o.m[i].klen);  //先写key
			PUTC(c, ':');   //写:
			lept_stringify_value(c, &v->u.o.m[i].value);//写value
		}
		PUTC(c, '}');
		break;
	default: assert(0 && "invalid type");
	}
}

char* lept_stringify(const lept_value* v, size_t* length)
{
	assert(v);
	lept_context c;
	c.stack = (char*)malloc(256);
	c.size = 256;
	c.top = 0;

	lept_stringify_value(&c, v);

	if (length)
		*length = c.top;
	PUTC(&c, '\0');

	return c.stack;
}

void lept_init(lept_value* v) //初始化类型
{
	assert(v);
	v->_type = LEPT_NULL;
}

void lept_free(lept_value* v) //空间的释放
{
	size_t i;
	assert(v != NULL);
	switch (v->_type) 
	{
	case LEPT_STRING:
		free(v->u.s.s);
		break;
	case LEPT_ARRAY:
		for (i = 0; i < v->u.a.size; i++)
			lept_free(&v->u.a.e[i]);
		free(v->u.a.e);
		break;
	case LEPT_OBJECT:
		for (i = 0; i < v->u.o.size; i++) 
		{
			free(v->u.o.m[i].kstr);
			lept_free(&v->u.o.m[i].value);
		}
		free(v->u.o.m);
		break;
	default: break;
	}
	v->_type = LEPT_NULL;
}

lept_type lept_get_type(const lept_value* v)//获取v节点的数据类型
{
	assert(v);
	return v->_type;
}

int lept_get_boolean(const lept_value* v)
{
	assert(v && (v->_type == LEPT_TRUE || v->_type == LEPT_FALSE));
	return v->_type == LEPT_TRUE;
}

void lept_set_boolean(lept_value* v, int b)
{
	lept_free(v);
	v->_type = b ? LEPT_TRUE : LEPT_FALSE;
}

double lept_get_number(const lept_value* v)//获取数字
{
	assert(v && v->_type == LEPT_NUMBER);

	return v->u.n;
}

void lept_set_number(lept_value* v, double n)
{
	lept_free(v);
	v->u.n = n;
	v->_type = LEPT_NUMBER;
}

const char* lept_get_string(const lept_value* v)
{
	assert(v && v->_type == LEPT_STRING);
	return v->u.s.s;
}

void lept_set_string(lept_value* v, const char* s, size_t len)
{
	assert(v && (s!=NULL || len == 0));//可以是空字符串

	lept_free(v);
	v->u.s.s = (char*)malloc(len+1);
	memcpy(v->u.s.s, s ,len);
	v->u.s.s[len] = '\0';
	v->u.s.length = len;
	v->_type = LEPT_STRING;
}

size_t lept_get_string_length(const lept_value* v)
{
	assert(v && v->_type == LEPT_STRING);
	return v->u.s.length;
}

size_t lept_get_array_size(const lept_value* v)
{
	assert(v && v->_type == LEPT_ARRAY);
	return v->u.a.size;
}

lept_value* lept_get_array_element(const lept_value* v, size_t index)
{
	assert(v && v->_type == LEPT_ARRAY);
	assert(index < v->u.a.size);
	return &v->u.a.e[index];
}

size_t lept_get_object_size(const lept_value* v)
{
	assert(v && v->_type == LEPT_OBJECT);
	return v->u.o.size;
}

const char* lept_get_object_key(const lept_value* v, size_t index)
{
	assert(v && v->_type == LEPT_OBJECT);
	assert(index < v->u.o.size);
	return v->u.o.m[index].kstr;
}

size_t lept_get_object_key_length(const lept_value* v, size_t index)
{
	assert(v && v->_type == LEPT_OBJECT);
	assert(index < v->u.o.size);
	return v->u.o.m[index].klen;
}

lept_value* lept_get_object_value(const lept_value* v, size_t index)
{
	assert(v && v->_type == LEPT_OBJECT);

	assert(index < v->u.o.size);
	return &v->u.o.m[index].value;
}

 

test.c

#include "JSON.h"


int main()
{
	//char b[] = " \"programmers\": [ \"firstName\": \"Elliotte\", \"lastName\" : \"Harold\", \"email\" : \"elharo@macfaq.com\"] ";

	char a[] = " {"
		"\"n\" : null , "
		"\"f\" : false , "
		"\"t\" : true , "
		"\"i\" : 123 , "
		"\"s\" : \"abc\", "
		"\"a\" : [ 1, 2, 3 ],"
		"\"o\" : { \"1\" : 1, \"2\" : 2, \"3\" : 3 }"
		" } ";

	lept_value v[] = { 0 };
	int size = 0;

	lept_parse(v, a);  //先将字符串转成结构体存储于v中

	size = sizeof(v) / sizeof(v[0]);

	printf("%s", lept_stringify(v, &size));//将v结构体转成字符串
	return 0;
}

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值