一、问题
(一)题干 总析
题干
编写一程序检查C源程序文件中{}、()等括号是否匹配,并输出第一个检测到的不匹配的括号及所对应括号所在的行号(程序中同一类括号只有一个不匹配)。
注意:
1.除了括号可能不匹配外,输入的C源程序无其它语法错误。
2.字符常量、字符串常量及注释中括号不应被处理,注释包括单行注释//和多行/* */注释
3.字符常量和字符串常量中不包含转义字符’和";
4.程序中出现有意义括号的个数不超过200个;
不匹配判断规则:
1.当检测的程序括号为’{‘时,若其前序尚未匹配的括号为’(‘时,输出该’(‘左括号及所在行号;
2.当遇到一个不匹配的右括号’)‘或’}'时,输出该右括号及所在行号;
3.当程序处理完毕时,还存在不匹配的左括号时,输出该左括号及所在行号。
总析
考虑使用栈检验新来的和之前的是否匹配,匹配就弹出,总可以保证剩在栈top处的就是最待匹配的那个,但是要注意处理注释和字符串。
(二)困难
- 读入的语义解释,过滤掉不需要的内容的同时保证有持续读取的动力
- 注释和引号中的括号是失效的
- 括号的匹配如何通过栈实现
二、NOTE
- 来自前篇【例1】的getsym与ungetc函数实现语义解释
- 注释匹配规则详解:
- 对// :直到\n为止文本被失效
- 对"" : 直到”为止文本被失效
- 对/**/: 直到*/为止文本失效
- 读入有效语义不按行划分,而是从头读到尾。
- 栈的规则是:
- 判定if合法:进栈,
- else不合法:
- 检验if 匹配:弹出
- 否则 如果是注释导致的不匹配,忽略,如果不是注释导致,则找到了错误,停止。
三、算法
- 首先保证读取的都是有效字符(下文定义什么是有效字符),去除无关字符。
- 设置一个栈留存所有待配对的字符(配对好的及时弹出,不会在栈中,右括号不会是待匹配的,不会在栈中)
- 然后考虑每读到一个字符的匹配判定问题:
- 如果是注释字符,注释开始标志入栈,则到注释结束标志为止,所有输入有效字符都不入栈直接舍弃。
读到注释结束标志后,注释开始标志弹出,栈恢复工作。 - 如果栈是空的,那么左括号一定能入栈,右括号尝试入栈一定是错误的
- 如果栈不是空的且待匹配不是注释(栈正常工作状态),则尝试入栈的要么是左括号要么是右括号。
- 对于左括号,若是(,一定可以进栈,若是{,之前不是(则一定可以进栈。
- 对于右括号,如果匹配top的左括号则配对成功,弹出,否则不匹配,错误。
- 持续进行,如果结果栈空则没有待配对,即完全配对正确。
- 如果是注释字符,注释开始标志入栈,则到注释结束标志为止,所有输入有效字符都不入栈直接舍弃。
四、具体代码分析
(一)定义声明
- str_bracket 记录有效bracket序列以便最终配对成功时候输出。
- n_line记录行,行与括号一同存入stack以便配对失败时候查找行号
char buffer[300];
char str_bracket[300];
int n_line = 1;
(二)语义解释,忽略无效符号
- 有效符号有:
- { } ()
- /* * / return as*
- “ ” return as"
- // return /
- \n 标志 换行(行数加1)和//结束
- EOF 标志文件结束
- 其中// 和/** / 涉及多位读取,读到/后需要后读一位确定是否为它们,如果不是则用ungetc推回流中
char GetSym(FILE *fp)
{
char c, c1;
while (c = fgetc(fp))
{
switch (c)
{
case '{':
case '}':
case '(':
case ')':
case '"':
case '\n':
case EOF:
return c;
case '/':
c1 = fgetc(fp);
if (c1 == '/')
return '/';
else if (c1 == '*')
return '*';
else
ungetc(c1, fp);
break;
case '*':
c1 = fgetc(fp);
if (c1 == '/')
return '*';
else
ungetc(c1, fp);
break;
case ' ':
case '\t':
break;
default:
break;
}
}
}
(三) 判定是否要让符号进栈
- 栈为空一定可以进栈
- 栈不为空判定合法性
- 注释和字符串内括号不进栈
- 注释的起始标志可以进栈但终止标志不可以
- (一定可以进栈
- {如果前面没有(可以进栈
// judge whether to put the sym into stack
int IsValid(char c)
{
if (top == NULL)
return 1;
else if (top->data == '*' || top->data == '/' || top->data == '"') // if is sym in explanation or string
return 0;
else
{
if (c == '/')
return 1;
if ((c == '*' || c == '"') && top->data != c)
return 1;
if (c == '(')
return 1;
if (c == '{' && top->data != '(')
return 1;
}
return 0;
}
(四)判定是否匹配需要弹出
- //不涉及匹配问题,在main函数中,得到有效字符为\n时候解决pop问题
- 注释匹配
- 括号匹配
int IsMatch(char c)
{
if ((c == ')' && top->data == '(') ||
(c == '}' && top->data == '{') ||
(c == '*' && top->data == '*') ||
(c == '"' && top->data == '"'))
return 1;
return 0;
}
(三)主函数
完整代码
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 300
#define DATATYPE char
typedef struct Node
{
DATATYPE data;
int line;
struct Node *next;
} Node;
Node *top = NULL;
void InitStack()
{
top = NULL;
}
int IsEmpty()
{
return top == NULL;
}
void Push(DATATYPE a, int b)
{
Node *p = (Node *)malloc(sizeof(Node));
p->data = a;
p->next = top;
p->line = b;
top = p;
}
void Pop()
{
Node *p = top;
top = top->next;
free(p);
}
void getTop(DATATYPE *A, int *B)
{
*A = top->data;
*B = top->line;
}
int IsBracket(char c)
{
if (c == '(' || c == ')' || c == '{' || c == '}')
return 1;
return 0;
}
char buffer[300];
char str_bracket[300];
int n_line = 1;
// /* push in stack *
// // push in stack /
// "" push in stack "
//
char GetSym(FILE *fp)
{
char c, c1;
while (c = fgetc(fp))
{
switch (c)
{
case '{':
case '}':
case '(':
case ')':
case '"':
case '\n':
case EOF:
return c;
case '/':
c1 = fgetc(fp);
if (c1 == '/')
return '/';
else if (c1 == '*')
return '*';
else
ungetc(c1, fp);
break;
case '*':
c1 = fgetc(fp);
if (c1 == '/')
return '*';
else
ungetc(c1, fp);
break;
case ' ':
case '\t':
break;
default:
break;
}
}
}
// judge whether to put the sym into stack
int IsValid(char c)
{
if (top == NULL)
return 1;
else if (top->data == '*' || top->data == '/' || top->data == '"') // if is sym in explanation or string
return 0;
else
{
if (c == '/')
return 1;
if ((c == '*' || c == '"') && top->data != c)
return 1;
if (c == '(')
return 1;
if (c == '{' && top->data != '(')
return 1;
}
return 0;
}
int IsMatch(char c)
{
if ((c == ')' && top->data == '(') ||
(c == '}' && top->data == '{') ||
(c == '*' && top->data == '*') ||
(c == '"' && top->data == '"'))
return 1;
return 0;
}
int main()
{
char *pt_buf = buffer;
Node *base = top;
Node *cursor = top;
FILE *fp;
int ind_str = 0;
char ch;
if ((fp = fopen("example.c", "r")) == NULL)
{
printf("error when file opens");
return -1;
}
//{}(){(()){}}
// while c=getsym
// if IsValid push in
// else: if IsMatch pop continue else end
// print end
while ((ch = GetSym(fp)) != EOF)
{
if (ch == '\n')
{
if (top != NULL && top->data == '/') // '//' make all sym un-useful until '\n'
Pop();
n_line++;
continue;
}
if (IsBracket(ch) &&
(top == NULL || !(top->data == '*' || top->data == '/' || top->data == '"')))
str_bracket[ind_str++] = ch;
if (IsValid(ch))
{
Push(ch, n_line);
}
else
{
if (IsMatch(ch))
{
Pop();
continue;
}
else if (top->data == '*' || top->data == '/' || top->data == '"')
continue;
else
break;
}
}
if (top != NULL && (ch == '}' || ch == ')'))
printf("without maching '%c' at line %d", ch, n_line);
else if (top != NULL)
printf("without maching '%c' at line %d", top->data, top->line);
else
printf("%s", str_bracket);
}