蓝影不姓蓝 2024-03-06 12:08 采纳率: 0%
浏览 6

串的堆分配存储的基本操作(急!)

为什么我删除完打印字符串验证还会多出两行字符串

#include<stdio.h>
#include<stdlib.h>

typedef struct {
    char* ch;     // 若是非空串,则按串长分配储存区,否则ch为NULL
    int length;   // 串长度
}String;

int StrAssign(String* T, char* chars) {
    // 将chars字符串的内容复制到String对象T中
    if (T->ch) free(T->ch);   // 判断T中的ch是否为空,如果不为空则释放空间。
    int i;
    char* c;
    for (i = 0, c = chars; *c; i++, ++c); //求chars长度i;进入循环,条件是指针c指向的字符不为NULL,即字符串还没有结束。
    if (!i) { T->ch = NULL; T->length = 0; }//如果为空字符串
    else {
        T->ch = (char*)malloc(i * sizeof(char));//动态分配内存给T->ch用来存储字符串chars的内容。
        if (!T->ch) exit(-1);
        for (int j = 0; j < i; j++)
            T->ch[j] = chars[j];
        T->length = i;
    }
    return 1;
}

int InitString(String* T) {
    //初始化String
    T->ch = NULL;
    T->length = 0;
    return 1;
}

int StrLength(String S) {
    // 返回串S的长度
    return S.length;
}

int StrCompare(String S, String T) {
    // 若S>T,则返回值>0;若S=T,则返回值=0;若S<T,则返回值<0
    for (int i = 0; i < S.length && i < T.length; ++i) {
        if (S.ch[i] != T.ch[i])
            return(S.ch[i] - T.ch[i]);
    }
    return(S.length - T.length);
}

void ClearString(String* S) {
    // 将S清为空串
    if (S->ch) {
        free(S->ch);
        S->ch = NULL;
    }
    S->length = 0;
}

int Connect(String* T, String S1, String S2) {
    // T返回由S1和S2联接而成的新串
    if (T->ch) free(T->ch);   // 释放旧空间
    T->ch = (char*)malloc((S1.length + S2.length) * sizeof(char));
    int i;
    for (i = 0; i < S1.length; i++)
        T->ch[i] = S1.ch[i];
    T->length = S1.length + S2.length;
    for (i = 0; i < S2.length; i++)
        T->ch[i + S1.length] = S2.ch[i];
    return 1;
}

int SubString(String* Sub, String S, int pos, int len) {//返回子串
    // 用Sub返回串的第pos个字符起长度为len的子串
    InitString(Sub);
    if (pos < 1 || pos > S.length || len < 0 || len > S.length - pos + 1)
        return 0;
    if (Sub->ch) free(Sub->ch);   // 释放旧空间
    if (!len) {                   // 空子串
        Sub->ch = NULL;
        Sub->length = 0;
    }
    else {                      // 完整子串
        Sub->ch = (char*)malloc(len * sizeof(char));
        for (int i = 0; i < len; i++) {
            Sub->ch[i] = S.ch[pos - 1 + i];
        }
        Sub->length = len;
    }
    return 1;
}

// KMP 算法辅助函数,用于计算 next 数组
void GetNext(String T, int* next) {
    int m = StrLength(T);
    next[0] = -1;
    int j = 0;
    int k = -1;
    while (j < m - 1) {
        // T[k] 表示前缀,T[j] 表示后缀
        if (k == -1 || T.ch[j] == T.ch[k]) {
            j++;
            k++;
            next[j] = k;
        }
        else {
            k = next[k];
        }
    }
}

// KMP 算法的核心匹配函数
int KMPMatch(String S, String T) {
    int i = 0;
    int j = 0;
    int s_len = StrLength(S);
    int t_len = StrLength(T);
    int* next = (int*)malloc(sizeof(int) * t_len);
    GetNext(T, next);

    while (i < s_len && j < t_len) {
        if (j == -1 || S.ch[i] == T.ch[j]) {
            i++;
            j++;
        }
        else {
            j = next[j];  // 当字符不匹配时,j 回溯
        }
    }
    free(next);  // 释放 next 数组
    if (j == t_len)
        return i - j;  // 匹配成功,返回匹配索引
    else
        return -1;  // 匹配失败
}


int Index(String S, String T, int pos) {
    // 若主串 S 中第 pos 个字符之后存在和 T 相等的子串,则返回第一个这样的子串在 S 中的位置,否则返回 0
    if (pos > 0) {
        int n = StrLength(S);  // 主串 S 的长度
        int m = StrLength(T);  // 子串 T 的长度
        if (n == 0 || n < m || m == 0) {
            return 0;  // 如果主串为空或者主串长度小于子串长度或者子串为空,则直接返回 0
        }

        // 创建一个新的String结构体Sub,用于存储从S的pos位置开始的子串
        String Sub;
        InitString(&Sub);  // 初始化Sub为一个空的String类型
        if (!SubString(&Sub, S, pos, n - pos + 1)) {
            return 0;  // 如果获取子串失败,则返回 0
        }

        // 在新的子串Sub中查找T的位置
        int index = KMPMatch(Sub, T);
        ClearString(&Sub);  // 释放临时子串Sub的内存

        // 如果找到了T,则返回它在主串S中的实际位置,否则返回0
        if (index != -1) {
            return index + pos;  // 调整索引到主串 S 中的位置
        }
    }
    return 0;  // pos <= 0 或者没有找到子串
}
int Replace(String* S1, String S2, String S3) {
    // 用S3替换S1中所有出现的与S2相等的不重叠的子串
    String before;
    String after;
    String temp;
    //初始化before、after和temp为空字符串
    InitString(&temp);
    InitString(&before);
    InitString(&after);
    int i = Index(*S1, S2, 1);//i为主串S1中查找字符串S2的第一次出现的位置
    do {
        SubString(&before, *S1, 1, i - 1);//从S1中截取位于i之前的子串
        SubString(&after, *S1, i + StrLength(S2), StrLength(*S1) - i - StrLength(S2) + 1);//从S1中截取位于i+S2长度之后的子串
        Connect(&temp, before, S3);
        Connect(S1, temp, after);
        i = Index(*S1, S2, StrLength(temp) + 1);
    } while (Index(after, S2, 1));   // 循环条件是判断after中是否还包含S2
    return 1;
}

typedef struct {
    String StrHead[100];
    int Num;
}StrHeadList;//用于保存多个字符串

typedef struct {
    char Cmd;    // 命令符
    int s[3];    // 命令的串参数的内部名(最多3个)
    int num[2];  // 命令的数值参数(最多2个)
}ResultType;//用于保存命令行解析的结果

int In(char c, char OP[]) {
    // 判断c是否在OP中
    int flag = 0;
    int i = 0;
    while (OP[i] != '\0') {
        if (OP[i] == c) flag = 1;
        i++;
    }
    return flag;
}

int InitResultType(ResultType* R) {
    int i;
    R->Cmd = '#';
    for (i = 0; i < 3; i++) { R->s[i] = -1; }
    for (i = 0; i < 2; i++) { R->num[i] = -1; }
    return 1;
}

int InitStrHeadList(StrHeadList* L) {
    //将StrHead数组的每个元素初始化为一个空字符串,将Num初始化为0
    for (int i = 0; i < 100; i++) {
        InitString(&L->StrHead[i]);
    }
    L->Num = 0;
    return 1;
}


int CmdAnalyse(ResultType* R, char str[], StrHeadList* L) {
    //命令行分析函数 ,命令行存放在str中,返回ResultType
    char* p;
    char* q;
    char temp[100];
    int strcnt = 0;
    int numcnt = 0;
    int i;
    p = str;
    // 读入命令符
    char OP[11] = { 'A','E','C','L','S','I','R','P','D','Q','\0' };
    for (; *p == ' '; p++);  // 跳过空格,找到第一个非空格字符作为命令符
    if (*p == '\0') return -1;
    q = p + 1;//判断命令符后是否为空格或结束符
    if (In(*p, OP) && (*q == ' ' || *q == '\0')) { R->Cmd = *p; p++; }
    else return -1;
    for (; *p == ' '; p++);
    if (*p == '\0') return 1;
    // 读入参数
    for (int n = 1; n <= 3; n++) { // 最多读入3个参数
        for (; *p == ' '; p++);
        if (*p == '\'') { //为字符串时
            p++;
            for (i = 0; *p != '\''; p++, i++) temp[i] = *p;
            temp[i] = '\0';
            StrAssign(&L->StrHead[L->Num], temp);
            R->s[strcnt] = L->Num;//将该字符串的位置编号存储到R->s数组中
            L->Num++;
            p++; strcnt++;
        }
        else if (*p >= '0' && *p <= '9') { //为内部名时
            if (R->Cmd == 'S' && R->s[0] != -1) {
                for (i = 0; *p != ' '; p++, i++) temp[i] = *p;
                temp[i] = '\0';
                R->num[numcnt] = atoi(temp);
                numcnt++;
            }
            else { // 为数值时
                for (i = 0; *p != ' '; p++, i++) temp[i] = *p;
                temp[i] = '\0';
                R->s[strcnt] = atoi(temp);
                strcnt++;
            }
        }
        else if (*p == '\0') break; // 仅有回车输入
    }
    return 1;
}

int Menu() {
    printf("命令格式菜单:\n");
    printf("(1)赋值:             格式:A <串标识> <回车>\n");
    printf("(2)判相等:           格式:E <串标识1> <串标识2> <回车>\n");
    printf("(3)连接:             格式:C <串标识1> <串标识2> <回车>\n");
    printf("(4)求串长度:         格式:L <串标识> <回车>\n");
    printf("(5)求子串:           格式:S <串标识> <数1> <数2> <回车>\n");
    printf("(6)子串定位:         格式:I <串标识1> <串标识2> <回车>\n");
    printf("(7)串替换:           格式:R <串标识1> <串标识2> <串标识3> <回车>\n");
    printf("(8)显示系统所有串:   格式:P <回车>\n");
    printf("(9)删除串:           格式:D <内部名> <回车>\n");
    printf("(10)退出串演示:      格式:Q <回车>\n");
    return 1;
}


void PrintString(String S) {
    // 打印字符串
    printf("\'");
    for (int i = 0; i < S.length; i++) {
        printf("%c", S.ch[i]);
    }
    printf("\'");

}
int ReadCmd(char str[]) {     //读入命令行的函数,用字符数组返回
    int i;
    for (i = 0; i < 100 && (str[i] = getchar()) != '\n'; i++);//直到遇到换行符(\n)或者达到最大字符长度100为止
    if (i >= 100) return -2;
    str[i] = '\0';//将字符数组的末尾设置为字符('\0'),以确保字符串结束
    return 0;
}

int CmdOpretor(ResultType R, StrHeadList* L) {
    // 命令行操作函数,读入ResultType,判断参数合法性并对串头表进行操作
    int i;
    switch (R.Cmd) {
    case 'A':
        // 赋值操作
        if (R.s[0] == -1) {
            printf("参数非法");
        }
        else {
            printf("%d   ", R.s[0]);
            PrintString(L->StrHead[R.s[0]]);
        }
        break;
    case 'E':
        // 判相等
        if (R.s[0] != -1 && R.s[1] != -1) {
            if (StrCompare(L->StrHead[R.s[0]], L->StrHead[R.s[1]])) printf("UNEQUAL");
            else printf("EQUAL");
        }
        else {
            printf("参数非法");
        }
        break;
    case 'C':
        // 联接两个串
        if (R.s[0] != -1 && R.s[1] != -1) {
            Connect(&L->StrHead[L->Num], L->StrHead[R.s[0]], L->StrHead[R.s[1]]);
            PrintString(L->StrHead[L->Num]);
            L->Num++;
        }
        else { printf("参数非法"); }
        break;
    case 'I':
        // 子串的定位
        if (R.s[0] != -1 && R.s[1] != -1) {
            int x;
            x = Index(L->StrHead[R.s[0]], L->StrHead[R.s[1]], 1);
            if (x) printf("%d", x);
            else printf("参数非法");
        }
        else { printf("参数非法"); }
        break;
    case 'R':
        // 串的替换
        if (R.s[0] != -1 && R.s[1] != -1 && R.s[2] != -1) {
            Replace(&L->StrHead[R.s[0]], L->StrHead[R.s[1]], L->StrHead[R.s[2]]);
            PrintString(L->StrHead[R.s[0]]);
        }
        else { printf("参数非法"); }
        break;
    case 'S':
        // 求子串
        if (R.num[0] != -1 && R.num[1] != -1 && R.s[0] != -1) {
            if (SubString(&L->StrHead[L->Num], L->StrHead[R.s[0]], R.num[0], R.num[1])) {
                PrintString(L->StrHead[L->Num]);
                L->Num++;
            }
            else { printf("参数非法"); }
        }
        else { printf("参数非法"); }
        break;
    case 'L':
        // 得到串的长度
        if (R.s[0] != -1) {
            printf("%d", StrLength(L->StrHead[R.s[0]]));
        }
        else { printf("参数非法"); }
        break;
    case 'P':
        // 显示串头表中串名和串值
        for (i = 0; i < L->Num; i++) {
            if (L->StrHead[i].length != 0 && L->StrHead[i].ch != NULL) {
                printf("%d   ", i);
                PrintString(L->StrHead[i]);
                printf("\n");
            }
        }
        break;
    case 'D':
        // 删除某串
        if (R.s[0] != -1) {
            ClearString(&L->StrHead[R.s[0]]);
        }
        else { printf("参数非法"); }
        break;
    case '#':
        // 命令符错误
        printf("ERROR");
        break;
    case 'Q':
        // 退出
        break;
    }
    return 1;
}

int main() {
    Menu();
    char str[100];//定义一个字符数组str,用于存储用户输入的命令
    ResultType R;
    StrHeadList L;
    InitStrHeadList(&L);
    do {//进入do-while循环,该循环将一直执行直到用户输入的命令为'Q'。
        InitResultType(&R);
        ReadCmd(str);
        if (CmdAnalyse(&R, str, &L)) {
            CmdOpretor(R, &L);
        }
        else {
            printf("参数错误");
        }
        printf("\n");
    } while (R.Cmd != 'Q');
    return 1;
}


img

  • 写回答

4条回答 默认 最新

  • 专家-赤兔[在线] 全栈领域优质创作者 2024-03-06 12:09
    关注

    引自免费微信小程序:皆我百晓生

    在您给出的代码中,您没有具体描述删除完字符串后打印时出现两行多余字符串的问题。然而,根据您提供的代码,我推测可能是因为在执行Replace函数替换子串之后,没有正确更新StrHeadList中的字符串状态,导致在后续操作中打印了未清理或未正确更新的字符串。

    针对Replace函数:

    int Replace(String* S1, String S2, String S3) {
        // 用S3替换S1中所有出现的与S2相等的不重叠的子串
        String before;
        String after;
        String temp;
        // 初始化before、after和temp为空字符串
        InitString(&temp);
        InitString(&before);
        InitString(&after);
        int i = Index(*S1, S2, 1); // i为主串S1中查找字符串S2的第一次出现的位置
        do {
            SubString(&before, *S1, 1, i - 1); // 从S1中截取位于i之前的子串
            SubString(&after, *S1, i + StrLength(S2), StrLength(*S1) - i - StrLength(S2) + 1); // 从S1中截取位于i+S2长度之后的子串
            Connect(&temp, before, S3);
            Connect(S1, temp, after);
            
            // 在这里添加一行释放原有S1内存的操作
            ClearString(S1);
    
            // 将连接后的子串赋值给S1
            *S1 = temp;
    
            i = Index(*S1, S2, StrLength(temp) + 1);
        } while (i != -1 && Index(after, S2, 1));   // 循环条件是判断after中是否还包含S2且i表示找到了子串位置
    
        return 1;
    }
    

    请注意,在将新的子串连接到S1之前,需要先调用ClearString清空原S1的内容,然后将新生成的子串赋值给S1。这样可以确保每次替换后S1指向的是最新的完整字符串,并避免了因旧内容未被清除而导致的多余字符串输出问题。同时,请注意对循环条件进行了调整以保证循环结束的正确性。

    评论

报告相同问题?

问题事件

  • 修改了问题 3月6日
  • 创建了问题 3月6日

悬赏问题

  • ¥15 单纯型python实现编译报错
  • ¥15 c++2013读写oracle
  • ¥15 c++ gmssl sm2验签demo
  • ¥15 关于模的完全剩余系(关键词-数学方法)
  • ¥15 有没有人懂这个博图程序怎么写,还要跟SFB连接,真的不会,求帮助
  • ¥15 PVE8.2.7无法成功使用a5000的vGPU,什么原因
  • ¥15 is not in the mmseg::model registry。报错,模型注册表找不到自定义模块。
  • ¥15 keil官网下载psn序列号在哪
  • ¥15 想用adb命令做一个通话软件,播放录音
  • ¥30 Pytorch深度学习服务器跑不通问题解决?