① 练习描述
写一个检查圆括号()、方括号[]、花括号{}不匹配的初级错误的程序,不要忘记单双引号、转义序列、注释。(要想完全通用,程序将变得困难)
② 思路
1.首先要想办法标记一下导入的文本,用int c, s, line, para来标记在字符''、字符串""、行注释//、段注释/* */中,1为在其中,0为在其外。借助练习1-23的思路即可,即通过识别开头和结尾的字符来标记状态。我们无需讨论这些状态中的括号,!c && !s && !line && !para表示不在这些状态中,即正常文本内。
2.可将问题分为检查错误和显示错误两部分。
用row和col记录当前的位置,用二维数组errorinfo来记录错误出现的行列位置和错误类型,0、1、2分别为圆括号()、方括号[]、花括号{}不匹配。
3.检查错误:
以圆括号()为例子,用state作依据检查,初值为0。遇到'(',state++,遇到')',state--。正常情况下,state只可能有0或1两个值。
当遇到单独的')',state = -1,记录此时坐标(row, col)及错误类型到errorinfo里,然后state = 0归零;
当state = 2,即碰到( ( 连着来怎么办呢?我采取的办法是,用last记录上一次state = 1时的位置,即第一个( ,当state增加到2时,说明第一个( 不匹配,那么记录last到errorinfo里,然后state = 1重置为1,这样连着三个( ( ( 也能处理。
当读取结束EOF后,如果state = 1,说明仍有一个未匹配的括号,把last记录到errorinfo即可。
把state定义为state[3]就能同时存三种错误的state了,last也是last[3]。
4.显示错误:
由于errorinfo中记录的位置一般不是从小到大顺序排列的,所以先按行列排个序,然后显示出来。
5.不足:
如果有int main(){
}
}
会把最后一行}当作错误。
#include<stdio.h>
#include<stdlib.h>
#define PARENTHESE_UNBALANCED 0 // error of unbalanced parentheses code: 0
#define BRACKET_UNBALANCED 1 // error of unbalanced brackets code: 1
#define BRACE_UNBALANCED 2 // error of unbalanced braces code: 2
#define MAXERROR 100 // max number of errors
void sort(int s[MAXERROR][3], int len);
void errorprint(int row, int col, int errortype);
int main(void){
char ch; // current character
int i, j;
int errorinfo[MAXERROR][3]; // save the info of errors, [][0] & [][1] mean position, [][2] means type of error
int count; // count the errors
int c, s, line, para; // mark the state when in '', "", // and /* */
int escape, slash, star; // mark the \, / and *
int row, col; // current position
int state[3], last[3][2]; // last[0/ 1/ 2][] means the last position of ( / [ / {
count = 0;
c = s = line = para = 0;
escape = slash = star = 0;
row = 1;
col = 0;
state[0] = state[1] = state[2] = 0;
for(i = 0; i < MAXERROR; ++i){
errorinfo[i][0] = errorinfo[i][1] = errorinfo[i][2] = 0;
}
// now read the code
for(i = 0; (ch = getchar()) != EOF; ++i){
// current position
++col;
// change of state
if(!c && !s && !line && !para){ // be not in quotes or comments
switch(ch){
case '(': ++state[0]; break;
case ')': --state[0]; break;
case '[': ++state[1]; break;
case ']': --state[1]; break;
case '{': ++state[2]; break;
case '}': --state[2]; break;
default: break;
}
}
// use state to judge errors and record their positions
for(j = 0; j < 3; ++j){
if(state[j] == -1){
state[j] = 0;
errorinfo[count][0] = row;
errorinfo[count][1] = col;
errorinfo[count][2] = j;
++count;
}
if(state[j] == 2){
state[j] = 1;
errorinfo[count][0] = last[j][0];
errorinfo[count][1] = last[j][1];
errorinfo[count][2] = j;
++count;
}
}
// record the last position of ( / [ / {
if(state[0] == 1 && ch == '('){
last[0][0] = row;
last[0][1] = col;
}
if(state[1] == 1 && ch == '['){
last[1][0] = row;
last[1][1] = col;
}
if(state[2] == 1 && ch == '{'){
last[2][0] = row;
last[2][1] = col;
}
/* below are current states of code */
// in ''?
if(ch == '\'' && c && escape == 0){
c = 0;
}
else if(ch == '\'' && !c && !s && !line && !para){
c = 1;
}
// in ""?
if(ch == '"' && s && escape == 0){
s = 0;
}
else if(ch == '"' && !c && !s && !line && !para){
s = 1;
}
// in line comment //?
if(line && ch == '\n'){
line = 0;
}
else if(!c && !s && !line && !para && ch == '/' && slash == 1){
line = 1;
}
// in paragraph comment /* */?
if(para && ch == '/' && star == 1){
para = 0;
}
else if(!c && !s && !line && !para && ch == '*' && slash == 1){
para = 1;
}
// mark escape sequence
if(ch == '\\'){
if(escape == 1){
escape = 0;
}
else escape = 1;
}
else escape = 0;
// mark the * in paragraph comment
if(para && ch == '*' && slash == 0){
star = 1;
}
else star = 0;
// mark the first /
if(!c && !s && !line && !para && ch == '/'){
slash = 1;
}
else slash = 0;
// newline if read the '\n'
if(ch == '\n' && !c && !s && !line){
++row;
col = 0;
}
}
// in the end, state should be 0, which means balanced
for(j = 0; j < 3; ++j){
if(state[j] == 1){
errorinfo[count][0] = last[j][0];
errorinfo[count][1] = last[j][1];
errorinfo[count][2] = j;
++count;
}
}
// now sort all the errors recorded by rows and cols
sort(errorinfo, count);
// now print all the errors recorded
printf("Here are your errors:\n");
for(i = 0; i < count; ++i){
errorprint(errorinfo[i][0], errorinfo[i][1], errorinfo[i][2]);
}
system("pause");
return 0;
}
void sort(int s[MAXERROR][3], int len){
int i, j, min;
int temp[3];
for(i = 0; i < len; ++i){
min = i;
for(j = i + 1; j < len; ++j){
if(s[j][0] < s[min][0]){
min = j;
}
else if(s[j][0] == s[min][0]){
if(s[j][1] < s[min][1]){
min = j;
}
}
}
if(min != i){
temp[0] = s[min][0];
temp[1] = s[min][1];
temp[2] = s[min][2];
s[min][0] = s[i][0];
s[min][1] = s[i][1];
s[min][2] = s[i][2];
s[i][0] = temp[0];
s[i][1] = temp[1];
s[i][2] = temp[2];
}
}
}
void errorprint(int row, int col, int errortype){
printf("ERROR(行 %3d 列 %2d):", row, col);
switch (errortype){
case PARENTHESE_UNBALANCED:
printf("圆括号不匹配。\n");
break;
case BRACKET_UNBALANCED:
printf("方括号不匹配。\n");
break;
case BRACE_UNBALANCED:
printf("花括号不匹配。\n");
break;
default:
break;
}
}
运行示例:
#include<stdio.h>
#include<stdlib.h>
int main((void)){
(char c] = '*';
int i;
}i = 10;
if(10 == '\n' && i){ // 说明在运算时,'\n'被当作10
[printf("%c%d,hello,world\n\"\n", c, !i);
}
system("pause");
return 0;
}
^Z
Here are your errors:
ERROR(行 4 列 9):圆括号不匹配。
ERROR(行 4 列 16):圆括号不匹配。
ERROR(行 5 列 5):圆括号不匹配。
ERROR(行 5 列 12):方括号不匹配。
ERROR(行 10 列 9):方括号不匹配。
ERROR(行 14 列 1):花括号不匹配。