基于Java语言的语法分析器

LR(0)语法分析器

  1. 实验目的:

根据LR(0)语法分析的原理,编写、调试一个语法分析代码程序,对输入的句子进行分析。

  1. 实验工具:

使用了Java语言进行编写 ,使用Java开发工具IntelliJ IDEA 。

  1. 实验过程分析:

3.1、进行对LR(0)语法分析过程的深入了解。首先我们需要在文件data. txt中保存我们想要分析的文法。(这里展示的是课本例题6.1)

3.2、利用Java文件读取类进行读取文件data. txt(如下伪代码)
BufferedReader bf = new BufferedReader(new FileReader(“这里输入文件地址”));
然后使用bf.readLine()就可以按行读取出文件中的句子。

String p = "";

while((p = bf.readLine()) != null){…要进行的操作…}

3.3、读取之后,使用数据结构数组保存非总结符和总结符。

结果输出:

3.4、同时利用链表left 和 right 保存文法的左右两部分,比如S’->S

Left链表就保存 S’,right链表就保存S。

3.5、之后,我们需要输出所有的加黑点的文法,这一步比较简单,直接遍历left和right ,然后根据right的长度进行移动黑点,再输出即可。

3.6、在这之后,我们需要构造出所有的状态来,来构造DFA,为此,我专门自定义了一个类,叫做DFA ,具体属性如图所示,可以完美表示单个状态,然后我们使用List<DFA>ok 用来保存整个状态连接图!

3.7、在对于状态图的构建上,我们的思路是,先把文法拓展一下,放入 S’->S 进入第1个状态,也就是T0,然后根据黑点的右部首个字符,如果他属于非终结符,意味着我们要拓展这个状态!这里拓展状态的算法是使用了递归回溯算法。

最终在不懈的努力下,完成了各个状态的保存,以及状态的转移:

 

3.8、接下来我们便需要构建LR(0)分析表了,我们知道LR(0)分析表包含了Action 表 和 Goto表,这里保存他们需要选择好的数据结构来保存。思来想去,最后还是选择了HashMap内置HashMap来保存数据。

我们根据所有的状态来进行遍历,同时在内部遍历一下终结符和非终结符的状态转移,也就是每个DFA对象的next中的数据,就可以将其保存。然后再输出时候,使用System.out.printf(“%-ns”); 来进行n位缩进,这样子输出来更好看。

3.9、最后,我们使用了while()循环输入一段句子,在句子最后判断有无#,加上它,然后进行语法分析。如果再分析过程,我们的HashMap中对应的值为空了,说明分析不对。具体情况如下:

如果输入的句子分析正确,我们就开始输出啦,对于数据的保存,我们又自定义了一个类,然后再对这个类进行对象数组的构建,用来保存一整个表:

 4、实验结论:

此次实验,其实原理并不困难,难的是,将原理用代码展示出来,我们需要选择合适的数据结构还有算法才可以进行,这也应证了那句“程序 = 数据结构 + 算法“,总之,通过此次实验,我们学到了很多语法分析的知识,对于其分析过程有了很深入的了解。

5、最终效果:

6、最终代码:

import java.io.BufferedReader;
import java.io.FileReader;
import java.util.*;
import java.util.logging.Level;

public class Main {
    public static List<String> left;
    public static List<String> right;
    //产生式 比如 :A -> a
    //构造dfa
    public static List<DFA> ok; //已经搞好的dfa

    public static Map<Integer,Map<Character,String>>Action;
    public static Map<Integer,Map<Character,Integer>>Goto;

    public static Map<String,Integer> R; //第几条产生式规约

    public static boolean is[];
    public static char ZhongJie[]; //保存终结符
    public static char FeiZhongJie[]; //保存非终结符
    public static int ZhongJieNum = 0;
    public static int FeiZhongJieNum = 0;
    public static class DFA{
        Integer id;
        List<String> DFAleft;
        List<String> DFAright;
        Map<Character,Integer> next;

        public DFA(){

        }
        public DFA(Integer id, List<String> DFAleft, List<String> DFAright, Map<Character, Integer> next) {
            this.id = id;
            this.DFAleft = DFAleft;
            this.DFAright = DFAright;
            this.next = next;
        }

        public Integer getId() {
            return id;
        }

        public void setId(Integer id) {
            this.id = id;
        }

        public List<String> getDFAleft() {
            return DFAleft;
        }

        public void setDFAleft(List<String> DFAleft) {
            this.DFAleft = DFAleft;
        }

        public List<String> getDFAright() {
            return DFAright;
        }

        public void setDFAright(List<String> DFAright) {
            this.DFAright = DFAright;
        }

        public Map<Character, Integer> getNext() {
            return next;
        }

        public void setNext(Map<Character, Integer> next) {
            this.next = next;
        }
    }

    //打开文件,将所有的产生式存进List数据结构
    public static void OpenFile() throws Exception{
        left.add("S'");
        right.add("S");
        String property = System.getProperty("user.dir");
        BufferedReader bf = new BufferedReader(new FileReader(property+"\\data.txt"));
        String p = "";
        while((p = bf.readLine()) != null){
            for(int k = 0;k<p.length();k++){
                char ch = p.charAt(k);
                if(is[ch]){
                    continue;
                }
                if(ch >= 'A' && ch <= 'Z'){ //非终结符
                    FeiZhongJie[FeiZhongJieNum++] = ch;
                }
                else { //终结符
                    if(ch != '-' && ch != '>')
                    ZhongJie[ZhongJieNum++] = ch;
                }
                is[ch] = true;
            }
            String a[] = p.split("->");
            String b[] = a[1].split("\\|");
            for(int i = 0;i<b.length;i++){
                left.add(a[0]);
                right.add(b[i]);
            }
        }
        bf.close();
    }

    //在头部加点
    public static String addFirstPoint(String str){
        return "."+str;
    }

    public static int getPointPosition(String str){
        return str.indexOf(".");
    }

    //往右边移动点
    public static String movePoint(String str){
        if(getPointPosition(str) != str.length()-1){
            String[] a = str.split("\\.");
            String newStr  = a[0]+a[1].charAt(0)+"."+a[1].substring(1);
            return newStr;
        }
        return str;
    }
    //
    public static void OutWenFa() throws Exception{
        R = new HashMap<>();
        OpenFile();
        for(int i  = 0;i<left.size();i++){
            String s = left.get(i) + "->" + right.get(i);
            R.put(s,i);
            System.out.println(s);
        }
        System.out.println("----------------------------------------------------------------");
    }

    public static void OutJiaDianWenFa(){
        for(int i = 0;i<left.size();i++){
            String a1 = left.get(i);
            String a2 = addFirstPoint(right.get(i));
            while(getPointPosition(a2) != a2.length()-1){
                System.out.println(a1+"->" + a2);
                a2 = movePoint(a2);
            }
            System.out.println(a1+"->" + a2);
        }
        System.out.println("----------------------------------------------------------------");
    }


    //扩展非终结符X的项目
    public static boolean isok[] = new boolean[1001];
    public static void soloveTuoZhanDFA(List<String> le , List<String>ri ,String X){
        int size = left.size();
        for(int i = 0;i<size;i++){
            if(X.equals(left.get(i))){ // 如果
                le.add(left.get(i)); // 添加左边的
                ri.add(addFirstPoint(right.get(i)));//添加右边的
                if(right.get(i).charAt(0) >= 'A' && right.get(i).charAt(0) <= 'Z' && !isok[right.get(i).charAt(0)]){
                    String nextStr = ""+right.get(i).charAt(0);
                    isok[right.get(i).charAt(0)] = true;
                    soloveTuoZhanDFA(le,ri,nextStr);
                }
            }
        }
    }

    public static void TuozhanDFA(DFA dfa){  //拓展dfa
        List<String> dfAright = dfa.getDFAright();
        int lenth = dfAright.size();
        List<String> dfaLeft = new LinkedList<>();
        List<String> dfaRight = new LinkedList<>();
        for(int l = 0;l<lenth;l++){
            String rg = dfAright.get(l);
            int len = rg.length();
            int pos = getPointPosition(rg);
            if(pos < len-1 && rg.charAt(pos+1) >= 'A' && rg.charAt(pos+1) <= 'Z'){
                String Arg = ""+rg.charAt(pos+1);
                Arrays.fill(isok,false);
                isok[Arg.charAt(0)] = true;
                soloveTuoZhanDFA(dfaLeft,dfaRight,Arg);
                int sz = dfaLeft.size();
                for(int i = 0;i<sz;i++){
                    dfa.getDFAleft().add(dfaLeft.get(i));
                    dfa.getDFAright().add(dfaRight.get(i));
                }
            }
        }
    }
// 状态
    public static void OutZhuangTai(){
        //先新建一个S'->S吧。。。、
        List<String> firstLeft = new LinkedList<>();
        List<String> firstRight = new LinkedList<>();
        firstLeft.add("S'");
        firstRight.add(addFirstPoint("S"));
        DFA dfa = new DFA();
        dfa.setDFAleft(firstLeft);
        dfa.setDFAright(firstRight);
        ok.add(dfa);
        TuozhanDFA(dfa);
    }

    public static int QuChuChongFu(){ //去除重复
        int sz = ok.size();
        for(int i  =0;i<sz-1;i++){
            if(ok.get(i).getDFAleft().get(0).equals(ok.get(sz-1).getDFAleft().get(0)) &&
                    ok.get(i).getDFAright().get(0).equals(ok.get(sz-1).getDFAright().get(0))){
                ok.remove(sz-1);
                return i;
            }
        }
        return -1;
    }

    public static void createNewDFA(String le,String ri){ //创建新的状态
        DFA dfa = new DFA();
        ri = movePoint(ri);
        dfa.setId(ok.size());
        List<String> LEFT = dfa.getDFAleft() == null ? new LinkedList<>() : dfa.getDFAleft();
        List<String> RIGHT = dfa.getDFAright() == null ? new LinkedList<>() : dfa.getDFAright();
        LEFT.add(le);
        RIGHT.add(ri);
        dfa.setDFAleft(LEFT);
        dfa.setDFAright(RIGHT);
        ok.add(dfa);

    }
    public static boolean Exist[] = new boolean[1001];
    public static void AddNewZhuangTai(){
        for(int i = 0;i<ok.size();i++){
            Arrays.fill(Exist,false);
            for(int j = 0;j<ok.get(i).getDFAleft().size();j++){
                String riStr = ok.get(i).getDFAright().get(j);
                if(riStr.indexOf(".") == riStr.length()-1){
                    break;//不可以移动黑点
                }
                int pointPosition = getPointPosition(riStr);
                char c = riStr.charAt(pointPosition+1);
                if(Exist[c]){
                    continue;
                }
                createNewDFA(ok.get(i).getDFAleft().get(j),ok.get(i).getDFAright().get(j));
                Exist[c] = true;
                for(int k = j+1;k<ok.get(i).getDFAleft().size();k++){
                    String s = ok.get(i).getDFAright().get(k);
                    if(s.indexOf(".") == s.length()-1){
                        continue;
                    }
                    int pointPosition1 = getPointPosition(s);
                    if(s.charAt(pointPosition1+1) == c){
                        String s1 = ok.get(i).getDFAleft().get(k);
                        String s2 = ok.get(i).getDFAright().get(k);
                        s2 = movePoint(s2);
                        ok.get(ok.size()-1).getDFAleft().add(s1);
                        ok.get(ok.size()-1).getDFAright().add(s2);
                        System.out.println(s1+"===>"+s2);
                    }
                }
                int chong = QuChuChongFu();
                if(chong == -1){
                    TuozhanDFA(ok.get(ok.size()-1));///用第一行拓展
                    int dotpos = getPointPosition(riStr);
                    Map<Character,Integer> map = ok.get(i).getNext() == null ? new HashMap<>() : ok.get(i).getNext();
                    map.put(riStr.charAt(dotpos+1) , ok.size()-1);
                    ok.get(i).setNext(map);
                }
                else{
                    int dotpos = getPointPosition(riStr);
                    Map<Character,Integer> map = ok.get(i).getNext() == null ? new HashMap<>() : ok.get(i).getNext();
                    map.put(riStr.charAt(dotpos+1) , chong);
                    ok.get(i).setNext(map);
                }
            }
        }
    }
    public static void PrintZhuangTai(){
        System.out.println("--------所有的状态--------");
        int size = ok.size();
        for(int i = 0;i<size;i++){
            System.out.println("I"+i+":");
            for(int j = 0;j<ok.get(i).getDFAleft().size();j++){
                System.out.println(ok.get(i).getDFAleft().get(j) + " -> " + ok.get(i).getDFAright().get(j));
            }
            System.out.println("-------------------");
        }
    }

    public static void OutBiaoLine(){ //输出表的行线
        System.out.print("-------");
        for(int i = 0;i<FeiZhongJieNum+ZhongJieNum;i++){
            System.out.print("-------");
        }
        System.out.println("-");
    }
    public static void PrintShu(){
        System.out.print("|");
    }
    public static void PrintBiao(){
        OutBiaoLine();//输出表的行线
        PrintShu();
        System.out.printf("%-5s","  状态");
        for(int i = 0;i<ZhongJieNum;i++){
            PrintShu();
            System.out.printf("%-6s","  "+ZhongJie[i]);
        }
        for(int i = 0;i<FeiZhongJieNum;i++){
            PrintShu();
            System.out.printf("%-6s","  "+FeiZhongJie[i]);
        }
        System.out.println("|");

//        for(int i = 0;i<ok.size();i++){ // 测试输出Action
//            Map<Character, String> characterStringMap = Action.get(i);
//            for(int j = 0;j<ZhongJieNum;j++){
//                char ch = ZhongJie[j];
//                String s = characterStringMap.get(ch);
//                System.out.print(s+" ");
//            }
//            System.out.println();
//        }
//        System.out.println("Goto");
//        for(int i = 0;i<ok.size();i++){ //测试输出Goto
//            Map<Character, Integer> characterStringMap = Goto.get(i);
//            for(int j = 0;j<FeiZhongJieNum;j++){
//                char ch = FeiZhongJie[j];
//                int s = characterStringMap.get(ch);
//                System.out.print(s+" ");
//            }
//            System.out.println();
//        }
        for(int i = 0;i<ok.size();i++){
            OutBiaoLine();
            PrintShu();
            System.out.printf("%-6s","  "+i);
            for(int j = 0;j<ZhongJieNum;j++){
                char ch = ZhongJie[j];
                if(Action.get(i).get(ch).equals("-1")){
                    PrintShu();
                    System.out.print("      ");
                }
                else{
                    PrintShu();
                    System.out.printf("%-6s","  "+Action.get(i).get(ch));
                }
            }
            for(int j = 0;j<FeiZhongJieNum;j++){
                char ch = FeiZhongJie[j];
                if(Goto.get(i).get(ch) != -1){
                    PrintShu();
                    System.out.printf("%-6s","  "+Goto.get(i).get(ch));
                }
                else{
                    PrintShu();
                    System.out.print("      ");
                }
            }
            System.out.println("|");
        }
        OutBiaoLine();
    }


    public static void GouJianACTION(){ //构造ACTION表
        Action = new HashMap<>();
        for(int i = 0;i<ok.size();i++){
            Map<Character,String> ac = new HashMap<>();
            for(int j = 0;j<ZhongJieNum;j++){
                char ch = ZhongJie[j];
                if(i == 1){
                    if(ch == '#'){
                        ac.put(ch,"acc");
                    }
                    else{
                        ac.put(ch,"-1");
                    }
                    continue;
                }
                if(ok.get(i).getNext() == null){
                    String s = ""+ok.get(i).getDFAleft().get(0)+"->"+ok.get(i).getDFAright().get(0);
                    s = s.substring(0,s.length()-1);
                    ac.put(ch,"r"+R.get(s));
                    continue;
                }
                if(ok.get(i).getNext().containsKey(ch)){
                    ac.put(ch,"s"+ok.get(i).getNext().get(ch));
                }
                else{
                    ac.put(ch,"-1");
                }
            }
            Action.put(i,ac);
        }
    }
    public static void GouJianGoto(){//构建Goto表
        Goto = new HashMap<>(); // Map<Integer,Map<Character,Integer>>Goto;
        for(int i = 0;i<ok.size();i++){
            Map<Character,Integer> ac = new HashMap<>();
            for(int j = 0;j<FeiZhongJieNum;j++){
                char ch = FeiZhongJie[j];
                if(ok.get(i).getNext() == null || ok.get(i).getNext().get(ch) == null){
                    ac.put(ch,-1);
                    continue;
                }
                Integer integer = ok.get(i).getNext().get(ch);
                ac.put(ch,integer);
            }
            Goto.put(i,ac);
        }

    }
    public static void OutZhong(){ //输出终结符和非终结符....
        System.out.print("非终结符:");
        for(int i = 0;i<FeiZhongJieNum;i++){
            System.out.print(FeiZhongJie[i]+" ");
        }
        System.out.println();
        System.out.print("终结符:");
        ZhongJie[ZhongJieNum++] = '#';
        for(int i = 0;i<ZhongJieNum;i++){
            System.out.print(ZhongJie[i]+" ");
        }
        System.out.println();
    }
    public static void PrintZhuangTaiZhuanYi(){
        for(int i = 0;i<ok.size();i++){
            Map<Character, Integer> next = ok.get(i).getNext();
            if(next != null){
                for(Character key : next.keySet()){
                    Integer to = next.get(key);
                    System.out.println("状态T"+i+"----"+key+"------>状态T"+to);
                }
            }
        }
    }

    public static class GuoCheng{
        String ZhuanTai;
        String FuHao;
        String ShuRuChuan;
        String Action;
        Integer goTo;

        public GuoCheng() {
        }

        public String getZhuanTai() {
            return ZhuanTai;
        }

        public void setZhuanTai(String zhuanTai) {
            ZhuanTai = zhuanTai;
        }

        public String getFuHao() {
            return FuHao;
        }

        public void setFuHao(String fuHao) {
            FuHao = fuHao;
        }

        public String getShuRuChuan() {
            return ShuRuChuan;
        }

        public void setShuRuChuan(String shuRuChuan) {
            ShuRuChuan = shuRuChuan;
        }

        public String getAction() {
            return Action;
        }

        public void setAction(String action) {
            Action = action;
        }

        public Integer getGoTo() {
            return goTo;
        }

        public void setGoTo(Integer goTo) {
            this.goTo = goTo;
        }
    }

    public static String StackToString(Stack st){
        String s = st.toString();
        String[] split = s.substring(1, s.length() - 1).split(", ");
        String ans = "";
        for(int i = split.length-1;i>=0;i--){
            ans+=split[i];
        }
        return ans;
    }

    public static void find(String str){ //对输入串的分析
        Stack<Character> Shuruchuan =  new Stack<>();//这是输入串的栈 abb#
        GuoCheng mp[] = new GuoCheng[1001]; //穿它个1000个
        int num = 0;
        Stack<Integer> ZhuangTai = new Stack<>(); //这是状态 0345....
        Stack<Character> Fuhao =  new Stack<>();//这是符号的栈
        for(int i = str.length()-1;i>=0;i--){
            Shuruchuan.add(str.charAt(i)); //加进去
        }
        ZhuangTai.add(0);
        Fuhao.add('#');
        while(true){
            mp[num] = new GuoCheng();
            StringBuffer sf = new StringBuffer(StackToString(ZhuangTai));
            mp[num].setZhuanTai(sf.reverse().toString());//状态放进入输出
            sf = new StringBuffer(StackToString(Fuhao));
            mp[num].setFuHao(sf.reverse().toString());//符号放进入输出
            mp[num].setShuRuChuan(StackToString(Shuruchuan));//符号放进入输出
            char ch = Shuruchuan.peek(); //取出首个字符
            int pi = ZhuangTai.peek(); //取出栈顶的状态
            Map<Character, String> map = Action.get(pi);
            String s = map.get(ch);
            if(s.equals("-1")){
                System.out.println("输入串不符合LR(0)的规则,请重新输入...");
                return ;
            }
            mp[num].setAction(s);
            if(s.charAt(0) == 's'){
                int Jin = s.charAt(1)-'0';
                Shuruchuan.pop();
                ZhuangTai.add(Jin);
                Fuhao.add(ch);
            }
            else if(s.charAt(0) == 'r'){
                int Jin = s.charAt(1) - '0';
                String s1 = left.get(Jin);
                String s2 = right.get(Jin);
                for(int k = 0;k<s2.length();k++){
                    ZhuangTai.pop();
                    Fuhao.pop();
                }
                ch = s1.charAt(0);
                Jin = ZhuangTai.peek();
                Map<Character, Integer> map1 = Goto.get(Jin);
                Jin = map1.get(ch);
                ZhuangTai.add(Jin);
                Fuhao.add(ch);
                mp[num].setGoTo(Jin);
            }
            num++;
            if(mp[num-1].getAction() .equals("acc")){
                break;
            }
        }
        System.out.println("---------字符串"+str+"的分析过程:---------");
        PrintLine();
        System.out.printf("%-14s","|    步骤");
        System.out.printf("%-13s","|    状态栈");
        System.out.printf("%-13s","|    符号栈");
        System.out.printf("%-13s","|    输入串");
        System.out.printf("%-16s","|    ACTION");
        System.out.printf("%-16s","|    GOTO");
        System.out.println("|");
        PrintLine();
        for(int i = 0;i<num;i++){
            String kk = "|    ";
            System.out.printf("%-16s",kk+i);
            System.out.printf("%-16s",kk+mp[i].getZhuanTai());
            System.out.printf("%-16s",kk+mp[i].getFuHao());
            System.out.printf("%-16s",kk+mp[i].getShuRuChuan());
            System.out.printf("%-16s",kk+mp[i].getAction());
            System.out.printf("%-16s",kk+mp[i].getGoTo());
            System.out.println("|");
            PrintLine();
        }
    }

    public static void PrintLine(){
        for(int i = 0;i< 6;i++){
            System.out.print("----------------");
        }
        System.out.println();
    }

    public static String add(String str){
        if(str.charAt(str.length()-1) != '#'){
            str+="#";
        }
        return str;
    }
    public static String str;
    public static void In(){
        Scanner cin = new Scanner(System.in);
        while(true){
            System.out.print("请输入待分析的字符串:");
            str = cin.next();
            if(str.equals("stop")){
                break;
            }
            str = add(str);
            find(str);
        }
    }

    public static void main(String[] args) throws Exception {
        System.out.println("--------------制作人----------------");
        System.out.println("|   姓名   |    学号    |  班级     |");
        System.out.println("|  吴小明  | 20206111x7 |  20计算机 |");
        System.out.println("|  温小明  | 20206111x6 |  20计算机 |");
        System.out.println("|  谢小明  | 20206111x9 |  20计算机 |");
        System.out.println("|  王小明  | 20206111x5 |  20计算机 |");
        System.out.println("|  张小明  | 20206111x7 |  20计算机 |");
        System.out.println("-----------------------------------");
        System.out.println("本实验以例题6.1作报告。当然,只需要改变data.txt中的文法就可以实现任何LR(0)语法分析!");
        left = new LinkedList<>();
        right = new LinkedList<>();
        ok = new LinkedList<>();
        is = new boolean[1001];
        ZhongJie = new char[1001];
        FeiZhongJie = new char[1001];
//        System.out.println("Hello world!");
        System.out.println("拓广文法为:");
        OutWenFa();
        System.out.println("终结符与非终结符:");
        OutZhong();
        System.out.println("加点的文法:");
        OutJiaDianWenFa();

        //解决状态
        OutZhuangTai();
//        添加新的状态
        AddNewZhuangTai();
        //输出所有的状态
        PrintZhuangTai();

        System.out.println("--------------状态转移-----------------");
        PrintZhuangTaiZhuanYi();

        //累寄了,终于可以开始建表了.......
        System.out.println("              LR(0)分析表:");
        GouJianACTION();//构建action表
        GouJianGoto(); //构建Goto表
        //输出分析表
        PrintBiao();
        //解决分析过程!!完结撒花,嘿嘿嘿
        In();
    }
}

需要源文件的可以联系我

  • 11
    点赞
  • 58
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

stu_kk

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值