C语言:使用链式栈检测txt文件中的括号匹配
便捷目录
前言
因为我这里用了比较多全局变量,如果不想用这么多全局变量,如果你觉得不顺手或者不顺眼,你可以不用,但是在其他函数需要用到这些函数的时候需要你加多几个参数进来。
举个栗子: 比如a这个全局变量,现在看他不顺眼,放在Main函数里面,我的另一个函数里面需要用到a,但是Main函数的变量不能在其他函数用,这时候需要把其他函数多加一个参数,把a传进去。
本程序最终会完成的任务
i. 如果都匹配,则返回都匹配。
ii. 若不匹配,则返回第几行第几列不匹配。
iii. 将不匹配的行打印出来。
栈的理解
栈(stack)是一个先进后出的一个线性表
(一般)使用尾插法:
## 那么↓ ##
表头是栈底
表尾是栈顶
本博客就是应用栈进行匹配括号的方式,平时我们一般人都不会察觉括号有这个规律,其实我也是一样,接触了算法后才知道括号可以用栈来实现配对。具体为什么,下面我会详细解释。
代码运行过程的解释
说明
首先说明一下栈的括号匹配,我们只需要把左括号进栈,右括号不用进栈,因为我们知道了左括号时,当遇到右括号的时候直接进行匹配就好,如果匹配成功就直接出栈,如果前面已经出栈了一个左括号,过程中又遇到左括号继续将他进栈,继续往后匹配,没有影响,其实如果你能理解这里的话你就会感叹栈的妙处~ 然后就直至全部出栈成功的话就代表配对成功
**(我这个单身狗都还没有配对成功) **
代码思想 (重要部分)
(该程序中只对英文的括号进行配对,即对中文括号配对无效)
希望阅读者一定要好好的理解下这个执行过程,如果看不懂代码就必须要看懂这部分,看懂这部分再去看代码就so easy了,加油!
①首先对于文件内容的扫描时,先判断是否遇到了左括号如果遇到了,首先把当前左括号的所有信息记录下来,即记录所在的行和列。
( ## 别急待会在全局变量和结构体代码部分中讲怎么弄这个 ## )
完成上述左括号进栈操作后将指向栈的位置往后移动,继续扫描文件中的括号。
②需要知道的是,当我们每次换行都要记录这行的文件开头位置,也就是说当你扫描到换行符了,立马用fgetpos函数记录下现在的位置。我们暂时把这个变量成为position。
③如果遇到了右括号,这时候要做的是与左括号进行匹配,
a 如果配对成功就左括号出栈 :然后继续扫描下一个右括号。
b 如果遇到的右括号是错误的 :那么就要记录这个右括号的行和列,还要用fgetpos函数对这个右括号所在的txt文件位置进行记录。我们暂时把这个变量成为curr。
④记录完成后,立马回到这个右括号所在文件的这一行的开头,也就是回到position所记录的位置,接着就是把该行内容用一个数组进行存储该行的错误信息(因为我们要实现的功能是打印错误的行信息)
⑤录入信息完成之后,还记得用fgetpos函数记录的右括号curr位置吗,没错这个时候派上用场了,回到这个位置之后重复刚刚的操作,重复的进栈和出栈。
全局变量和结构体代码
下面的结构体就是纯纯为记录左括号位置而服务的,
所以结构体里面包含了行和列还有存放的字符变量。
right[51]:存放匹配错误的右括号信息
left[51]:存放匹配错误左括号的信息
r_index = 0:记录存放数组的右括号下标,也是记录右括号的错误数量
l_index = 0:记录存放数组的左括号下标,也是记录左括号的错误数量
P_bracket temp = NULL:这个是栈表头 (也是左括号进栈的链式栈)
t_index = 0:记录链式栈的下标
//剩下的就是存放错误信息了,比较好理解就不赘述了,代码有注释
切记一定要用二维数组来存放这些错误信息。
/*括号链表*/
typedef struct _bracket{
int column;//列
int row;//行
char BRA;//括号字符
struct _bracket *next;
}bracket, *P_bracket;//先给五十个空间
P_bracket temp = NULL;
bracket right[51], left[51];// ;,temp[100]
int r_index = 0, l_index = 0, t_index = 0;
char err_information[51][200];
//存放错误信息,这里你可以修改存放的错误信息空间
int err_index = 0;//错误信息数组的下标
进栈:创建链表空间函数
如果不懂具体实现的过程,可以跳转至这篇文章 -> 最全链表剖析–点击博客
代码如下,正常的添加链表空间的操作
void ADD_LinkBracket_Stack(P_bracket *L)//二级指针 ,链式栈,这里采用头插法,出的时候也要头出
{
P_bracket temp = (*L), new_room;
if((*L) == NULL)
{
(*L) = (P_bracket)malloc(sizeof(bracket));
if(!(*L))
{
PF(括号链表分配失败!);
exit(1);//程序异常退出
}
(*L)->next = NULL;
}
else
{
new_room = (P_bracket)malloc(sizeof(bracket));
if(!new_room)
{
PF(括号链表分配失败!);
exit(1);//程序异常退出
}
new_room->next = temp;
(*L) = new_room;
}
}
出栈:删除链表空间函数
如果不懂具体实现的过程,可以跳转至这篇文章 -> 最全链表剖析–点击博客
代码如下,正常的删除链表空间的操作
void Out_LinkBracket_Stack(P_bracket *L)
//头出,因为进栈是头插,所以出栈也要头出
{
P_bracket temp = (*L), cur;
if(temp->next)//判断不为空栈
{
cur = temp->next;
free(temp);
(*L) = cur;//头部释放后,应该把头部修改成下一个
}
//if((*L) == NULL) (*L) = NULL;
}
释放申请的链式栈空间
如果不懂具体实现的过程,可以跳转至这篇文章 -> 最全链表剖析–点击博客
代码如下,正常的释放链表空间的操作
void Release_linkBracket_Stack(P_bracket *L)
{
P_bracket t = (*L);
while(t)
{
t = t->next;
free((*L));
(*L) = t;
}
}
如何实现检查括号配对函数
具体的实现过程已经在前面解释过了,如果看懂了运行过程解释的话,看下面代码就轻而易举。
补充:bool open = true; //一定要赋值为true,因为如果没有出现错误也能判断返回一个true。
这个是用来判断是否出现过错误信息,如果出现了,在整个函数结束之前返回false表示出现了括号匹配错误,否则返回true。
如果还不懂,恕我无能。你也可以来问我,我看到信息都会很乐意的为大家解答疑问喔!
bool Check_Bracket_link(FILE*fp, P_bracket *Check_B)
{
int row = 1, column = 1,j;
char ch;
P_bracket temp = (*Check_B);
bool open = true;//用来判断是否有错误信息
fpos_t position, curr;
//ADD_LinkBracket_Stack(&temp);//先初始化一个空间
fgetpos(fp, &position);//先记录第一行的位置
while(!feof(fp))
{
ch = fgetc(fp);
column++;//列数加一
if(ch == '\n')//没有遇到错误括号的时候,用这个来记录文件位置
{
//行数加一
row++;
column = 1;
fgetpos(fp, &position);//记录下一行的行开头
}
if(ch== '<' || ch== '{' || ch== '(' || ch== '[')
{
ADD_LinkBracket_Stack(&temp);//开辟一个新空间,等待下一个括号进入PF(测试测试!);
temp->BRA = ch;
temp->column = column;
temp->row = row;
}
if(ch=='>' || ch=='}' || ch==')' || ch==']')
{
if(temp && ((temp->BRA=='<' && ch == '>' )||
(temp->BRA=='{' && ch == '}' )||
(temp->BRA=='(' && ch == ')' )||
(temp->BRA=='[' && ch == ']' ) ))
{
//从中学到,判断条件是从左到右进行判断,所以要把temp是否为空先放在左边,不然如果为空,又进行temp的指针判断就会程序异常退出
Out_LinkBracket_Stack(&temp);//出栈,头部出
}
else
{
open = false;//代表出现错误了
fgetpos(fp, &curr); //记录当前右括号的位置,录入完错误信息后退出循环要继续返回到这个位置扫描
if(temp)
{//因为有可能开局就只有右边的括号而没有左括号,所以要判断一下,所以在输出的时候就要判断左括号的行坐标是否大于右括号的坐标
left[l_index].column = temp->column;
left[l_index].row = temp->row;
l_index++;//单独在里面加加,不能放在外面,因为要输出左括号的时候要用到,如果没有大于0就代表是没有对应的左括号
}
right[r_index].column = column;
right[r_index].row = row;
r_index++;
fsetpos(fp, &position);
while(!feof(fp))//存行信息的循环,记得最后把数组移位,进行下次错误信息存储
{
ch = fgetc(fp);
err_information[err_index][j++] = ch;
if(ch == '\n')//这个是遇到错误括号匹配的时候的条件判断,用来记录文件位置
{
fsetpos(fp, &curr);//返回上次错误的位置
err_information[err_index][j] = '\0';//变为字符串方便打印
j = 0;//j变回0,以便下次继续使用
err_index++;//字符串空间移动下一位
break;
}
}
}
}
}
if(open == false) return false;
else return true;
}
完整实现代码
#include<stdio.h>
#include<ctype.h>
#include<stdbool.h>
#include<stdlib.h>
/*括号链表*/
typedef struct _bracket{
int column;//列
int row;//行
char BRA;//括号字符
struct _bracket *next;
}bracket, *P_bracket;//先给五十个空间
P_bracket temp = NULL;
bracket right[51], left[51];// ;,temp[100]
int r_index = 0, l_index = 0, t_index = 0;
char err_information[51][200];
//存放错误信息,这里你可以修改存放的错误信息空间
int err_index = 0;//错误信息数组的下标
bool Check_Bracket_link(FILE*fp, P_bracket *Check_B);
void ADD_LinkBracket_Stack(P_bracket *L);
void Out_LinkBracket_Stack(P_bracket *L);
void Release_linkBracket_Stack(P_bracket *L);
int main()
{
FILE *fp;
int i;
bool Judge_err;
fp = fopen("D:/D/C.txt", "r");//括号
//Judge_err = Check_Bracket_Sq(fp);//完成了搜索,然后需要包装函数将其输出
Judge_err = Check_Bracket_link(fp, &temp);//完成了搜索,然后需要包装函数将其输出
fclose(fp);
if(Judge_err) printf("都匹配!");
else
{
if(l_index > 0)
for(i = 0; i < l_index; i++)
{
printf("第%d个错误信息坐标\n", i+1);
if(left[i].row < right[i].row)
printf("左括号:(%d,%d)\n", left[i].row, left[i].column);
printf("右括号:(%d,%d)\n", right[i].row, right[i].column);
printf("错误信息行:%s\n",err_information[i]);
}
else
{
for(i = 0; i < r_index; i++)
{
printf("第%d个错误信息坐标\n", i+1);
printf("左括号:无对应括号\n");
printf("右括号:(%d,%d)\n", right[i].row, right[i].column);
printf("错误信息行:%s\n",err_information[i]);
}
}
}
//需要一个函数把链表里面的东西置空,不然当你做一个循环的时候会持续叠加数量
Release_linkBracket_Stack(&temp); //养成好习惯,当不用的时候就要把申请的空间释放掉
err_index = 0;
l_index = 0;
r_index = 0;
return 0;
}
bool Check_Bracket_link(FILE*fp, P_bracket *Check_B)
{
int row = 1, column = 1,j;
char ch;
P_bracket temp = (*Check_B);
bool open = true;//用来判断是否有错误信息
fpos_t position, curr;
//ADD_LinkBracket_Stack(&temp);//先初始化一个空间
fgetpos(fp, &position);//先记录第一行的位置
while(!feof(fp))
{
ch = fgetc(fp);
column++;//列数加一
if(ch == '\n')//没有遇到错误括号的时候,用这个来记录文件位置
{
//行数加一
row++;
column = 1;
fgetpos(fp, &position);//记录下一行的行开头
}
if(ch== '<' || ch== '{' || ch== '(' || ch== '[')
{
ADD_LinkBracket_Stack(&temp);//开辟一个新空间,等待下一个括号进入PF(测试测试!);
temp->BRA = ch;
temp->column = column;
temp->row = row;
}
if(ch=='>' || ch=='}' || ch==')' || ch==']')
{
if(temp && ((temp->BRA=='<' && ch == '>' )||
(temp->BRA=='{' && ch == '}' )||
(temp->BRA=='(' && ch == ')' )||
(temp->BRA=='[' && ch == ']' ) ))
{
//从中学到,判断条件是从左到右进行判断,所以要把temp是否为空先放在左边,不然如果为空,又进行temp的指针判断就会程序异常退出
Out_LinkBracket_Stack(&temp);//出栈,头部出
}
else
{
open = false;//代表出现错误了
fgetpos(fp, &curr); //记录当前右括号的位置,录入完错误信息后退出循环要继续返回到这个位置扫描
if(temp)
{//因为有可能开局就只有右边的括号而没有左括号,所以要判断一下,所以在输出的时候就要判断左括号的行坐标是否大于右括号的坐标
left[l_index].column = temp->column;
left[l_index].row = temp->row;
l_index++;//单独在里面加加,不能放在外面,因为要输出左括号的时候要用到,如果没有大于0就代表是没有对应的左括号
}
right[r_index].column = column;
right[r_index].row = row;
r_index++;
fsetpos(fp, &position);
while(!feof(fp))//存行信息的循环,记得最后把数组移位,进行下次错误信息存储
{
ch = fgetc(fp);
err_information[err_index][j++] = ch;
if(ch == '\n')//这个是遇到错误括号匹配的时候的条件判断,用来记录文件位置
{
fsetpos(fp, &curr);//返回上次错误的位置
err_information[err_index][j] = '\0';//变为字符串方便打印
j = 0;//j变回0,以便下次继续使用
err_index++;//字符串空间移动下一位
break;
}
}
}
}
}
if(open == false) return false;
else return true;
}
void ADD_LinkBracket_Stack(P_bracket *L)//二级指针 ,链式栈,这里采用头插法,出的时候也要头出
{
P_bracket temp = (*L), new_room;
if((*L) == NULL)
{
(*L) = (P_bracket)malloc(sizeof(bracket));
if(!(*L))
{
printf("括号链表分配失败!");
exit(1);//程序异常退出
}
(*L)->next = NULL;
}
else
{
new_room = (P_bracket)malloc(sizeof(bracket));
if(!new_room)
{
printf("括号链表分配失败!");
exit(1);//程序异常退出
}
new_room->next = temp;
(*L) = new_room;
}
}
void Out_LinkBracket_Stack(P_bracket *L)//头出,因为进栈是头插,所以出栈也要头出
{
P_bracket temp = (*L), cur;
if(temp->next)//判断不为空栈
{
cur = temp->next;
free(temp);
(*L) = cur;//头部释放后,应该把头部修改成下一个
}
//if((*L) == NULL) (*L) = NULL;
}
void Release_linkBracket_Stack(P_bracket *L)
{
P_bracket t = (*L);
while(t)
{
t = t->next;
free((*L));
(*L) = t;
}
}
结尾
栈这个东西真的还是蛮多用处的,我做这篇文章的初衷也是想要帮自己回忆一下这些si去的记忆,大家如果看到这对栈的印象还有点模糊的话,可以多看几遍,用这个例子来演示可以更好的了理解栈的具体实现操作。
希望能帮到大家~
C语言:使用链式栈检测txt文件中的括号匹配的更多相关文章
- Android——检测TXT文件中是否含有双字节字符
在读取双字节字符时,主要涉及到编码的选取: public static boolean isRightfulTXT(File f) { // TODO Auto-generated method st ...
- 深度学习tensorflow实战笔记(1)全连接神经网络(FCN)训练自己的数据(从txt文件中读取)
1.准备数据 把数据放进txt文件中(数据量大的话,就写一段程序自己把数据自动的写入txt文件中,任何语言都能实现),数据之间用逗号隔开,最后一列标注数据的标签(用于分类),比如0,1.每一行表示一个 ...
- 按行读取TXT文件中的内容
public Dictionary<int, string> GetDicFromLog() { try { StreamReader sr = new StreamReader(file ...
- java 写一个"HelloJavaWorld你好世界"输出到操作系统文件Hello.txt文件中
package com.beiwo.homework; import java.io.File; import java.io.FileOutputStream; import java.io.IOE ...
- 从txt文件中读取数据放在二维数组中
1.我D盘中的test.txt文件内的内容是这样的,也是随机产生的二维数组 /test.txt/ 5.440000 3.4500006.610000 6.0400008.900000 3.030000 ...
- 把cmd信息中的正常和异常输出分别输出到不同txt文件中
场景一: 1.大量滚动信息容纳不下,在小黑屏中被冲刷掉. 2.希望把正常输出和异常输出分别输出到不同地方. 相关命令 一共有4个输出到文件的命令,现以jar命令打war包举例说明: 命令 说明 举例 ...
- java将数据写入到txt文件中(txt有固定的格式)
java将数据写入到txt文件中,这个应该对于学过java I/O的人来说是很简单的事情了,但是如果要将数据以固定的格式写入到txt文件中,就需要一定的技巧了. 这里举个简单的例子,以供参考: 比如我 ...
- ORACLE 中写入txt文本与从Txt文件中读入数据 修改表结构
--创建一个表 DROP TABLE TEST CASCADE CONSTRAINTS ; CREATE TABLE TEST(A VARCHAR(30),B VARCHAR(30)); --查看具体 ...
- 读取同一文件夹下多个txt文件中的特定内容并做统计
读取同一文件夹下多个txt文件中的特定内容并做统计 有网友在问,C#读取同一文件夹下多个txt文件中的特定内容,并把各个文本的数据做统计. 昨晚Insus.NET抽上些少时间,来实现此问题,加强自身的 ...
- SQL C# nvarchar类型转换为int类型 多表查询的问题,查询结果到新表,TXT数据读取到控件和数据库,生成在控件中的数据如何存到TXT文件中
在数据库时候我设计了学生的分数为nvarchar(50),是为了在从TXT文件中读取数据插入到数据库表时候方便,但是在后期由于涉及到统计问题,比如求平均值等,需要int类型才可以,方法是:Conver ...
随机推荐
- #树形依赖背包,点分治#BZOJ 4182 Shopping
题目 给定一棵大小为 \(n\) 的树,每个点代表一种物品,其具有体积.价值和数量的属性, 现在选择一个连通块,使得里面所有点都被选中且体积不超过 \(m\),问最大价值. \(n\leq 500,m ...
- #线性基,点分治#洛谷 3292 [SCOI2016]幸运数字
题目 分析 题目就是将\(x\)到\(y\)路径上的线性基合并求解, 这里用的是点分治,每次换根到重心的时候维护前缀线性基, 查询的时候如果属于不同的子树就能询问答案,记得\(x=y\)要特判 代码 ...
- JDK14的新特性:JFR,JMC和JFR事件流
目录 简介 JFR JMC 创建JFR 分析JFR JFR事件 JFR事件流 总结 JDK 14的新特性:JFR,JMC和JFR事件流 简介 Java Flight Recorder(JFR)是JVM ...
- MySQL 数据库操作指南:LIMIT,OFFSET 和 JOIN 的使用
限制结果 您可以通过使用"LIMIT"语句来限制查询返回的记录数量.以下是一个示例,获取您自己的Python服务器中"customers"表中的前5条记录: i ...
- openGauss数据库源码解析——慢SQL检测
openGauss 数据库源码解析--慢 SQL 检测 慢 SQL 检测的定义: 基于历史 SQL 语句信息进行模型训练,并用训练好的模型进行 SQL 语句的预测,利用预测结果判断该 SQL 语句是否 ...
- k8s之持久卷NFS
一.简介 NFS网络存储卷,Kubernetes原生支持NFS作为Kubernetes的持久存储卷之一.NFS可以实现Pod的跨界点的数据持久性. 首先需要创建一个nfs 服务器,作为存储服务器: 将 ...
- Godot.NET C#IOC重构(2):TileMap 详解
目录 前言 TileMap添加 TileMap绘制 TileMap 连续图块 修改纹理原点 统一设置 自动地形 匹配规则 修改匹配概率 修改概率前 修改概率后 随机图块 Scattering 不连续间 ...
- sql 语句系列(行与列处理)[八百章之第一章]
排序时对null进行处理 比如说: select * from EMP order by COMM 我需要对红框部分进行desc处理,也就是从大到小排列. 解析: 重点是如何让null独立出去. se ...
- 周博磊老师强化学习纲领笔记第二课:MDP,Policy Iteration与Value Iteration
gym环境:FrozenLake-v0:http://gym.openai.com/envs/FrozenLake-v0/ 代码来自:周博磊老师的GitHub:https://github.com/c ...
- 我自己的JdbcTemplate
import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import jav ...