ByteStream

前公司有个这样的类(感觉很像stringstream),主要是网络中传数据用的(大概是因为要考虑字节序的问题,所以有了这么一个类),不过公司的实现有一个可移植性的问题:比如将网络中的一端流入一个int/long类型的数据,只要两端int/long占用字节大小不一致,那么在网络的另一端流出就会出问题。

今天自己依据其原理写了个,当然我的实现在空间上是不及前公司的,因为前公司的有一些优化,比如bool只占1bit,一个int数据可能占1/2/3/4byte;我的没有这些,bool占1bit的思想,我感觉写在上层更好,如果有必要的话;对于比较小的数压缩下空间倒是不错的想法。另外,公司是一个类,我的是三个。

写这篇文章不会被告侵权吧?!

#ifndef __BYTE_STREAM_H__
#define __BYTE_STREAM_H__

#include <string>
using std::string;

class ByteStream
{
public:
    ByteStream(unsigned short len);
    ~ByteStream();

private:
    ByteStream(const ByteStream &);
    ByteStream & operator = (const ByteStream &);

public:
    static void big2little(void * obj, unsigned int size);

    bool seek(unsigned short offset);
    unsigned short tell();
    unsigned short left();

protected:
    unsigned short m_length;
    unsigned short m_offset;
};

class OBStream : public ByteStream
{
public:
    OBStream(char * byte, unsigned short len);
    ~OBStream();

public:
    void init(char * byte, unsigned short len);

    bool writebytes(unsigned short offset, 
                    const void * obj, unsigned short size);

public:
    OBStream & write(const char * ptr, unsigned short size);

    OBStream & operator << (bool);
    OBStream & operator << (signed char);
    OBStream & operator << (unsigned char);
    OBStream & operator << (signed short);
    OBStream & operator << (unsigned short);
    OBStream & operator << (signed int);
    OBStream & operator << (unsigned int);
    OBStream & operator << (signed long);
    OBStream & operator << (unsigned long);
    OBStream & operator << (float);
    OBStream & operator << (double);

    OBStream & operator << (const string &);

private:
    char * m_byte;
};

class IBStream : public ByteStream
{
public:
    IBStream(const char * byte, unsigned short len);
    ~IBStream();

public:
    void init(const char * byte, unsigned short len);

    bool readbytes(unsigned short offset, 
                   void * obj, unsigned short size);

public:
    IBStream & read(char * ptr, unsigned short size);

    IBStream & operator >> (bool &);
    IBStream & operator >> (signed char &);
    IBStream & operator >> (unsigned char &);
    IBStream & operator >> (signed short &);
    IBStream & operator >> (unsigned short &);
    IBStream & operator >> (signed int &);
    IBStream & operator >> (unsigned int &);
    IBStream & operator >> (signed long &);
    IBStream & operator >> (unsigned long &);
    IBStream & operator >> (float &);
    IBStream & operator >> (double &);

    IBStream & operator >> (string &);

private:
    const char * m_byte;
};

#endif
#include <cassert>
#include <cstring>
#include "ByteStream.h"


ByteStream::ByteStream(unsigned short len)
 : m_length(len), m_offset(0)
{
    
}

ByteStream::~ByteStream()
{
    
}

void ByteStream::big2little(void * obj, unsigned int size)
{
    static union {
        unsigned short us;
        unsigned char  uc[sizeof(unsigned short)];
    } un;
    un.us = 0x0001;

    if (0x00 == un.uc[0]) {
        char * ptr = static_cast<char *>(obj);
        for (int i = 0; i < size / 2; ++i) {
            char temp = ptr[i];
            ptr[i] = ptr[size - 1 - i];
            ptr[size - 1 - i] = temp;
        }
    }
}

bool ByteStream::seek(unsigned short offset)
{
    if (offset <= m_length) {
        m_offset = offset;
        return(true);
    }
    else {
        return(false);
    }
}

unsigned short ByteStream::tell()
{
    return(m_offset);
}

unsigned short ByteStream::left()
{
    return(m_length - m_offset);
}

OBStream::OBStream(char * byte, unsigned short len)
 : ByteStream(len), m_byte(byte)
{
    
}

OBStream::~OBStream()
{
    
}

void OBStream::init(char * byte, unsigned short len)
{
    m_length = len;
    m_offset = 0;
    m_byte = byte;
}

bool OBStream::writebytes(unsigned short offset, 
                          const void * obj, unsigned short size)
{
    if (offset + size <= m_length) {
        memcpy(m_byte + offset, obj, size);
        return(true);
    }
    else {
        return(false);
    }
}

OBStream & OBStream::write(const char * ptr, unsigned short size)
{
    assert(m_offset + size <= m_length);
    writebytes(m_offset, ptr, size);
    m_offset += size;
    return(*this);
}

OBStream & OBStream::operator << (bool value)
{
    assert(m_offset + sizeof(bool) <= m_length);
    big2little(&value, sizeof(bool));
    writebytes(m_offset, &value, sizeof(bool));
    m_offset += sizeof(bool);
    return(*this);
}

OBStream & OBStream::operator << (signed char value)
{
    assert(m_offset + sizeof(signed char) <= m_length);
    big2little(&value, sizeof(signed char));
    writebytes(m_offset, &value, sizeof(signed char));
    m_offset += sizeof(signed char);
    return(*this);
}

OBStream & OBStream::operator << (unsigned char value)
{
    assert(m_offset + sizeof(unsigned char) <= m_length);
    big2little(&value, sizeof(unsigned char));
    writebytes(m_offset, &value, sizeof(unsigned char));
    m_offset += sizeof(unsigned char);
    return(*this);
}

OBStream & OBStream::operator << (signed short value)
{
    assert(m_offset + sizeof(signed short) <= m_length);
    big2little(&value, sizeof(signed short));
    writebytes(m_offset, &value, sizeof(signed short));
    m_offset += sizeof(signed short);
    return(*this);
}
OBStream & OBStream::operator << (unsigned short value)
{
    assert(m_offset + sizeof(unsigned short) <= m_length);
    big2little(&value, sizeof(unsigned short));
    writebytes(m_offset, &value, sizeof(unsigned short));
    m_offset += sizeof(unsigned short);
    return(*this);
}

OBStream & OBStream::operator << (signed int value)
{
    assert(m_offset + sizeof(unsigned char) + sizeof(signed int) <= m_length);
    unsigned char size = static_cast<unsigned char>(sizeof(signed int));
    operator << (size);
    big2little(&value, sizeof(signed int));
    writebytes(m_offset, &value, sizeof(signed int));
    m_offset += sizeof(signed int);
    return(*this);
}

OBStream & OBStream::operator << (unsigned int value)
{
    assert(m_offset + sizeof(unsigned char) + sizeof(unsigned int) <= m_length);
    unsigned char size = static_cast<unsigned char>(sizeof(unsigned int));
    operator << (size);
    big2little(&value, sizeof(unsigned int));
    writebytes(m_offset, &value, sizeof(unsigned int));
    m_offset += sizeof(unsigned int);
    return(*this);
}

OBStream & OBStream::operator << (signed long value)
{
    assert(m_offset + sizeof(unsigned char) + sizeof(signed long) <= m_length);
    unsigned char size = static_cast<unsigned char>(sizeof(signed long));
    operator << (size);
    big2little(&value, sizeof(signed long));
    writebytes(m_offset, &value, sizeof(signed long));
    m_offset += sizeof(signed long);
    return(*this);
}

OBStream & OBStream::operator << (unsigned long value)
{
    assert(m_offset + sizeof(unsigned char) + sizeof(unsigned long) <= m_length);
    unsigned char size = static_cast<unsigned char>(sizeof(unsigned long));
    operator << (size);
    big2little(&value, sizeof(unsigned long));
    writebytes(m_offset, &value, sizeof(unsigned long));
    m_offset += sizeof(unsigned long);
    return(*this);
}

OBStream & OBStream::operator << (float value)
{
    assert(m_offset + sizeof(float) <= m_length);
    big2little(&value, sizeof(float));
    writebytes(m_offset, &value, sizeof(float));
    m_offset += sizeof(float);
    return(*this);
}

OBStream & OBStream::operator << (double value)
{
    assert(m_offset + sizeof(double) <= m_length);
    big2little(&value, sizeof(double));
    writebytes(m_offset, &value, sizeof(double));
    m_offset += sizeof(double);
    return(*this);
}

OBStream & OBStream::operator << (const string & value)
{
    assert(m_offset + sizeof(unsigned short) + value.size() <= m_length);
    unsigned short size = static_cast<unsigned short>(value.size());
    operator << (size);
    write(value.c_str(), size);
    return(*this);
}


IBStream::IBStream(const char * byte, unsigned short len)
 : ByteStream(len), m_byte(byte)
{
    
}

IBStream::~IBStream()
{
    
}

void IBStream::init(const char * byte, unsigned short len)
{
    m_length = len;
    m_offset = 0;
    m_byte = byte;
}

bool IBStream::readbytes(unsigned short offset, 
                         void * obj, unsigned short size)
{
    if (offset + size <= m_length) {
        memcpy(obj, m_byte + offset, size);
        return(true);
    }
    else {
        return(false);
    }
}

IBStream & IBStream::read(char * ptr, unsigned short size)
{
    assert(m_offset + size <= m_length);
    readbytes(m_offset, ptr, size);
    m_offset += size;
    return(*this);
}

IBStream & IBStream::operator >> (bool & value)
{
    assert(m_offset + sizeof(bool) <= m_length);
    readbytes(m_offset, &value, sizeof(bool));
    big2little(&value, sizeof(bool));
    m_offset += sizeof(bool);
    return(*this);
}

IBStream & IBStream::operator >> (signed char & value)
{
    assert(m_offset + sizeof(signed char) <= m_length);
    readbytes(m_offset, &value, sizeof(signed char));
    big2little(&value, sizeof(signed char));
    m_offset += sizeof(signed char);
    return(*this);
}

IBStream & IBStream::operator >> (unsigned char & value)
{
    assert(m_offset + sizeof(unsigned char) <= m_length);
    readbytes(m_offset, &value, sizeof(unsigned char));
    big2little(&value, sizeof(unsigned char));
    m_offset += sizeof(unsigned char);
    return(*this);
}

IBStream & IBStream::operator >> (signed short & value)
{
    assert(m_offset + sizeof(signed short) <= m_length);
    readbytes(m_offset, &value, sizeof(signed short));
    big2little(&value, sizeof(signed short));
    m_offset += sizeof(signed short);
    return(*this);
}

IBStream & IBStream::operator >> (unsigned short & value)
{
    assert(m_offset + sizeof(unsigned short) <= m_length);
    readbytes(m_offset, &value, sizeof(unsigned short));
    big2little(&value, sizeof(unsigned short));
    m_offset += sizeof(unsigned short);
    return(*this);
}

IBStream & IBStream::operator >> (signed int & value)
{
    assert(m_offset + sizeof(unsigned char) <= m_length);
    unsigned char size = 0;
    operator >> (size);
    assert(m_offset + size <= m_length);
    if (sizeof(signed int) == size) {
        readbytes(m_offset, &value, sizeof(signed int));
        big2little(&value, sizeof(signed int));
        m_offset += sizeof(signed int);
    }
    else if (sizeof(signed int) > size) {
        value = 0;
        unsigned char * ptr = reinterpret_cast<unsigned char *>(&value);
        for (int i = 0; i < size; ++i) {
            operator >> (ptr[i]);
        }
        big2little(&value, sizeof(signed int));
    }
    else {
        assert(sizeof(signed int) <= 8);
        unsigned char ptr[8] = { 0 };
        for (int i = 0; i < size; ++i) {
            operator >> (ptr[i]);
        }
        big2little(ptr, size);
        value = *(reinterpret_cast<signed int *>(ptr));
    }
    return(*this);
}

IBStream & IBStream::operator >> (unsigned int & value)
{
    assert(m_offset + sizeof(unsigned char) <= m_length);
    unsigned char size = 0;
    operator >> (size);
    assert(m_offset + size <= m_length);
    if (sizeof(unsigned int) == size) {
        readbytes(m_offset, &value, sizeof(unsigned int));
        big2little(&value, sizeof(unsigned int));
        m_offset += sizeof(unsigned int);
    }
    else if (sizeof(unsigned int) > size) {
        value = 0;
        unsigned char * ptr = reinterpret_cast<unsigned char *>(&value);
        for (int i = 0; i < size; ++i) {
            operator >> (ptr[i]);
        }
        big2little(&value, sizeof(unsigned int));
    }
    else {
        assert(sizeof(unsigned int) <= 8);
        unsigned char ptr[8] = { 0 };
        for (int i = 0; i < size; ++i) {
            operator >> (ptr[i]);
        }
        big2little(ptr, size);
        value = *(reinterpret_cast<unsigned int *>(ptr));
    }
    return(*this);
}

IBStream & IBStream::operator >> (signed long & value)
{
    assert(m_offset + sizeof(unsigned char) <= m_length);
    unsigned char size = 0;
    operator >> (size);
    assert(m_offset + size <= m_length);
    if (sizeof(signed long) == size) {
        readbytes(m_offset, &value, sizeof(signed long));
        big2little(&value, sizeof(signed long));
        m_offset += sizeof(signed long);
    }
    else if (sizeof(signed long) > size) {
        value = 0;
        unsigned char * ptr = reinterpret_cast<unsigned char *>(&value);
        for (int i = 0; i < size; ++i) {
            operator >> (ptr[i]);
        }
        big2little(&value, sizeof(signed long));
    }
    else {
        assert(sizeof(signed long) <= 8);
        unsigned char ptr[8] = { 0 };
        for (int i = 0; i < size; ++i) {
            operator >> (ptr[i]);
        }
        big2little(ptr, size);
        value = *(reinterpret_cast<signed long *>(ptr));
    }
    return(*this);
}

IBStream & IBStream::operator >> (unsigned long & value)
{
    assert(m_offset + sizeof(unsigned char) <= m_length);
    unsigned char size = 0;
    operator >> (size);
    assert(m_offset + size <= m_length);
    if (sizeof(unsigned long) == size) {
        readbytes(m_offset, &value, sizeof(unsigned long));
        big2little(&value, sizeof(unsigned long));
        m_offset += sizeof(unsigned long);
    }
    else if (sizeof(unsigned long) > size) {
        value = 0;
        unsigned char * ptr = reinterpret_cast<unsigned char *>(&value);
        for (int i = 0; i < size; ++i) {
            operator >> (ptr[i]);
        }
        big2little(&value, sizeof(unsigned long));
    }
    else {
        assert(sizeof(unsigned long) <= 8);
        unsigned char ptr[8] = { 0 };
        for (int i = 0; i < size; ++i) {
            operator >> (ptr[i]);
        }
        big2little(ptr, size);
        value = *(reinterpret_cast<unsigned long *>(ptr));
    }
    return(*this);
}

IBStream & IBStream::operator >> (float & value)
{
    assert(m_offset + sizeof(float) <= m_length);
    readbytes(m_offset, &value, sizeof(float));
    big2little(&value, sizeof(float));
    m_offset += sizeof(float);
    return(*this);
}

IBStream & IBStream::operator >> (double & value)
{
    assert(m_offset + sizeof(double) <= m_length);
    readbytes(m_offset, &value, sizeof(double));
    big2little(&value, sizeof(double));
    m_offset += sizeof(double);
    return(*this);
}

IBStream & IBStream::operator >> (string & value)
{
    assert(m_offset + sizeof(unsigned short) <= m_length);
    unsigned short size = 0;
    operator >> (size);
    assert(m_offset + size <= m_length);
    value.resize(size);
    read(&value[0], size);
    return(*this);
}
#include <complex>
#include <string>
#include <iostream>
using namespace std;

#include "ByteStream.h"

template <typename T>
OBStream & operator << (OBStream & obs, const complex<T> & comp)
{
    return(obs << comp.real() << comp.imag());
}

template <typename T>
IBStream & operator >> (IBStream & ibs, complex<T> & comp)
{
    T real;
    T imag;
    ibs >> real >> imag;
    comp = complex<T>(real, imag);
    return(ibs);
}

int main(int argc, char * argv[])
{
    char buffer[100] = { 0 };

    bool           in_b = true;
    signed char    in_sc = 'a';
    unsigned char  in_uc = 'A';
    signed short   in_ss = 12345;
    unsigned short in_us = 65535;
    signed int     in_si = 123456789;
    unsigned int   in_ui = 987654321;
    signed long    in_sl = 11223344;
    unsigned long  in_ul = 66554433;
    float          in_f  = 123456.789f;
    double         in_d = 987654321.123456789;
    const char   * in_ptr = "ByteStream";
    string         in_str("Hello World");
    complex<int>   in_comp(123, 456);

    cout << in_b << endl;
    cout << in_sc << endl;
    cout << in_uc << endl;
    cout << in_ss << endl;
    cout << in_us << endl;
    cout << in_si << endl;
    cout << in_ui << endl;
    cout << in_sl << endl;
    cout << in_ul << endl;
    cout << in_f << endl;
    cout << in_d << endl;
    cout << in_ptr << endl;
    cout << in_str << endl;
    cout << in_comp << endl;

    unsigned short in_size = 0;
    OBStream obs(buffer, 100);
    (obs << in_size << in_b << in_sc << in_uc << in_ss 
         << in_us << in_si << in_ui << in_sl << in_ul 
         << in_f << in_d).write(in_ptr, 10) << in_str << in_comp;
    in_size = obs.tell();
    obs.seek(0);
    obs << in_size;
    obs.seek(in_size);

    cout << "write " << in_size << " Bytes" << endl;

    unsigned short out_size;
    bool           out_b;
    signed char    out_sc;
    unsigned char  out_uc;
    signed short   out_ss;
    unsigned short out_us;
    signed int     out_si;
    unsigned int   out_ui;
    signed long    out_sl;
    unsigned long  out_ul;
    float          out_f;
    double         out_d;
    char           out_ptr[11] = { 0 };
    string         out_str;
    complex<int>   out_comp;

    IBStream ibs(buffer, obs.tell());
    (ibs >> out_size >> out_b >> out_sc >> out_uc >> out_ss 
         >> out_us >> out_si >> out_ui >> out_sl >> out_ul 
         >> out_f >> out_d).read(out_ptr, 10) >> out_str >> out_comp;

    cout << "read " << out_size << " Bytes" << endl;
    cout << out_b << endl;
    cout << out_sc << endl;
    cout << out_uc << endl;
    cout << out_ss << endl;
    cout << out_us << endl;
    cout << out_si << endl;
    cout << out_ui << endl;
    cout << out_sl << endl;
    cout << out_ul << endl;
    cout << out_f << endl;
    cout << out_d << endl;
    cout << out_ptr << endl;
    cout << out_str << endl;
    cout << out_comp << endl;

    return(0);
}

#include <iostream>
using namespace std;

#include "ByteStream.h"

int main(int argc, char * argv[])
{
    char buffer[32] = { 0 };
    unsigned short in_int = 65535;
    unsigned char  in_int_len = sizeof(unsigned short);
    long in_long = 123456;
    double * in_p_long = reinterpret_cast<double *>(&in_long);
    unsigned char in_long_len = sizeof(double);

    unsigned short in_size = 0;
    OBStream obs(buffer, 100);
    obs << in_size;
    obs << in_int_len << in_int;        /* less-int: (len + short) like a 16bit-int*/
    obs << in_long_len << (*in_p_long); /* large-long: (len + double) like a 64bit-long */
    in_size = obs.tell();
    obs.seek(0);
    obs << in_size;
    obs.seek(in_size);

    cout << in_int << endl;
    cout << in_long << endl;
    cout << "write " << in_size << " Bytes" << endl;

    unsigned short out_size = 0;
    signed int     out_int = 0;
    signed long    out_long = 0;

    IBStream ibs(buffer, obs.tell());
    ibs >> out_size;
    ibs >> out_int;  /* large-int */
    ibs >> out_long; /* less-long */

    cout << "read " << out_size << " Bytes" << endl;
    cout << out_int << endl;
    cout << out_long << endl;

    return(0);
}

今天被complex<T>整了,1是对它不熟,2是模板报错乱七八糟


<think>好的,我现在需要回答用户关于字节流(Byte Stream)的概念和使用方法的问题。首先,我要确保自己正确理解字节流的基本定义。字节流是处理二进制数据的基本单位,每个字节由8位组成,可以表示0到255的数值。在编程中,字节流用于读取或写入二进制文件,比如图片、音频、视频等,或者进行网络数据传输。 接下来,用户可能想知道字节流与字符流的区别。字符流处理的是字符数据,通常是文本,而字节流处理的是原始二进制数据。字符流在底层实际上也是基于字节流实现的,但会处理字符编码(如UTF-8、GBK),将字节转换为字符。因此,在处理文本时,使用字符流更方便,而处理非文本数据时,必须使用字节流。 然后,我需要考虑如何在不同的编程语言中使用字节流。例如,在Java中,InputStream和OutputStream是处理字节流的基类,FileInputStream和FileOutputStream用于文件操作。Python中使用open函数以二进制模式('rb'或'wb')打开文件进行字节操作。C++中则使用std::ifstream和std::ofstream,并指定二进制模式(如ios::binary)。 用户可能还需要了解字节流的应用场景,比如文件传输、网络通信、加密解密等。例如,在发送文件时,将文件分割成字节流传输;加密算法处理的是字节数据;序列化对象时转换为字节流存储或传输。 此外,需要注意字节流的常见问题,比如正确处理大文件时的缓冲区使用,避免内存溢出;读取或写入时的异常处理,如文件不存在或权限问题;以及跨平台时的字节序(大端小端)问题。 最后,根据用户提供的引用,特别是引用[2]中提到数据流像水流,最小单位是字节或字符,这支持了字节流作为基本单位的解释。同时,引用[4]中的示例虽然处理的是字符数组,但可以类比到字节数组的操作,比如循环处理和条件判断,不过字节流更关注二进制数据的处理。 总结回答结构:先定义字节流,对比字符流,再分语言举例使用方法,接着应用场景和注意事项,最后相关问题。确保使用正确的LaTeX格式和引用标记。</think>### 字节流(Byte Stream)的概念与使用方法 #### 1. **概念定义** 字节流是编程中处理二进制数据的基本单位,以**单个字节(8位)**为最小处理单元,范围是$0 \leq \text{值} \leq 255$。它直接操作原始二进制数据,适用于非文本文件(如图像、音频、视频)或网络数据传输[^2]。 #### 2. **字节流 vs 字符流** - **字节流**:直接处理原始字节,不涉及编码转换(如Java的`InputStream`/`OutputStream`)。 - **字符流**:基于字节流,但会结合字符编码(如UTF-8)将字节转换为字符(如Java的`Reader`/`Writer`),适用于文本处理。 #### 3. **使用方法(以常见语言为例)** ##### **Java** ```java // 读取文件字节流 try (FileInputStream fis = new FileInputStream("image.jpg")) { int byteData; while ((byteData = fis.read()) != -1) { // 处理每个字节(例如加密、复制) } } // 写入字节流 try (FileOutputStream fos = new FileOutputStream("copy.jpg")) { byte[] buffer = new byte[1024]; int bytesRead; while ((bytesRead = fis.read(buffer)) != -1) { fos.write(buffer, 0, bytesRead); } } ``` ##### **Python** ```python # 读取二进制文件 with open("data.bin", "rb") as f: byte_data = f.read() # 返回bytes对象 # 处理字节数据(如解析结构体) # 写入二进制数据 with open("output.bin", "wb") as f: f.write(b'\x48\x65\x6c\x6c\x6f') # 写入字节序列 ``` ##### **C++** ```cpp #include <fstream> using namespace std; // 读取二进制文件 ifstream input("file.bin", ios::binary); char buffer[1024]; input.read(buffer, sizeof(buffer)); // 写入二进制数据 ofstream output("output.bin", ios::binary); output.write(buffer, input.gcount()); ``` #### 4. **应用场景** - **文件传输**:如通过HTTP发送图片或视频的字节数据。 - **网络通信**:TCP协议基于字节流传输(如Socket编程)。 - **加密/压缩**:AES加密、ZIP压缩均直接操作字节流。 - **序列化**:将对象转换为字节流存储(如Protobuf协议)。 #### 5. **注意事项** - **缓冲区管理**:大文件需分块读取(如Java的`BufferedInputStream`),避免内存溢出。 - **编码问题**:若误用字节流处理文本,可能导致乱码(如未正确解析UTF-8多字节字符)。 - **字节序(Endianness)**:跨平台传输时需统一多字节数据的顺序(如`int`类型)。 --- ###
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值