实现算术表达式的语法分析器

前言:本实验采用自上而下的方法实现算术表达式的语法分析器。只是实现了对加减乘数和带括号的语法分析,判断语法的正确性。

一 实验要求:

1)程序通过标准输入按行读取用户输入,表达式在1行内读完。

2)程序对用户输入的内容首先进行词法分析处理(可以复用实验一的部分代码,由于词法规则更简单,可以大大简化),词法分析得到的词法单位对应文法中的终结符。//代码太长直接就用词法分析的结果进行语法分析。

3)对于用户输入的表达式,如果经过分析后语法正确,给出相应提示。如果分析过程中遇到错误不需要尝试恢复分析,停止该次分析过程即可,但应尽量给出说明性较强的错误提示。

 

下面给出一些验证语法分析结果正确性的测试用例:

 

正确:a+3*( b + c/10) -4 +5


错误:a+3 ( b + c/10)


错误:(1+2)(3-4)


错误:(1+2

 二:假设词法分析已经正确的完成,只进行语法分析

比如以  a+3*( b + c/10) -4 +5为例,进行词法分析后得到的结果为

 1 (29,a)
 2 (12,+)
 3 (28,3)
 4 (14,*)
 5 (21,()
 6 (29,b)
 7 (12,+)
 8 (29,c)
 9 (15,/)
10 (28,10)
11 (22,))
12 (13,-)
13 (28,4)
14 (12,+)
15 (28,5)

注意,上面的结果第一个参数是种别码,种别码的声明如下(关于词法分析的过程可具体参考我之前写的词法分析 https://www.cnblogs.com/henuliulei/p/10597281.html),当然种别码可以自己声明。

 1 begin  1
 2 end  2
 3 if  3
 4 then 4
 5 while  5
 6 do  6
 7 const  7
 8 var  8
 9 call  9
10 procedure  10
11 odd  11
12 +  12
13 -  13
14 *  14
15 /  15
16 =  16
17 #  17
18 <  18
19 >  19
20 :=  20
21 (  21
22 )  22
23 ,  23
24 .  24
25 ;  25
26 (*多行注释*)    26
27 //单行注释  27
28 常数    28
29 标识符    29
View Code

输入:

  以上面的结果作为输入

输出:

  对于语法正确的表达式,报告“正确:“+”表达式”;
  对于语法错误的表达式,报告“语法错误”, 指出错误原因

设计思想
  递归下降分析法的原理是利用函数之间的递归调用来模拟语法树自上而下的构建过程。从根节点出发,自顶向下为输入串中寻找一个最左匹配序列,建立一棵语法树。在不含左递归和每个非终结符的所有候选终结首字符集都两两不相交条件下,我们就可能构造出一个不带回溯的自顶向下的分析程 序,这个分析程序是由一组递归过程(或函数)组成的,每个过程(或函数)对应文法的而一个非终结符。
代码:

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 ifstream infile("D:/Test/Test1.txt");
  4 string str;//string变量进行字符识别
  5 string sym; //指针
  6 
  7 void expressionAnalysis();//表达式分析
  8 void termAnaysis();//项分析
  9 void factorAnalysis();//因子分析
 10 int advance();
 11 
 12 int conterr=0;//记录错误
 13 int lpnum=0;//记录左括号
 14 int found;//提取字符串中指针的位置
 15 int flag=0;//记录往后移动一个指针SYM是否正确
 16 
 17 int advance(){//SYM的移动
 18     if(!getline(infile,str)){//从文件中提取字符
 19         return 0;
 20     }
 21     found=str.find(',',0);
 22     if(found==-1){//当为error的时候,没有‘,’
 23         conterr++;
 24         cout<<"语法错误 识别字符错误"<<endl;
 25         return -1;
 26     }
 27     sym=str.substr(1,found-1);
 28     //cout<<sym<<endl;
 29     return 1;
 30 }
 31 
 32 void factorAnalysis(){//识别分析标识符 
 33     if(sym=="29"||sym=="28"){//如果是标识符和无符号整数,指针就往后移动
 34         flag=advance();
 35         if(conterr){
 36             return;
 37         }
 38         if(lpnum==0&&sym=="22"){//
 39             conterr++;
 40             cout<<"语法错误 ')'不匹配"<<endl;
 41             return;
 42         }
 43     }
 44     else if(sym=="21"){//如果是左括号,就要接下来判断是否为表达式,指针往后移动
 45         lpnum++;
 46       //  cout<<lpnum<<endl;
 47         flag=advance();
 48         if(conterr){
 49             return;
 50         }
 51         if(flag==0){//当为最后一个标志的时候,若没有右括号匹配就错误
 52             conterr++;
 53             cout<<"语法错误 '('后缺少表达式"<<endl;
 54             return;
 55         }
 56         expressionAnalysis();
 57         if(conterr){
 58             return;
 59         }
 60         if(flag==0||sym!="22"){
 61             conterr++;
 62             cout<<"语法错误 表达式后面缺少')'"<<endl;
 63             return;
 64         }else{
 65             lpnum--;
 66             flag=advance();
 67             if(conterr){
 68                 return;
 69             }
 70             if(flag==0){
 71                 return;
 72             }
 73         }
 74     }else{
 75         cout<<"语法错误 因子首部不为<标识符>|| <无符号整数> ||'('"<<endl;
 76         conterr++;
 77         return;
 78     }
 79     return;
 80 }
 81     
 82 void termAnalysis(){//识别分析乘除符号 
 83     factorAnalysis();
 84     if(conterr){
 85         return;
 86     }
 87     while((sym=="14")||(sym=="15")){//当为'*'或'/'的时候,一直往后识别因子并循环
 88         flag=advance();
 89         if(conterr){
 90             return;
 91         }
 92         if(flag==0){
 93             conterr++;
 94             cout<<"语法错误 <乘除法运算符>后缺因子"<<endl;
 95             return;
 96         }
 97         if(conterr){
 98             return;
 99         }
100         factorAnalysis();
101         if(conterr){
102             return;
103         }
104     }
105     return;
106 }; 
107 
108 void expressionAnalysis(){//识别分析加减符号 
109     if(conterr){
110         return;
111     }
112     if((sym=="12")||(sym=="13")){//当为'+'或'-'的时候
113         flag=advance();
114         if(!conterr){
115             return;
116         }
117         if(flag==0){
118             cout<<"语法错误 <加减法运算符>后缺项"<<endl;
119             conterr++;
120             return;
121         }
122     }
123     termAnalysis();
124     if(conterr){
125         return;
126     }
127     while((sym=="12")||(sym=="13")){//当为'+'或'-'的时候,一直往后识别项并循环
128         flag=advance();
129         if(conterr){
130             return;
131         }
132         if(flag==0){
133             cout<<"语法错误 <加法运算符>后缺项"<<endl;
134             conterr++;
135             return;
136         }
137         termAnalysis();
138         if(conterr){
139             return;
140         }
141     }
142     return;
143 } 
144 
145 int main(){
146     flag=advance();
147     if(flag){
148         expressionAnalysis();
149     }
150     if(flag!=-1&&!conterr){
151         cout<<"语法正确"<<endl;
152     }
153     return 0;
154 }
View Code

 3:代码中先进行词法分析,再把词法分析的结果作为输入进行语法分析

本代码里的路径D:/Test/b.txt对应文件里放的是要词法分析的表达式,D:/Test/a.txt放的是种别码,程序会把词法分析的结果放到D:/Test/c.txt里面,再读到程序里面进行语法分析,把结果显示在屏幕上,代码里是上面的语法分析和之前写的词法分析的结合。

  1 // pL/0语言词法分析器
  2 #include<bits/stdc++.h>
  3 using namespace std;
  4 
  5 ifstream infile("D:/Test/c.txt");//词法分析的结果或语法分析的输入 
  6 string str;//string变量进行字符识别
  7 string sym; //指针
  8 
  9 void expressionAnalysis();//表达式分析
 10 void termAnaysis();//项分析
 11 void factorAnalysis();//因子分析
 12 int advance();
 13 
 14 int conterr=0;//记录错误
 15 int lpnum=0;//记录左括号
 16 int found;//提取字符串中指针的位置
 17 int flag=0;//记录往后移动一个指针SYM是否正确
 18 string s;//用来保存要分析的字符串 
 19 struct _2tup
 20 {
 21     string token;
 22     int id;
 23 };
 24 
 25 int advance(){//SYM的移动
 26     if(!getline(infile,str)){//从文件中提取字符
 27         return 0;
 28     }
 29     found=str.find(',',0);
 30     if(found==-1){//当为error的时候,没有‘,’
 31         conterr++;
 32         cout<<"语法错误 识别字符错误"<<endl;
 33         return -1;
 34     }
 35     sym=str.substr(1,found-1);
 36     //cout<<sym<<endl;
 37     return 1;
 38 }
 39 
 40 void factorAnalysis(){//识别分析标识符 
 41     if(sym=="29"||sym=="28"){//如果是标识符和无符号整数,指针就往后移动
 42         flag=advance();
 43         if(conterr){
 44             return;
 45         }
 46         if(lpnum==0&&sym=="22"){//
 47             conterr++;
 48             cout<<"语法错误 ')'不匹配"<<endl;
 49             return;
 50         }
 51     }
 52     else if(sym=="21"){//如果是左括号,就要接下来判断是否为表达式,指针往后移动
 53         lpnum++;
 54       //  cout<<lpnum<<endl;
 55         flag=advance();
 56         if(conterr){
 57             return;
 58         }
 59         if(flag==0){//当为最后一个标志的时候,若没有右括号匹配就错误
 60             conterr++;
 61             cout<<"语法错误 '('后缺少表达式"<<endl;
 62             return;
 63         }
 64         expressionAnalysis();
 65         if(conterr){
 66             return;
 67         }
 68         if(flag==0||sym!="22"){
 69             conterr++;
 70             cout<<"语法错误 表达式后面缺少')'"<<endl;
 71             return;
 72         }else{
 73             lpnum--;
 74             flag=advance();
 75             if(conterr){
 76                 return;
 77             }
 78             if(flag==0){
 79                 return;
 80             }
 81         }
 82     }else{
 83         cout<<"语法错误 因子首部不为<标识符>|| <无符号整数> ||'('"<<endl;
 84         conterr++;
 85         return;
 86     }
 87     return;
 88 }
 89     
 90 void termAnalysis(){//识别分析乘除符号 
 91     factorAnalysis();
 92     if(conterr){
 93         return;
 94     }
 95     while((sym=="14")||(sym=="15")){//当为'*'或'/'的时候,一直往后识别因子并循环
 96         flag=advance();
 97         if(conterr){
 98             return;
 99         }
100         if(flag==0){
101             conterr++;
102             cout<<"语法错误 <乘除法运算符>后缺因子"<<endl;
103             return;
104         }
105         if(conterr){
106             return;
107         }
108         factorAnalysis();
109         if(conterr){
110             return;
111         }
112     }
113     return;
114 }; 
115 
116 void expressionAnalysis(){//识别分析加减符号 
117     if(conterr){
118         return;
119     }
120     if((sym=="12")||(sym=="13")){//当为'+'或'-'的时候
121         flag=advance();
122         if(!conterr){
123             return;
124         }
125         if(flag==0){
126             cout<<"语法错误 <加减法运算符>后缺项"<<endl;
127             conterr++;
128             return;
129         }
130     }
131     termAnalysis();
132     if(conterr){
133         return;
134     }
135     while((sym=="12")||(sym=="13")){//当为'+'或'-'的时候,一直往后识别项并循环
136         flag=advance();
137         if(conterr){
138             return;
139         }
140         if(flag==0){
141             cout<<"语法错误 <加法运算符>后缺项"<<endl;
142             conterr++;
143             return;
144         }
145         termAnalysis();
146         if(conterr){
147             return;
148         }
149     }
150     return;
151 } 
152 
153 bool is_blank(char ch)
154 {
155     return ch == ' ' || ch == '    ';//空格或控制字符
156 }
157 bool gofor(char& ch, string::size_type& pos, const string& prog)//返回指定位置的字符
158 {
159     ++pos;
160     if (pos >= prog.size())
161     {
162         return false;
163     }
164     else
165     {
166         ch = prog[pos];
167         return true;
168     }
169 }
170 
171 _2tup scanner(const string& prog, string::size_type& pos, const map<string, int>& keys, int& row)
172 {
173     /*
174     if
175         标示符
176     else if
177         数字
178     else
179         符号
180     */
181     _2tup ret;
182     string token;
183     int id = 0;
184 
185     char ch;
186     ch = prog[pos];
187 
188     while(is_blank(ch))
189     {
190         ++pos;
191         ch = prog[pos];
192     }
193     // 判断标示符、关键字
194     if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_')
195     {
196         //保证读取一个单词
197         while((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_')
198         {
199             token += ch;//追加标示符、关键字
200             if (!gofor(ch, pos, prog))
201             {
202                 break;
203             }
204         }
205         // 这里先看做都是其他标示符
206         id = keys.size();
207 
208         // 验证是否是关键字
209         map<string, int>::const_iterator cit = keys.find(token);//根据string类型的token返回int类型的id赋值给cit
210         if (cit != keys.end())
211         {
212             id = cit->second;//此时是关键字,记录他的id
213         }
214     }
215     // 识别常数
216     else if ((ch >= '0' && ch <= '9') || ch == '.')
217     {
218         while (ch >= '0' && ch <= '9' || ch == '.')
219         {
220             token += ch;
221             if (!gofor(ch, pos, prog))
222             {
223                 break;
224             }
225         }
226         id = keys.size() - 1;
227         int dot_num = 0;
228         for (string::size_type i = 0; i != token.size(); ++i)
229         {
230             if (token[i] == '.')
231             {
232                 ++dot_num;
233             }
234         }
235         if (dot_num > 1)
236         {
237             id = -1;
238         }
239     }
240     else
241     {
242         map<string, int>::const_iterator cit;
243         switch (ch)
244         {
245         case '-': // - 操作符
246             token += ch;
247             if (gofor(ch, pos, prog))
248             {
249                 if (ch == '-' || ch == '=' || ch == '>') // -- 操作符
250                 {
251                     token += ch;
252                     gofor(ch, pos, prog);
253                 }
254             }
255             cit = keys.find(token);
256             if (cit != keys.end())
257             {
258                 id = cit->second;
259             }
260             break;
261         case ':':
262              token += ch;
263             if (gofor(ch, pos, prog))
264             {
265                 if (ch == '=') // -- 操作符
266                 {
267                     token += ch;
268                     gofor(ch, pos, prog);
269                 }
270             }
271             cit = keys.find(token);
272             if (cit != keys.end())
273             {
274                 id = cit->second;
275             }
276             break;
277 
278         case '=':
279             token += ch;
280             if (gofor(ch, pos, prog))
281             {
282                 if (ch == '=') // !% %= 操作符
283                 {
284                     token += ch;
285                     gofor(ch, pos, prog);
286                 }
287             }
288             cit = keys.find(token);
289             if (cit != keys.end())
290             {
291                 id = cit->second;
292             }
293             break;
294 
295         case '/': // / 操作符
296             token += ch;
297             if (gofor(ch, pos, prog))
298             {
299                 if (ch == '=') // /= 操作符
300                 {
301                     token += ch;
302                     gofor(ch, pos, prog);
303                 }
304                 else if (ch == '/') // 单行注释
305                 {
306                     token += ch;
307                     ++pos;
308                     while (pos < prog.size())
309                     {
310                         ch = prog[pos];
311                         if (ch == '\n')
312                         {
313                             break;
314                         }
315                         token += ch;
316                         ++pos;
317                     }
318                     if (pos >= prog.size())
319                     {
320                         ;
321                     }
322                     else
323                     {
324                         ;
325                     }
326                     id = keys.size() - 2;
327                     break;
328                 }
329                 else if (ch == '*') // 注释
330                 {
331                     token += ch;
332                     if (!gofor(ch, pos, prog))
333                     {
334                         token += "\n!!!注释错误!!!";
335                         id = -10;
336                         break;
337                     }
338                     if (pos + 1 >= prog.size())
339                     {
340                         token += ch;
341                         token += "\n!!!注释错误!!!";
342                         id = -10;
343                         break;
344                     }
345                     char xh = prog[pos + 1];
346                     while (ch != '*' || xh != '/')
347                     {
348                         token += ch;
349                         if (ch == '\n')
350                         {
351                             ++row;
352                         }
353                         //++pos;
354                         if (!gofor(ch, pos, prog))
355                         {
356                             token += "\n!!!注释错误!!!";
357                             id = -10;
358                             ret.token = token;
359                             ret.id    = id;
360                             return ret;
361                         }
362                         //ch = prog[pos];
363                         if (pos + 1 >= prog.size())
364                         {
365                             token += ch;
366                             token += "\n!!!注释错误!!!";
367                             id = -10;
368                             ret.token = token;
369                             ret.id    = id;
370                             return ret;
371                         }
372                         xh = prog[pos + 1];
373                     }
374                     token += ch;
375                     token += xh;
376                     pos += 2;
377                     ch = prog[pos];
378                     id = keys.size() - 2;
379                     break;
380                 }
381             }
382             cit = keys.find(token);
383             if (cit != keys.end())
384             {
385                 id = cit->second;
386             }
387             break;
388         case '+':
389             token += ch;
390             cit = keys.find(token);
391             if (cit != keys.end())
392             {
393                 id = cit->second;
394             }
395             gofor(ch, pos, prog);
396             break;
397 
398         case '<':
399             token += ch;
400             if (gofor(ch, pos, prog))
401             {
402                 if (ch == '<')
403                 {
404                     token += ch;
405                     if (gofor(ch, pos, prog))
406                     {
407                         if (ch == '=')
408                         {
409                             token += ch;
410                             gofor(ch, pos, prog);
411                         }
412                     }
413                 }
414                 else if (ch == '=')
415                 {
416                     token += ch;
417                     gofor(ch, pos, prog);
418                 }
419             }
420             cit = keys.find(token);
421             if (cit != keys.end())
422             {
423                 id = cit->second;
424             }
425             break;
426 
427         case '>':
428             token += ch;
429             if (gofor(ch, pos, prog))
430             {
431                 if (ch == '>')
432                 {
433                     token += ch;
434                     if (gofor(ch, pos, prog))
435                     {
436                         if (ch == '=')
437                         {
438                             token += ch;
439                             gofor(ch, pos, prog);
440                         }
441                     }
442                 }
443                 else if (ch == '=')
444                 {
445                     token += ch;
446                     gofor(ch, pos, prog);
447                 }
448             }
449             cit = keys.find(token);
450             if (cit != keys.end())
451             {
452                 id = cit->second;
453             }
454             break;
455          case '(': // / 操作符
456             token += ch;
457             if (gofor(ch, pos, prog))
458 
459             {
460                  if (ch == '*') // 注释
461                 {
462                     token += ch;
463                     if (!gofor(ch, pos, prog))
464                     {
465                         token += "\n!!!注释错误!!!";
466                         id = -10;
467                         break;
468                     }
469                     if (pos + 1 >= prog.size())
470                     {
471                         token += ch;
472                         token += "\n!!!注释错误!!!";
473                         id = -10;
474                         break;
475                     }
476                     char xh = prog[pos + 1];
477                     while (ch != '*' || xh != ')')
478                     {
479                         token += ch;
480                         if (ch == '\n')
481                         {
482                             ++row;
483                         }
484                         //++pos;
485                         if (!gofor(ch, pos, prog))
486                         {
487                             token += "\n!!!注释错误!!!";
488                             id = -10;
489                             ret.token = token;
490                             ret.id    = id;
491                             return ret;
492                         }
493                         //ch = prog[pos];
494                         if (pos + 1 >= prog.size())
495                         {
496                             token += ch;
497                             token += "\n!!!注释错误!!!";
498                             id = -10;
499                             ret.token = token;
500                             ret.id    = id;
501                             return ret;
502                         }
503                         xh = prog[pos + 1];
504                     }
505                     token += ch;
506                     token += xh;
507                     pos += 2;
508                     ch = prog[pos];
509                     id = keys.size() - 2;
510                     break;
511                 }
512             }
513             cit = keys.find(token);
514             if (cit != keys.end())
515             {
516                 id = cit->second;
517             }
518             break;
519 
520         case '*':
521             token += ch;
522             cit = keys.find(token);
523             if (cit != keys.end())
524             {
525                 id = cit->second;
526             }
527              gofor(ch, pos, prog);
528             break;
529 
530         case ',':
531         case ')':    
532         case '#':
533         case '.':
534         case ';':
535             token += ch;
536             gofor(ch, pos, prog);
537             //++pos;
538             //ch = prog[pos];
539             cit = keys.find(token);
540             if (cit != keys.end())
541             {
542                 id = cit->second;
543             }
544             break;
545 
546         case '\n':
547             token += "换行";
548             ++pos;
549             ch = prog[pos];
550             id = -2;
551             break;
552         default:
553             token += "错误";
554             ++pos;
555             ch = prog[pos];
556             id = -1;
557             break;
558         }
559     }
560     ret.token = token;
561     ret.id    = id;
562 
563     return ret;
564 }
565 
566 void init_keys(const string& file, map<string, int>& keys)//读取单词符号和种别码
567 {
568     ifstream fin(file.c_str());//.c_str返回的是当前字符串的首地址
569     if (!fin)
570     {
571         cerr << file << " doesn't exist!" << endl;//cerr不经过缓冲而直接输出,一般用于迅速输出出错信息
572       //  exit(1);
573     }
574     keys.clear();//清空map对象里面的内容
575     string line;
576     string key;
577     int id;
578     while (getline(fin, line))//这个函数接收两个参数:一个输入流对象和一个string对象,getline函数从输入流的下一行读取,并保存读取的内容到string中
579     {
580         istringstream sin(line);//istringstream sin(s);定义一个字符串输入流的对象sin,并调用sin的复制构造函数,将line中所包含的字符串放入sin 对象中!
581         sin >> key >> id;//读取里面的字符串每一行一个key id
582         keys[key] = id;
583     }
584 }
585 
586 void read_prog(const string& file, string& prog){//读取代码,并追加到prog上
587     ifstream fin(file.c_str());
588     if (!fin)
589     {
590         cerr << file << " error!" << endl;
591       //  exit(2);
592     }
593     prog.clear();
594     string line;
595     while (getline(fin, line))
596     {
597         prog += line + '\n';
598     }
599 }
600 
601 void cifafenxi()
602 {
603     map<string, int> keys;
604     init_keys("D:/Test/a.txt", keys);
605 
606     string prog;
607     read_prog("D:/Test/b.txt", prog);
608 
609     vector< _2tup > tups;
610     string token, id;
611 
612     string::size_type pos = 0;//size_type属于string标准库,作用可看做相当于unsigned·int
613     int row  = 1;
614 
615     _2tup tu;
616     int no = 0;
617     freopen("D:/Test/c.txt","w",stdout);
618     do
619     {
620         tu = scanner(prog, pos, keys, row);
621 
622         switch (tu.id)
623         {
624         case -1://返回的是错误
625             ++no;
626             cout << no << ": ";
627             cout << "Error in row" << row << "!" << '<' << tu.token<< "," << tu.id << '>' << endl;
628             tups.push_back(tu);
629             break;
630         case -2:
631             ++row;
632             // cout << '<' << tu.token<< "," << tu.id << '>' << endl;
633             break;
634         default:
635            
636                 s=prog;
637                 cout << '(' << tu.id<< "," << tu.token << ')' << endl;
638            
639             tups.push_back(tu);
640             break;
641         }
642     } while (pos < prog.size());
643     
644 }
645 
646 yufafenxi()//语法分析 
647 {
648     freopen("CON", "w", stdout);//结果在控制台上输出 
649      flag=advance();
650     if(flag){
651         expressionAnalysis();
652     }
653     if(flag!=-1&&!conterr){
654         cout<<"正确:"<<s<<endl;
655     }
656 
657 }
658 int main()
659 {
660     cifafenxi();
661     yufafenxi();
662     return 0;
663 }
View Code

效果如下:

referencehttps://blog.csdn.net/Flamewaker/article/details/82899466

注:本文里用到了freopen函数的句柄,该句柄作用是当不想输入或输出到文件了,要恢复句柄,可以重新打开标准控制台设备文件,这个设备文件的名字是与操作系统相关:

DOS/Win:  freopen("CON", "r", stdin);
            freopen("CON", "w", stdout);

 在linux中,控制台设备是 /dev/console:freopen("/dev/console", "r", stdin)

转载于:https://www.cnblogs.com/henuliulei/p/10753635.html

  • 0
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值