题目描述
【样例输入1】
若当前目录下输入文件example.c中内容如下:
#include<stdio.h>
int main(){
printf("{ hello world }\n"); // }
)
without maching ')' at line 4
【样例输入2】
若当前目录下输入文件example.c中内容如下:
#include<stdio.h>
int main(){
printf("{ hello world }d\n"); /* }*/
【样例输出2】
without maching '{' at line 2
【样例输入3】
若当前目录下输入文件example.c中内容如下:
#include<stdio.h>
int main(){
printf("{ hello world }d\n"); /* }*/
}
【样例输出3】
(){()}
【样例说明】
样例1:在注释部分和字符串中的括号不考虑,在将程序处理之后得到的括号序列是(){()),遇到右括号时与最近的左括号匹配,发现最后一个小括号和大括号不匹配。
样例2:处理之后的括号序列是(){(),在最后缺少了右大括号,那么应该输出与之相对应的左括号不匹配。
题目分析
简单来说,这就是一道括号匹配问题,是栈应用中比较经典的问题。
在解决这一问题中,题目的难点是存在引号、注释等干扰判断,在编写代码的过程中应加以判断。
实现思路(详细见代码)
代码实现
#include <stdio.h>
#include <stdbool.h> /* C99标准 新增的头文件,里面定义了bool、true、false。原本C语言是不含布尔型的 */
#include <stdlib.h>
#define MAX_SIZE 10000 // C程序最多的字符个数
#define STACK_SIZE 202 // 栈的大小
#define IN_SINGLE_QUOTE 4 // 当前读入字符处于单引号内
#define IN_DOUBLE_QUOTE 3 // 当前读入字符处于双引号内
#define IN_SINGLE_ANNOT 2 // 当前读入字符处于单行注释
#define IN_MULTIPLE_ANNOT 1 // 当前读入字符处于多行注释
#define OUT 0
#define E_SINGLE_ANN -1 // 单行注释出错
#define E_MULTI_ANN -2 // 单行注释出错
#define E_QUOTE -3 // 引号不匹配出错
#define E_BRACKET -4 // 括号(bracket)不匹配出错
#define E_ESC -5 // 转义字符(escape character)错误
void print_error(int line, char where, int error_type);
void print_err(int line, char where);
bool check_bracket(char stack[], int *pointer);
void check_syntax() {
int c, next, cur_line, state,j=0;
char buffer[MAX_SIZE];
char stack[STACK_SIZE], all[STACK_SIZE] ; // stack 保存各种括号,检测是否匹配 ; all 保存所有出现括号
int line[STACK_SIZE] = {0};// 保存各种括号对应行数,检测是否匹配
int pointer = 0; // 栈顶指针
bool open = true, must_end = false; // 是否C程序还未读完
cur_line = 0; // 正在入读程序第第几行
state = OUT;
FILE *fp;
fp = fopen("example.c", "r");
while (fgets(buffer, 201, fp) != NULL) {
cur_line++;
int length = strlen(buffer);
for (int i = 0; i < length; i++) {
c = buffer[i];
if (state == OUT) {
switch (c) {
case '(': // 括号
case ')':
case '[':
case ']':
case '{':
case '}':
line[pointer] = cur_line; // 将各种括号存于栈中以便检查
all[j++] = c;
stack[pointer++] = c; // 将各种括号存于栈中以便检查
if (c != '(' && c != '[' && c != '{')
if (!check_bracket(stack, &pointer)) {
// 括号不匹配
print_error(cur_line, c, E_BRACKET);
}
break;
case '/': // 注释
next = buffer[i + 1];
if (next == '\n')
cur_line++;
if (next == EOF) {
open = false;
// 单行注释语法错误
print_error(cur_line, c, E_SINGLE_ANN);
}
else if (next == '*') { // 进入多行注释
state = IN_MULTIPLE_ANNOT;
}
else if (next == '/') {
state = IN_SINGLE_ANNOT;
}
else {
// 单行注释语法错误
print_error(cur_line, c, E_SINGLE_ANN);
}
break;
case '\'': // 单引号
state = IN_SINGLE_QUOTE;
must_end = false;
break;// 双引号
case '\"':
state = IN_DOUBLE_QUOTE;
break;
}
if (!open) // 当由next读到EOF时,无法从switch中跳出循环,故增加此变量,当next == EOF时跳出while循环
break;
}
else if (state == IN_MULTIPLE_ANNOT) {
if (c == '*') {
next = buffer[i + 1];
if (next == '\n')
cur_line++;
if (next == EOF) {
print_error(cur_line, c, E_MULTI_ANN);
break; // 跳出while
}
else if (next == '/') { // 多行注释结束
state = OUT;
}
}
}
else if (state == IN_SINGLE_ANNOT) {
if (c == '\n')
state = OUT; // 单行注释结束
}
else if (state == IN_SINGLE_QUOTE) {
if (must_end) { // 用来检查是否 只有一个字符
// c 必须是‘'’
if (c == '\'')
state = OUT; // 单引号结束
else
print_error(cur_line, c, E_QUOTE);
}
else if (c == '\\') {
// 遇到转义字符
next = buffer[i + 1];
if (next == EOF)
break;
switch (next) {
// 暂时考虑这些转义字符,没有考虑八进制与十六进制
case '\a':
case '\b':
case '\f':
case '\n':
case '\r':
case '\t':
case '\v':
case '\\':
case '\'':
case '\"':
case '\?':
case '\0':
must_end = true;
break;
default: // 转义字符错误
print_error(cur_line, c, E_ESC);
break;
}
}
else if (c == '\'') { // 单引号内容为空
state = OUT;
}
else { // c是普通字符
must_end = true;
}
}
else if (state == IN_DOUBLE_QUOTE) {
if (c == '\\') {
// 遇到转义字符
next = buffer[i + 1];
if (next == EOF)
break;
switch (next) {
// 暂时考虑这些转义字符,没有考虑八进制与十六进制
case 'a':
case 'b':
case 'f':
case 'n':
case 'r':
case 't':
case 'v':
case '\\':
case '\'':
case '"':
case '?':
case '0':
break;
default: // 转义字符错误
print_error(cur_line, c, E_ESC);
break;
}
}
else if (c == '\"') { // 双引号结束
state = OUT;
}
}
}
}
fclose(fp);
fp = NULL;
if (state != OUT) {
switch (state) {
// 引号缺少结束符
case IN_SINGLE_QUOTE:
case IN_DOUBLE_QUOTE:
print_error(cur_line, '\0', E_QUOTE);
break;
// 多行注释一直延续到程序结束,缺少结束符‘’
case IN_MULTIPLE_ANNOT:
print_error(cur_line, '\0', E_MULTI_ANN);
break;
}
}
else {// 检测栈内容要为空
if (pointer > 0) {
print_error(line[pointer-1], stack[pointer-1], E_BRACKET);
}
else {
for (int i = 0; i < j; i++) {
printf("%c", all[i]);
}
}
}
}
// 如果函数参数是字符数组,调用可以时直接传入字符串常量
void print_error(int line, char where, int error_type) {
// 打印错误信息 :只打印了括号错误
if (error_type == E_BRACKET) {
printf("without maching '%c' at line %d", where, line);
exit(0);
}
}
// 检测括号是否匹配
bool check_bracket(char stack[], int *pointer) {
if (*pointer > 1) { // pointer指向的栈顶位置为空
int r_bracket = stack[--*pointer]; // 右括号
int l_bracket = stack[--*pointer]; // 左括号
switch (r_bracket) {
case ')':
if (l_bracket != '(')
return false;
break;
case ']':
if (l_bracket != '[')
return false;
break;
case '}':
if (l_bracket != '{')
return false;
break;
default:
return false;
}
}
else if (*pointer == 1) {
int top = stack[--*pointer];
if (top != '(' || top != '[' || top != '{')
return false;
}
return true;
}
int main() {
check_syntax();
return 0;
}