C#编写的词法分析器 带可视化界面

【转载】写的C#编写的词法分析器

带有可视化界面     感谢原作者

用C或C++写一个简单的词法分析程序,程序可以满足下列要求:

1、能分析如下几种简单的语言词法

(1) 标识符: ID=letter(letter|digit)*

(2) 关键字(全部小写)

main int float double char if  then  else switch case break continue while do for

(3)整型常量:NUM=digit digit*

(4)运算符: = + - * / < <= == != > >= ; ( )? : 

(5)空格由空白、制表符和换行符组成,用以分隔ID、NUM、运算符等,字符分析时被忽略。

2、单词符号和相应的类别码

假定单词符号和相应的类别码如下:

单词符号 种别码 

int  1 =  17 float  2  <  20 if  3  <=  21

switch 4  ==  22 while  5  !=  23 Do  6  >  24

标识符 10  >=  25 整型常量 11  ;  26 +  13  (  27

-  14  )  28 *  15  ?  29 /  16  :  30

3、词法分析程序实现的功能

输入:单词序列(以文件形式提供),输出识别的单词的二元组序列到文件和屏幕

输出:二元组构成: (syn,token或sum)

其中: syn 为单词的种别码

token 为存放的单词自身符号串

sum 为整型常数

例:源程序: int ab; float ef=20;ab=10+ef;

输出:

(保留字--1,int) (标识符--10,ab) (分号--26,;)

(保留字--2,float) (标识符--10,ef) (等号--17,=)

(整数--11,20) (分号--26,;) (标识符--10,ab) 

(等号--17,=)  (整数--11,10) (加号--13,+)

(标识符--10,ef)  (分号--26,;)

4、自己准备测试数据存放于TestData.txt文件中,测试数据中应覆盖有以上5种数据,测试结果要求以原数据与结果对照的形式输出并保存在Result.txt中,同时要把结果输出到屏幕。

5、提前准备 

① 实验前,先编制好程序,上机时输入并调试程序。

② 准备好多组测试数据(存放于文件TestData.txt中)。

6、写出实验报告

报告格式:要求有实验名称、实验目的、实验要求、实验内容、实验小结。

其中实验内容包括算法分析、程序流程图及程序代码。


种别码

类型

名称

各别码

符号

名称

各别码

符号

运算符

取反

1

~

按位或

18

|

2

+

逻辑或

19

||

加等于

3

++

按位或等于

20

|=

加加

4

++

逻辑非

21

!

5

-

不等于

22

!=

减等于

6

-=

按位异或

23

^

减减

7

--

按位异或等于

24

^=

8

*

小于

25

<

乘等于

9

*=

小于等于

26

<=

条件

10

?:

左移

27

<<

11

/

左移等于

28

<<=

除等于

12

/=

大于

29

>

求余

13

%

大于等于

30

>=

求余等于

14

%=

右移

31

>>

按位与

15

&

右移等于

32

>>=

逻辑与

16

&&

等于

33

=

按位与等于

17

&=

恒等于

34

==

分隔符

左小括号

35

(

左大括号

41

{

右小括号

36

)

右大括号

42

}

左中括号

37

[

单引号

43

右中括号

38

]

点运算符

44

.

逗号

39

,

#

45

#

分号

40

;

错误代码

-1

----

关键字

关键字

46

----

标识符

标识符

47

l(l|d)*

数字

数字

48

d d*

错误代码

错误代码

-1

----

源代码


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO; 
namespace 词法分析器
{
    public partial class 词法分析器 : Form
    { 
        public string path1 = "", path2 = "";//路径1,路径2
        public 词法分析器()
        {
            InitializeComponent();
        } 
        private void ListViewer_Load(object sender, EventArgs e) //Load
        { 
            this.saveFiles.Enabled = false; //控件隐藏
            this.button1.Enabled = false;
            this.label1.Visible = false;
            this.label2.Visible = false;
            this.linkLabel1.Visible = false;
            this.linkLabel2.Visible = false;
        }
        //打开文件的函数
        private void openFiles_Click(object sender, EventArgs e)
        {
            openFileDialog1.InitialDirectory = "";
            openFileDialog1.Title = "请选择文件";
            openFileDialog1.FileName = "";
            openFileDialog1.Filter = "文本文档(*.txt)|*.txt";
            if (openFileDialog1.ShowDialog() != DialogResult.Cancel)
            {
                System.IO.StreamReader sr = new System.IO.StreamReader(openFileDialog1.FileName);
                this.textBox1.Text = sr.ReadToEnd();
                sr.Close();
                sr.Dispose();
            }
            this.label1.Visible = false; //控件隐藏
            this.label2.Visible = false;
            this.linkLabel1.Visible = false;
            this.linkLabel2.Visible = false;
        }
        //保存源文件的函数
        private void saveFiles_Click(object sender, EventArgs e)
        {
            string stringTemp = this.textBox1.Text;
            saveFileDialog1.Title = "请输入要保存的文件名";
            saveFileDialog1.Filter = "文本文档(*.txt)|*.txt";
            saveFileDialog1.OverwritePrompt = true;
            if (saveFileDialog1.ShowDialog() == DialogResult.OK)
            {
                path1 = saveFileDialog1.FileName;
                StreamWriter sw = new StreamWriter(path1);
                sw.Write(stringTemp);
                sw.Close();
                sw.Dispose();
                this.label1.Visible = true;
                this.linkLabel1.Visible = true;
             this.linkLabel1.Text = path1.Substring(path1.LastIndexOf("\\") + 1);
            }
            this.saveFiles.Enabled = false;
        } 
        private void button1_Click(object sender, EventArgs e) //保存结果
        {
            string[] stringTemp = new string[this.resultListBox1.Items.Count];
            saveFileDialog1.Title = "请输入要保存的文件名";
            saveFileDialog1.Filter = "文本文档(*.txt)|*.txt";
            saveFileDialog1.OverwritePrompt = true;
            if (saveFileDialog1.ShowDialog() == DialogResult.OK)
            {
                path2 = saveFileDialog1.FileName;
                StreamWriter sw = new StreamWriter(path2);
       for (int counter = 0; counter < this.resultListBox1.Items.Count; counter++)
                    sw.WriteLine(this.resultListBox1.Items[counter].ToString());
                sw.Close();
                sw.Dispose();
                this.label2.Visible = true;
                this.linkLabel2.Visible = true;
             this.linkLabel2.Text = path2.Substring(path2.LastIndexOf("\\") + 1);
            }
            this.button1.Enabled = false;
        } 
        private void save()//自动保存结果
        {
            try
            { 
                this.label1.Visible = true; //控件显示
                this.label2.Visible = true;
                this.linkLabel1.Visible = true;
                this.linkLabel2.Visible = true; 
                string stringTemp1 = this.textBox1.Text; //保存源文件
                string date = DateTime.Now.ToString("HH-mm-ff");
                string zipath1 = "TestData(" + date + ").txt";
                path1 = Application.StartupPath + "\\Text\\" + zipath1;
                StreamWriter sw1 = new StreamWriter(path1);
                sw1.Write(stringTemp1);
                this.linkLabel1.Text = zipath1;
                sw1.Close();
                sw1.Dispose(); 
                string zipath2 = "Result(" + date + ").txt";//保存分析结果
                path2 = Application.StartupPath + "\\Text\\" + zipath2;
                StreamWriter sw2 = new StreamWriter(path2);
       for (int counter = 0; counter < this.resultListBox1.Items.Count; counter++)
                   sw2.WriteLine(this.resultListBox1.Items[counter].ToString());
                this.linkLabel2.Text = zipath2;
                sw2.Close();
                sw2.Dispose();
            }
            catch 
            {
                MessageBox.Show("保存失败, 请手动保存!", "警告", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                this.saveFiles.Enabled = true;
                this.button1.Enabled = true; 
                this.label1.Visible = false; //控件显示
                this.label2.Visible = false;
                this.linkLabel1.Visible = false;
                this.linkLabel2.Visible = false;
            }
        } 
        //分析函数
        private void BeginToAnalyse_Click(object sender, EventArgs e)
        {
            if (MessageBox.Show("您要在分析完成后,自动保存分析结果吗?", "温馨提示", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
            {
                //判断列表是否为空,否则清空列表
                if (this.resultListBox1.Items.Count != 0)
                    this.resultListBox1.Items.Clear(); 
                char[] getch = textToCharArray();//字符数组。
                //将字符数组转换为词法分析的单词数组。
                charArrayToStringArray(getch);
                save();//自动保存
                this.saveFiles.Enabled = false;
                this.button1.Enabled = false;
            }
            else
            {
                //判断列表是否为空,否则清空列表
                if (this.resultListBox1.Items.Count != 0)
                    this.resultListBox1.Items.Clear(); 
                char[] getch = textToCharArray();//字符数组。
                //将字符数组转换为词法分析的单词数组。
                charArrayToStringArray(getch);
                this.saveFiles.Enabled = true;
                this.button1.Enabled = true;
            }
        } 
        private char[] textToCharArray()//将Text中的字符串,存入一个字符数组中。
        {
            string stringTemp;
            stringTemp = this.textBox1.Text; 
          char[] getch = stringTemp.ToCharArray();//要处理的字符都在getch这个数组中
            return getch;
        }
        // 字符数组 到 单词数组 //将字符数组转换为字符串数组。
        private void charArrayToStringArray(char[] getch)
        {
            int errorCounter = 0, rowCounter = 1;
            //用这个字符串数组存放词法分析后得到的单词。
            string[] stringArrange ={ "" };
            char charTemp = ' ', charTemp1 = ' ';
            //  存放一个分析得到的单词
            string stringSave = "";
            string[] keywords ={ "auto", "break", "case", "char", "const", "continue", "default", "do","double", "else","enum", "extern", "float", "for", "goto", "if", "int", "long", "register", "return", "short", "signed", "sizeof", "static","struct", "switch", "typedef", "union", "unsigned", "void", "volatile", "while" };//关键字32个(46) //标识符(47) //整数(48)
           string[] sign = { "~", "+", "+=", "++", "-", "-=", "--", "*", "*=", "?:", "/", "/=", "%", "%=", "&", "&&", "&=", "|", "||", "|=",  "!", "!=", "^", "^=", "<", "<=", "<<", "<<=", ">",">=",  ">>", ">>=", "=", "==" };//运算符34个(0-33)
            string[] fengefu = { "(", ")", "[", "]", ",", ";", "{", "}", "'", ".", "#" };//分隔符11个(34-45)
            string[] Num ={ "取反", "加", "加等于", "加加", "减", "减等于", "减减", "乘", "乘等于", "条件","除",  "除等于", "求余", "求余等于", "按位与", "逻辑与", "按位与等于", "按位或", "逻辑或", "按位或等于", "逻辑非","不等于", "按位异或", "按位异或等于", "小于","小于等于", "左移", "左移等于", "大于", "大于等于", "右移", "右移等于", "等于", "恒等于", "左小括号", "右小括号", "左中括号", "右中括号", "逗号", "分号", "左大括号", "右大括号", "单引号",  "点运算符", "#号",  "关键字", "标识符", "数字"};//错误所在行-1
            try//一次循环得到一个单词。
            {   
                if (getch.Length == 0)
                    MessageBox.Show("请输入要处理的程序代码......", "警告", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                else
                { 
                    this.resultListBox1.Items.Add("****************分析开始!");
                    this.resultListBox1.Items.Add("******************第 " + rowCounter + " 行");//打印第一行
                    //查找//l为标识符,d为数字,c为".",为1的时候
                     int l = 0, d = 0, c = 0;
                    for (int i = 0; i < getch.Length; )
                    {
                        charTemp = getch[i];
                        //标识符或者关键字或者数字
                        if (char.IsLetter(charTemp) || char.IsNumber(charTemp) || charTemp == '_' || charTemp == '.')
                        {
                            if (char.IsLetter(charTemp))//纯字母
                                l = 1;
                            else if (char.IsNumber(charTemp))//纯数字
                                d = 1;
                            else if (charTemp == '.')//小数点
                                c = 1;
                            int a = 0;
                            //统计标识符或者关键字或者数字的集合
                            stringSave = stringSave + charTemp.ToString();
                            try//如果下一字符不是同类
                            {
                                charTemp1 = getch[i + 1];
                            }
                            catch
                            {
                                a = 1;//处理结束
                            }
                            if (!(char.IsLetter(charTemp1) || char.IsNumber(charTemp1) || charTemp1 == '_' || charTemp1 == '.' ) || a == 1)
                            {
                                if (l == 1 && d == 0)//纯字母
                                {
                                    if (c == 0)//不含"."的字母
                                    { 
                                        int flag = 0; //关键字
                                        for (int t = 0; t < keywords.Length; t++)
                                        {
                                            string si = keywords[t];
                                            if (stringSave == si)
                                            {
                                                this.resultListBox1.Items.Add("关键字 , 46 , " + stringSave);
                                                flag = 1;
                                                break;
                                            }
                                            else
                                                flag = 0;
                                        }
                                        if (flag == 0)//标识符
                                            this.resultListBox1.Items.Add("标识符 , 47 , " + stringSave);
                                    }
                                    else if (c == 1)//含"."的字母
                                    {
                                        //标识符+.+标识符.
                                        string str1 = stringSave.Substring(0, stringSave.LastIndexOf("."));
                                        string str2 = stringSave.Substring(stringSave.LastIndexOf(".") + 1);
                                        this.resultListBox1.Items.Add("标识符 , 47 , " + str1);
                                        this.resultListBox1.Items.Add("点运算符 , 44 , .");
                                        this.resultListBox1.Items.Add("标识符 , 47 , " + str2);
                                    }
                                }//end if
                                else if (l == 0 && d == 1)//纯数字
                                    this.resultListBox1.Items.Add("数字  , 47 , " + stringSave);
                                else if (l == 1 && d == 1)//数字与字母的组合
                                {
                                    try//数字开头的标识符形如"12c"
                                    {
                                        int shu = Convert.ToInt32(stringSave.Substring(0, 1));
                                        if (shu >= 0 && shu <= 9)//打印错误信息
                                        {
                                            this.resultListBox1.Items.Add("无效标识符 , -1 , " + stringSave);
                                            errorCounter++;//计算错误个数
                                        }
                                        else
                                            this.resultListBox1.Items.Add("标识符 , 47 , " + stringSave);
                                    } 
                                    catch//不是以数字开头的标识符
                                    {
                                        if (c == 0)//无"."的
                                            this.resultListBox1.Items.Add("标识符 , 47 , " + stringSave);
                                        else if (c == 1)//有"."的
                                        {
                                            try//"."后面每一个字母是数字
                                            {
                                                int shu = Convert.ToInt32(stringSave.Substring(stringSave.LastIndexOf(".") + 1, 1));
                                                if (shu >= 0 && shu <= 9)//打印错误信息
                                                {
                                                    this.resultListBox1.Items.Add("无效标识符 , -1 , " + stringSave);
                                                    errorCounter++;//计算错误个数
                                                }
                                                else//标识符+.+标识符.
                                                {
                                                    string str1 = stringSave.Substring(0, stringSave.LastIndexOf("."));
                                                    string str2 = stringSave.Substring(stringSave.LastIndexOf(".") + 1);
                                                    this.resultListBox1.Items.Add("标识符 , 47 , " + str1);
                                                    this.resultListBox1.Items.Add("点运算符 , 44 , .");
                                                    this.resultListBox1.Items.Add("标识符 , 47 ," + str2);
                                                }   
                                            }
                                            catch //标识符+.+标识符.
                                            {
                                                string str1 = stringSave.Substring(0, stringSave.LastIndexOf("."));
                                                string str2 = stringSave.Substring(stringSave.LastIndexOf(".") + 1);
                                                this.resultListBox1.Items.Add("标识符 , 47 , " + str1);
                                                this.resultListBox1.Items.Add("点运算符 , 44 , .");
                                                this.resultListBox1.Items.Add("标识符 , 47 ," + str2);
                                            }//end catch
                                        }//end else if
                                    }//end catch
                                }//end else if
                                stringSave = "";//清空单词
                                l = d = c = 0;//清零
                            }//end if
                        }//end if 
                        else//运算符或者分隔符
                        {
                            int a = 0;
                            //统计运算符或者分隔符的集合
                            stringSave = stringSave + charTemp.ToString(); 
                            int flag = 0; //分隔符
                            //对照分隔符 打到则flag = 1;
                            for (int t = 0; t < fengefu.Length; t++)
                            {
                                string si = fengefu[t];
                                if (stringSave == si) //显示分隔符
                                { 
                                    this.resultListBox1.Items.Add(Num[t + 34] + " , " + (t + 34) + " , " + stringSave);
                                    flag = 1;
                                    break;
                                }
                                else
                                    flag = 0;
                            }
                            if (flag == 0)//不是分隔符
                            {
                                try//如果下一字符不是同类
                                {
                                    charTemp1 = getch[i + 1];
                                }
                                catch
                                {
                                    a = 1;//处理结束
                                }
                                string stringSave2 = charTemp1.ToString(); 
                                int flag2 = 0; //对照分隔符 ,查到则flag = 1;
                                for (int t = 0; t < fengefu.Length; t++)
                                {
                                    string si = fengefu[t];
                                    if (stringSave2 == si)
                                    {
                                        flag2 = 1;
                                        break;
                                    }
                                    else
                                        flag2 = 0;
                                }
                                //下一个字符不是同类则执行操作
                                if (char.IsLetter(charTemp1) || char.IsNumber(charTemp1) || charTemp1 == '_' || charTemp1 == '.' || flag2 == 1 || a == 1)
                                { 
                                    int flag1 = 0; //运算符
                                    //对照运算符 打到则flag = 1;
                                    for (int t = 0; t < sign.Length; t++)
                                    {
                                        string si = sign[t];
                                        if (stringSave == si)
                                        {
                                            //显示运算符
                                            this.resultListBox1.Items.Add(Num[t] + " , " + t + " , " + stringSave);
                                            flag1 = 1;
                                            break;
                                        }
                                        else
                                            flag1 = 0;
                                    }
                                    //不是运算符,则为分隔符
                                    if (flag1 == 0 && charTemp != ' ' && charTemp != '\n' && charTemp != '\r')
                                    {
                                        //打印错误信息
                                        this.resultListBox1.Items.Add("无效运算符 , -1 , " + stringSave);
                                        errorCounter++;//计算错误个数
                                    }
                                    stringSave = "";//清空单词
                                }//不是运算符,也不是分隔符,则跳过.
                            }
                            else
                                stringSave = "";//清空单词
                        } 
                        if (charTemp == '\n') //如果回车,则列表加一行
                        {
                            rowCounter++;//行数加1
                            this.resultListBox1.Items.Add("******************第 " + rowCounter + " 行");
                        }
                        //回车,换行符,跳过,并且清空单词
                       if (charTemp == '\n' || charTemp == '\r' || charTemp == ' ')
                            stringSave = "";
                        i = i + 1;//计数器加1
                        if (i == getch.Length)//循环结束,打印相应信息
                        {
                            this.resultListBox1.Items.Add("****************分析结束!");
                            if (errorCounter != 0)
                                MessageBox.Show(" 程序中有 " + errorCounter + " 个错误!", "警告", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                            else
                                MessageBox.Show("恭喜您! 程序没有错误!", "信息", MessageBoxButtons.OK, MessageBoxIcon.Information);
                        }
                    }//end for
                }//end else
            }//end try
            catch{}
        }
        //linkLabel1点击
        private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            System.Diagnostics.Process.Start(path1);
        }
        //linkLabel2点击
        private void linkLabel2_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            System.Diagnostics.Process.Start(path2);
        }
        //打开种别码对照表
        private void button2_Click(object sender, EventArgs e)
        {
            string path = Application.StartupPath + "\\Text\\种别码对照表.doc";
            System.Diagnostics.Process.Start(path);
        }
        //关闭文件
        private void close_Click(object sender, EventArgs e)
        {
            this.Close();
        }
    }
}


  • 3
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Java中可以使用JavaFX来编写界面词法分析器。下面是一个示例代码: ```java import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.TextArea; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; import javafx.stage.FileChooser; import javafx.stage.Stage; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.util.regex.Matcher; import java.util.regex.Pattern; public class LexicalAnalyzer extends Application { private final String KEYWORDS = "\\b(int|float|double|String|if|else|while|for|do|switch|case|break|continue|return|void)\\b"; private final String OPERATORS = "[\\+\\-\\*/%]"; private final String PUNCTUATION = "[\\{\\}\\(\\)\\[\\];,]"; private final String INT_CONST = "\\b\\d+\\b"; private final String FLOAT_CONST = "\\b\\d+\\.\\d+\\b"; private final String STRING_CONST = "\"[^\"]*\""; private final String IDENTIFIER = "\\b[A-Za-z_]\\w*\\b"; private TextArea textArea = new TextArea(); private TextArea resultArea = new TextArea(); @Override public void start(Stage primaryStage) { BorderPane borderPane = new BorderPane(); HBox hbox = new HBox(); Button btnOpen = new Button("Open"); Button btnAnalyze = new Button("Analyze"); hbox.getChildren().addAll(btnOpen, btnAnalyze); borderPane.setTop(hbox); borderPane.setLeft(textArea); borderPane.setRight(resultArea); Scene scene = new Scene(borderPane, 800, 600); primaryStage.setScene(scene); primaryStage.show(); btnOpen.setOnAction(e -> { FileChooser fileChooser = new FileChooser(); File file = fileChooser.showOpenDialog(primaryStage); if(file != null) { try { BufferedReader reader = new BufferedReader(new FileReader(file)); String line; while ((line = reader.readLine()) != null) { textArea.appendText(line + "\n"); } reader.close(); } catch (IOException ex) { ex.printStackTrace(); } } }); btnAnalyze.setOnAction(e -> { String text = textArea.getText(); String[] lines = text.split("\n"); StringBuilder resultBuilder = new StringBuilder(); Pattern pattern = Pattern.compile(KEYWORDS + "|" + OPERATORS + "|" + PUNCTUATION + "|" + INT_CONST + "|" + FLOAT_CONST + "|" + STRING_CONST + "|" + IDENTIFIER); for (String line : lines) { Matcher matcher = pattern.matcher(line); while (matcher.find()) { String token = matcher.group(); if (token.matches(KEYWORDS)) { resultBuilder.append("<Keyword, " + token + ">\n"); } else if (token.matches(OPERATORS)) { resultBuilder.append("<Operator, " + token + ">\n"); } else if (token.matches(PUNCTUATION)) { resultBuilder.append("<Punctuation, " + token + ">\n"); } else if (token.matches(INT_CONST)) { resultBuilder.append("<IntConst, " + token + ">\n"); } else if (token.matches(FLOAT_CONST)) { resultBuilder.append("<FloatConst, " + token + ">\n"); } else if (token.matches(STRING_CONST)) { resultBuilder.append("<StringConst, " + token + ">\n"); } else if (token.matches(IDENTIFIER)) { resultBuilder.append("<Identifier, " + token + ">\n"); } } } resultArea.setText(resultBuilder.toString()); }); } public static void main(String[] args) { launch(args); } } ``` 这个程序使用JavaFX来创建界面,其中包含一个文本框和一个按钮。用户可以在文本框中输入代码,点击按钮后程序将分析代码并在另一个文本框中输出结果。程序使用正则表达式来识别代码中的关键字、运算符、标点符号、常量和标识符,并将它们转换成相应的词法单元。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值