首页 > 其他分享 >PTA 7-75 简单宏

PTA 7-75 简单宏

时间:2024-11-13 18:48:23浏览次数:3  
标签:name ++ PTA 空格 75 简单 字符串 line def

期中考试后我做完了这个学期的pta题目,得意忘形,用pta中的题目打趣老师,没想到老师在下节课上增加了题目。这题对我来说是颇有难度的一道题,完整代码在文章结尾。

这道题要求我们识别出输入语句中的宏定义,处理语句中的多余空格并将其中的宏替换。

处理语句还有额外要求:语句前的空格与双引号内的内容不能改动。

任务貌似有点多,让我们稍微拆分下。

考虑到宏的输入会有“        #define        hehe        "hehe"”这样的形式,按照以下顺序来进行或许更方便:

1.读取输入的语句

2.识别是否有宏

3.移除宏语句的多余空格并读取其中的宏

4.对其他语句替换宏

5.移除多余空格

6.打印结果

40c3666246b049b4adb677b0a4b261a6.png

42558bc00d0641f484c2e70ed9f976f9.png

框架

题目规定不会在预编译语句外出现'#'!这是个好消息,我们可以依靠这个识别预编译语句。

题目要求引号内不做处理,我们可以设置状态变量quote,如果不在引号内,quote为0,否则为1。

对于第3-5步,我们暂时没有明显的思路,故可以将其中可能会用到的操作声明为函数,方便我们调试。

为了确保宏的有关数据有序,我们用结构体数组来存储宏。

typedef struct {//包含标识符与宏字符串的结构体
    char name[21];
    char value[31];
} Def;
Def def[10];//结构体数组
int def_count = 0;//已获取的宏数量

void remove_spaces(char* line);//移除空格
void remove_def_spaces(char* line);//移除宏语句空格
int get_def(char* line);//获取宏
void replace_def(char* line);//替换宏

int main(void) {
    char line[201];//用于存储输入的c++语句
    while (fgets(line, sizeof(line), stdin)) {//当读取一行语句
        if (strchr(line, '#') != NULL) //如果在这条语句中含有字符'#'
            if (get_def(line)) continue;//则获取其中的标识符与宏字符串
        if (strcmp(line, "@@\n") == 0) break; //如果语句为@@,结束读取
        replace_def(line);//替换其中的宏
        remove_spaces(line);//移除多余的空格
        if (strlen(line) > 0) printf("%s", line);//输出语句
    }
    return 0;
}

获取宏

要获取宏,我们可以识别"#define"来知道是否为宏语句,首先我们会对其移除一次多余空格。

处理空格与后面的步骤高度重合,所以假如我们读取到的都是正确格式,那么我们可以用sscanf函数来获取标识符与宏字符串,这个函数可以从字符串中读取输入。

为了保证读取到的宏合法,我们对其再进行检查。

int get_def(char* line) {//用于获取标识符与宏字符串
    remove_spaces(line);//处理空格
    if (strncmp(line, "#define", 7) == 0) {//如果识别到了"#define",那么判定为宏
        char name[21];
        char value[31];
        if (sscanf(line + 7, " %20s %30s", name, value) == 2) {
        //sscanf可以将字符串内容输入至其他字符串内,这里获取标识符与宏字符串
            int flag = 1;//判断宏是否符合题目要求,即由数字和字母组成
            if (!isalpha(name[0])) flag = 0;
            for (int i = 0; name[i] != '\0'; ++i)
                if (!isalnum(name[i])) flag = 0;
            if (flag) {
                strcpy(def[def_count].name, name);
                strcpy(def[def_count].value, value);
                def_count++;//正式获取一个合法宏!
            }
            return 1;
        }
    }
    return 0;
}

移除多余空格

读取宏完成了,其次是处理空格,我们可以依靠双指针来进行操作,这样就不必多声明一个数组增加操作。

声明两个int变量i,j。i用于遍历原字符串,j用于向字符串中复制原字符串的内容。

如果符合条件,那么j复制i指向的字符至字符串,覆盖原先的内容,不符合条件,那么j不动,i向后移动。当i移动到字符串末尾时,我们将j之后的子串全部清空并添加空字符(否则scanf("%s")会输出乱码)。

当i遇到了引号时,quote为1,将原字符串内容复制,直到i再次遇到引号,此时quote变为0。

在调试过程中发现了换行符前出现空格的特殊情况,我们多添加一个if语句来进行处理。

函数代码如下:

void remove_spaces(char* line) {//用于移除语句的多余空格
    int i = 0, j = 0;//双指针,j为用于复制新字符串的左指针,i为用于遍历原字符串的右指针
    int len = strlen(line);
    if(strchr(line,'#')==NULL) while (line[i] == ' ') i++, j++;//如果是预编译语句 对语句前的空格不做处理
    else while (line[i] == ' ') i++;//如果是其他语句 跳过前面的空格
    while (i < len) {
        if (line[i] == ' ') {//如果原字符串此处为空格
            line[j++] = ' ';//复制这个空格
            while (line[i] == ' ') i++;//跳过其余的空格
        }//这个if判断用于将连续空格变为一个空格
        else if (line[i] == '"') {//如果原字符串此处是引号
            line[j++] = line[i++];
            while (i < len && line[i] != '"') line[j++] = line[i++];//接下来对引号内的子串不做变动
            line[j++] = line[i++];//复制这个引号
            continue;
        }
        else line[j++] = line[i++];//其他字符直接复制
    }
    while (j > 0 && line[j - 1] == ' ') j++;//删去尾部空格
    line[j] = '\0';//字符串末尾添加结束字符
    if (line[j - 2] == ' ') {//这里处理换行符前为空格的特殊情况
        line[j - 2] = line[j - 1];
        line[j - 1] = '\0';
    }
}

替换宏

接下来是替换宏。这里我们需要注意:双引号内的宏无需替换,作为其他字段子串的宏无需替换。

用found作为标志变量后,我们依然利用双指针来进行遍历,替换则交给strcpy函数。这个函数可以将一个字符串的内容直接复制至另一个字符串。

要检查宏是否为独立的子串,用isalnum函数会方便很多,isalnum可以判断字符是否为字母或数字。

main函数最后打印的是line,所以我们最后把result复制回line中。

void replace_def(char* line) {//用于对语句中的宏进行替换
    char result[201];//替换后的结果
    int i = 0, j = 0;//i为原字符串的指针,j为结果字符串中的指针
    int len = strlen(line);//获取原长度
    while (line[i] == ' ') {//对开头的空格不做处理
        i++;
        result[j++] = ' ';
    }
    while (i < len) {
        if (line[i] == '"') {//对引号内的部分不做处理
            result[j++] = line[i++];
            while (i < len && line[i] != '"')
                result[j++] = line[i++];
            if (i < len) result[j++] = line[i++];
            continue;
        }
        int found = 0;//找到宏的标志
        for (int k = 0; k < def_count; k++) {
            int name_len = strlen(def[k].name);//标识符长度
            if (strncmp(line + i, def[k].name, name_len) == 0 && 
                (i + name_len == len || !isalnum(line[i + name_len])) 
                && !isalnum(line[i - 1])) {
                //保证宏为单独隔开的子串,前后没有数字或字母
                strcpy(result + j, def[k].value);//替换宏
                j += strlen(def[k].value);//更新指针
                i += name_len;
                found = 1;//找到宏了!
                break;
            }
        }
        if (!found) result[j++] = line[i++];//没找到,将整个语句原样复制
    }
    result[j] = '\0';//补充空字符
    strcpy(line, result);//将结果复制回最终打印的字符串
}

完整代码

至此,这题的大部分难点就解决了,剩下的无非是完善指针的边界等等工作。

基于以上思路,完整ac代码如下:

#include <stdio.h>
#include <string.h>
#include <ctype.h>
//好恶心的题 不过确实锻炼了我的字符串处理能力
//功能函数化太香了 比全部堆在main里面好调试多了
typedef struct {//包含标识符与宏字符串的结构体
    char name[21];
    char value[31];
} Def;
Def def[10];//结构体数组
int def_count = 0;//已获取的宏数量
void remove_spaces(char* line) {//用于移除语句的多余空格
    int i = 0, j = 0;//双指针,j为用于复制新字符串的左指针,i为用于遍历原字符串的右指针
    int len = strlen(line);
    if(strchr(line,'#')==NULL) while (line[i] == ' ') i++, j++;//如果是预编译语句 对语句前的空格不做处理
    else while (line[i] == ' ') i++;//如果是其他语句
    while (i < len) {
        if (line[i] == ' ') {//如果原字符串此处为空格
            line[j++] = ' ';//复制这个空格
            while (line[i] == ' ') i++;//跳过其余的空格
        }//这个if判断用于将连续空格变为一个空格
        else if (line[i] == '"') {//如果原字符串此处是引号
            line[j++] = line[i++];
            while (i < len && line[i] != '"') line[j++] = line[i++];//接下来对引号内的子串不做变动
            line[j++] = line[i++];//复制这个引号
            continue;
        }
        else line[j++] = line[i++];//其他字符直接复制
    }
    while (j > 0 && line[j - 1] == ' ') j++;//删去尾部空格
    line[j] = '\0';//字符串末尾添加结束字符
    if (line[j - 2] == ' ') {//这里处理换行符前为空格的特殊情况
        line[j - 2] = line[j - 1];
        line[j - 1] = '\0';
    }
}
int get_def(char* line) {//用于获取标识符与宏字符串
    remove_spaces(line);//处理空格
    if (strncmp(line, "#define", 7) == 0) {//如果识别到了"#define",那么判定为宏
        char name[21];
        char value[31];
        if (sscanf(line + 7, " %20s %30s", name, value) == 2) {
        //sscanf可以将字符串内容输入至其他字符串内,这里获取标识符与宏字符串
            int flag = 1;//判断宏是否符合题目要求,即由数字和字母组成
            if (!isalpha(name[0])) flag = 0;
            for (int i = 0; name[i] != '\0'; ++i)
                if (!isalnum(name[i])) flag = 0;
            if (flag) {
                strcpy(def[def_count].name, name);
                strcpy(def[def_count].value, value);
                def_count++;//正式获取一个合法宏!
            }
            return 1;
        }
    }
    return 0;
}
void replace_def(char* line) {//用于对语句中的宏进行替换
    char result[201];//替换后的结果
    int i = 0, j = 0;//i为原字符串的指针,j为结果字符串中的指针
    int len = strlen(line);//获取原长度
    while (line[i] == ' ') {//对开头的空格不做处理
        i++;
        result[j++] = ' ';
    }
    while (i < len) {
        if (line[i] == '"') {//对引号内的部分不做处理
            result[j++] = line[i++];
            while (i < len && line[i] != '"')
                result[j++] = line[i++];
            if (i < len) result[j++] = line[i++];
            continue;
        }
        int found = 0;//找到宏的标志
        for (int k = 0; k < def_count; k++) {
            int name_len = strlen(def[k].name);//标识符长度
            if (strncmp(line + i, def[k].name, name_len) == 0 && 
                (i + name_len == len || !isalnum(line[i + name_len])) 
                && !isalnum(line[i - 1])) {
                //保证宏为单独隔开的子串,前后没有数字或字母
                strcpy(result + j, def[k].value);//替换宏
                j += strlen(def[k].value);//更新指针
                i += name_len;
                found = 1;//找到宏了!
                break;
            }
        }
        if (!found) result[j++] = line[i++];//没找到,将整个语句原样复制
    }
    result[j] = '\0';//补充空字符
    strcpy(line, result);//将结果复制回最终打印的字符串
}
int main(void) {
    char line[201];//用于存储输入的c++语句
    while (fgets(line, sizeof(line), stdin)) {//当读取一行语句
        if (strchr(line, '#') != NULL) //如果在这条语句中含有字符'#'
            if (get_def(line)) continue;//则获取其中的标识符与宏字符串
        if (strcmp(line, "@@\n") == 0) break; //如果语句为@@,结束读取
        replace_def(line);//替换其中的宏
        remove_spaces(line);//移除多余的空格
        if (strlen(line) > 0) printf("%s", line);//输出语句
    }
    return 0;
}

这题缕清思路后其实框架并不复杂,但用何种办法处理字符串困扰了我很久,也迫使我学习了很多关于此的技巧,例如string.h内的大部分函数以及双指针的灵活运用。

标签:name,++,PTA,空格,75,简单,字符串,line,def
From: https://blog.csdn.net/qq_37231166/article/details/143726024

相关文章

  • vue3使用tsParticles实现爆开五彩碎纸屑动效,简单高效免费(撒花特效、粒子效果)
    实现效果:tsParticles|ConfettiPreset|JavaScriptParticles,ConfettiandFireworksanimationsforyourwebsitetsParticles-Easilycreatehighlycustomizableparticles,confettiandfireworksanimationsandusethemasanimatedbackgroundsforyourweb......
  • 如何批量打水印?六个电脑屏幕水印批量设置方法分享!步骤既简单,又快速!
    如何批量打水印?你是否曾经为需要在大量文件或屏幕上添加水印而感到烦恼?电脑屏幕水印,作为版权保护和信息安全的重要手段,重要性不言而喻。然而,手动逐个添加水印不仅耗时费力,还容易出错。那么,有没有一种方法能够批量、快速地为电脑屏幕或文件添加水印呢?答案是肯定的。本文,将......
  • LCR 012. 寻找数组的中心下标(简单)(主站724)
    https://leetcode.cn/problems/tvdfij/https://leetcode.cn/problems/find-pivot-index/难度:☆☆☆题目:给你一个整数数组nums,请计算数组的中心下标。数组中心下标是数组的一个下标,其左侧所有元素相加的和等于右侧所有元素相加的和。如果中心下标位于数组最左端,那么左......
  • HTML5期末大作业:北京旅游网页设计制作(1页) 简单静态HTML网页作品 我的旅游网页作业成
    ......
  • pytorch简单识别CIFAR10彩色图片的卷积神经网络
    环境:python3.11.10pytorch2.3.0一、前期准备1.设置GPUimporttorchimporttorch.nnasnnimportmatplotlib.pyplotaspltimporttorchvisiondevice=torch.device("cuda"iftorch.cuda.is_available()else"cpu")device2.导入数据使用dataset下载CI......
  • 搭建本地大模型和知识库最简单的方法
    01、本地大模型越来越简单经过了一年多时间的迭代,大模型种类繁多,使用也越来越简单了。前排提示,文末有大模型AGI-CSDN独家资料包哦!在本地跑大模型,个人认为目前最好的软件肯定是Ollama无疑了,不管你是在PC上跑大模型,在Mac上跑大模型,还有在树莓派上跑大模型,我们都可以用Oll......
  • 使用 BCPL 语言编写简单的文字识别程序
    BCPL(BasicCombinedProgrammingLanguage)是一种较早期的编程语言,以其简洁性和基础特性著称。以下文章将使用BCPL编写一个简单的文字识别程序,通过分析字形和字符之间的相似性来实现基本的文字识别功能。代码实现bcplGET"libhdr"LETWIDTH=5LETHEIGHT=7//定义字......
  • 虚拟定位之简单易操作
    **一.苹果手机端(无需越狱)工具:爱思助手:https://www.i4.cn/先拿数据线连接苹果手机,连接成功后修改后可以断开数据线连接,修改是否成功,可以去微信共享位置验证。二.电脑端工具:雷电模拟器:https://www.ldmnq.com可以改游戏的战区位置哦!......
  • SD卡误删视频怎么恢复?这3个简单方法,要学会
    SD卡作为一类通用的存储媒介,目前被众多电子产品采用,包括:智能手机和摄像机等。然而,在平时使用的过程中有时无意失误或者其他的原因,难免会导致存储的视频数据被意外删除。那么遇到这种情况sd卡误删视频怎么恢复?本文将详细介绍SD卡误删视频的恢复方法,帮助你轻松找回丢失的SD卡视频......
  • PTA-C语言-数组-字符串转换成十进制整数
    题目:输入一个以#结束的字符串,本题要求滤去所有的非十六进制字符(不分大小写),组成一个新的表示十六进制数字的字符串,然后将其转换为十进制数后输出。如果在第一个十六进制字符之前存在字符“-”,则代表该数是负数。输入格式:输入在一行中给出一个以#结束的非空字符串。输出格式:......