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 ...
随机推荐
- #范德蒙德卷积,第二类斯特林数,NTT#洛谷 2791 幼儿园篮球题
题目 \(T(T\leq 200)\)组数据求 \[\frac{1}{C(n,k)}\sum_{i=0}^kC(m,i)C(n-m,k-i)i^L \] 对于所有数据满足 \(n,m,k\leq 2* ...
- #后缀数组#洛谷 4051 [JSOI2007]字符加密
题目 分析 将字符串复制一份放入末尾,将其后缀排序之后 SA数组既然表示排名为\(i\)的后缀的起始位置, 那么只要它在\([1,len]\)范围内就是合法的, 那么输出以这个位置开头长度为\(len ...
- 从模型到部署,教你如何用Python构建机器学习API服务
本文分享自华为云社区<Python构建机器学习API服务从模型到部署的完整指南>,作者: 柠檬味拥抱. 在当今数据驱动的世界中,机器学习模型在解决各种问题中扮演着重要角色.然而,将这些模型 ...
- Yolov5代码详解——detect.py
首先执行扩展包的导入: import argparse import os import platform import sys from pathlib import Path import t ...
- 探索生成式AI的未来:Chat与Agent的较量与融合
近年来,生成式人工智能(AI)不仅在技术界引起了广泛关注,更成为了推动多个行业革新的关键力量.这种技术之所以备受瞩目,不仅在于其独特的创造性和高效性,还在于它对未来商业模式和社会结构可能产生的深远影响 ...
- 机器学习服务活体检测算法荣获CFCA权威安全认证
随着人脸识别技术在金融.医疗等多个领域的加速落地,网络安全.信息泄露等问题愈为突出,用户对应用稳定性和安全性的要求也更为严格.为保障各行业高效稳定的开展业务,提前发现和应对潜在安全风险,HMS Cor ...
- 可视化库 pygal 无法保存成本地文件
问题:在使用可视化库 pygal 保存图像到本地时,出现报错 第一次报错是,提示没有 cairosvg 这个模块,所以直接通过 pip 安装 pip install cairosvg 安装完了以后 ...
- 在python中通过面向对象方式,实现烤地瓜案例
例子:烤地瓜,不同时间,反馈不同状态,并给不同状态地瓜加入不同味道 烤地瓜时间 0-3分钟,生的 4-7分钟,半生不熟的 8-12分钟,熟了 12分钟以上,已烤熟,糊了 用户可以按自己的意思添加调料 ...
- HarmonyOS Connect认证测试
原文链接:https://mp.weixin.qq.com/s/zRG97PWPqfDo0vfwQWSUew,点击链接查看更多技术内容: 在HarmonyOS Connect生态产品的认证测试过 ...
- sql 语句系列(删库跑路系列)[八百章之第七章]
前言 最开心的章节,没有之一. 删除违反参照完整性的记录 EMP 是员工表,DEPT 是部门表 DEPTNO是部门编号 delete from EMP where not exists ( selec ...