数据结构-检查C文件符号匹配-C语言-【检查C源文件中{}、()等括号是否匹配,(包含引号注释) 并输出第一个检测到的不匹配的括号及所对应括号所在的行号(程序中只有一个括号不匹配)。】

题目描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

【样例输入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;
}
  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

eeenkidu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值