【自用】【数据结构习题 练1】括号匹配判定

一、问题

(一)题干 总析

题干
编写一程序检查C源程序文件中{}、()等括号是否匹配,并输出第一个检测到的不匹配的括号及所对应括号所在的行号(程序中同一类括号只有一个不匹配)。
注意:
1.除了括号可能不匹配外,输入的C源程序无其它语法错误。
2.字符常量、字符串常量及注释中括号不应被处理,注释包括单行注释//和多行/* */注释
3.字符常量和字符串常量中不包含转义字符’和";
4.程序中出现有意义括号的个数不超过200个;
不匹配判断规则:
1.当检测的程序括号为’{‘时,若其前序尚未匹配的括号为’(‘时,输出该’(‘左括号及所在行号;
2.当遇到一个不匹配的右括号’)‘或’}'时,输出该右括号及所在行号;
3.当程序处理完毕时,还存在不匹配的左括号时,输出该左括号及所在行号。
总析
考虑使用栈检验新来的和之前的是否匹配,匹配就弹出,总可以保证剩在栈top处的就是最待匹配的那个,但是要注意处理注释和字符串。

(二)困难

  1. 读入的语义解释,过滤掉不需要的内容的同时保证有持续读取的动力
  2. 注释和引号中的括号是失效的
  3. 括号的匹配如何通过栈实现

二、NOTE

  1. 来自前篇【例1】的getsym与ungetc函数实现语义解释
  2. 注释匹配规则详解:
    • 对// :直到\n为止文本被失效
    • 对"" : 直到”为止文本被失效
    • 对/**/: 直到*/为止文本失效
  3. 读入有效语义不按行划分,而是从头读到尾。
  4. 栈的规则是:
    • 判定if合法:进栈,
    • else不合法:
      • 检验if 匹配:弹出
      • 否则 如果是注释导致的不匹配,忽略,如果不是注释导致,则找到了错误,停止。

三、算法

  1. 首先保证读取的都是有效字符(下文定义什么是有效字符),去除无关字符。
  2. 设置一个栈留存所有待配对的字符(配对好的及时弹出,不会在栈中,右括号不会是待匹配的,不会在栈中)
  3. 然后考虑每读到一个字符的匹配判定问题:
    1. 如果是注释字符,注释开始标志入栈,则到注释结束标志为止,所有输入有效字符都不入栈直接舍弃。
      读到注释结束标志后,注释开始标志弹出,栈恢复工作。
    2. 如果栈是空的,那么左括号一定能入栈,右括号尝试入栈一定是错误的
    3. 如果栈不是空的且待匹配不是注释(栈正常工作状态),则尝试入栈的要么是左括号要么是右括号。
      • 对于左括号,若是(,一定可以进栈,若是{,之前不是(则一定可以进栈。
      • 对于右括号,如果匹配top的左括号则配对成功,弹出,否则不匹配,错误。
    4. 持续进行,如果结果栈空则没有待配对,即完全配对正确。

四、具体代码分析

(一)定义声明

  1. str_bracket 记录有效bracket序列以便最终配对成功时候输出。
  2. n_line记录行,行与括号一同存入stack以便配对失败时候查找行号
char buffer[300];
char str_bracket[300];
int n_line = 1;

(二)语义解释,忽略无效符号

  1. 有效符号有:
    • { } ()
    • /* * / return as*
    • “ ” return as"
    • // return /
    • \n 标志 换行(行数加1)和//结束
    • EOF 标志文件结束
  2. 其中// 和/** / 涉及多位读取,读到/后需要后读一位确定是否为它们,如果不是则用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;
        }
    }
}

(三) 判定是否要让符号进栈

  1. 栈为空一定可以进栈
  2. 栈不为空判定合法性
    1. 注释和字符串内括号不进栈
    2. 注释的起始标志可以进栈但终止标志不可以
    3. (一定可以进栈
    4. {如果前面没有(可以进栈
// 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;
}

(四)判定是否匹配需要弹出

  1. //不涉及匹配问题,在main函数中,得到有效字符为\n时候解决pop问题
  2. 注释匹配
  3. 括号匹配
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);
}
  • 18
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值