尝试分割字符串,但得到了混乱的子字符串。

3
我尝试将一个字符串分割成三元组字符串。但结果是生成的子字符串总是混乱的。需要输入长度和字符**类型,因为后面我会将它们用作Python调用该函数的参数。 这是我编写的函数。
struct strArrIntArr getSearchArr(char* input, int length) {

    struct strArrIntArr nameIndArr;
    // flag of same bit
    int same;
    // flag/index of identical strings
    int flag = 0;
    // how many identical strings
    int num = 0;
    // array of split strings
    char** nameArr = (char **)malloc(sizeof(char *) * (length - 2));
    if ( nameArr == NULL ) exit(0);
    // numbers of every split string
    int* valueArr = (int* )malloc(sizeof(int) * (length-2));
    if ( valueArr == NULL ) exit(0);

    // loop length of search string -2 times (3-gram)
    for(int i = 0; i<length-2; i++){
        if(flag==0){
            nameArr[i - num] = (char *)malloc(sizeof(char) * 3);
            if ( nameArr[i - num] == NULL ) exit(0);
            printf("----i------------%d------\n", i);
            printf("----i-num--------%d------\n", i-num);
        }
        flag = 0;
        // compare splitting string with existing split strings,
        // if a string exists, it would not be stored
        for(int k=0; k<i-num; k++){
            same = 0;
            for(int j=0; j<3; j++){
                if(input[i + j] == nameArr[k][j]){
                    same ++;
                }
            }
            // identical strings found, if all the three bits are the same
            if(same == 3){
                flag = k;
                num++;
                break;
            }
        }
        // if the current split string doesn't exist yet
        // put current split string to array
        if(flag == 0){
            for(int j=0; j<3; j++){
                nameArr[i-num][j] = input[i + j];
                valueArr[i-num] = 1;
            }
        }else{
            valueArr[flag]++;
        }
        printf("-----string----%s\n", nameArr[i-num]);
    }

    // number of N-gram strings
    nameIndArr.length = length- 2- num;
    // array of N-gram strings
    nameIndArr.charArr = nameArr;
    nameIndArr.intArr = valueArr;
    return nameIndArr;
}

调用函数的方法:

int main(int argc, const char * argv[]) {
    int length = 30;
    char* input = (char *)malloc(sizeof(char) * length);
    input = "googleapis.com.wncln.wncln.org";

    // split the search string into N-gram strings
    // and count the numbers of every split string
    struct strArrIntArr nameIndArr = getSearchArr(input, length);
}

以下是结果。从17开始的字符串很混乱。

----i------------0------
----i-num--------0------
-----string----goo

----i------------1------
----i-num--------1------
-----string----oog

----i------------2------
----i-num--------2------
-----string----ogl

----i------------3------
----i-num--------3------
-----string----gle

----i------------4------
----i-num--------4------
-----string----lea

----i------------5------
----i-num--------5------
-----string----eap

----i------------6------
----i-num--------6------
-----string----api

----i------------7------
----i-num--------7------
-----string----pis

----i------------8------
----i-num--------8------
-----string----is.

----i------------9------
----i-num--------9------
-----string----s.c

----i------------10------
----i-num--------10------
-----string----.co

----i------------11------
----i-num--------11------
-----string----com

----i------------12------
----i-num--------12------
-----string----om.

----i------------13------
----i-num--------13------
-----string----m.w

----i------------14------
----i-num--------14------
-----string----.wn

----i------------15------
----i-num--------15------
-----string----wnc

---i------------16------
----i-num--------16------
-----string----ncl

----i------------17------
----i-num--------17------
-----string----clnsole

----i------------18------
----i-num--------18------
-----string----ln.=C:

----i------------19------
----i-num--------19------
-----string----n.wgram 馻绚s

----i------------20------
----i-num--------20------
-----string----n.wgram 馻绚s

-----string----n.wgram 馻绚s

-----string----n.wgram 馻绚s

-----string----n.wgram 馻绚s

-----string----n.wgram 馻绚s

-----string----n.oiles(騛窑=

----i------------26------
----i-num--------21------
-----string----.orSModu鯽蓼t

----i------------27------
----i-num--------22------
-----string----org

在Win10操作系统下,使用Codeblocks 17.12和GCC 8.1.0。

要么使用 %.3s 进行打印,要么在字符串中包含空格和终止零字符。 - KamilCuk
我不确定这是否与问题有关,但input="googleapis.com.wncln.wncln.org"将导致内存泄漏。由于此字符串长度为30个字符,因此必须增加缓冲区大小以使用strcpy()。(您还必须为终止的空字符分配空间。)或者,可以仅删除malloc(),因为该字符串似乎没有被修改。(不能修改字符串常量。) - MikeCAT
nameArr[i - num] = (char *)malloc(sizeof(char) * 3); 中使用未初始化的自动变量 num,导致您正在调用未定义行为(这不应该是原因,因为首先将打印 0 作为 i - num)。 - MikeCAT
flag == 0 无法区分“未找到匹配项”和“与第一个字符串匹配”。 - MikeCAT
在修复了一些小问题后,使用 valgrind 运行您的程序显示它在 printf("-----string----%s\n", nameArr[i]); 访问未初始化的内存,即使已经初始化了 num=0。显然,在某些情况下,您没有填充(或分配?)nameArr[i]。赋值语句 input = "googleapis.com.wncln.wncln.org"; 并不会复制字符串,而是用您字符串字面量的地址覆盖指针。我建议使用 int length; char* input = strdup("googleapis.com.wncln.wncln.org"); length = strlen(input); - Bodo
显示剩余2条评论
2个回答

2
你在许多地方让生活变得复杂了:
  • 不要倒数计算:将“num”设置为唯一三字母组的计数,而不是重复项的计数。
  • 在函数中尽可能紧密地定义变量。有几个未初始化的变量。你在函数的开头声明了它们,但你只需要在本地块中使用它们。
  • 一旦分配就进行初始化。在你的代码中,你使用一个标志来确定是否创建新的字符串。分配字符串和初始化字符串的代码在不同的块中。这些块具有相同的标志作为条件,但标志在中间被更新。这可能会导致异步性,甚至在尝试初始化未分配的内存时出现错误。
  • 最好将字符串及其计数一起保存在结构体中。如果需要排序,这也会对你有所帮助。这还提供了一些简化:不要分配3字节的块,而是在结构体中保留四字节的char数组,以便所有条目都可以适当地以空字符结尾。这些不需要单独分配。
以下是另一种实现方式:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

struct tri {
    char str[4];        // trigraph: 3 chars and NUL
    int count;          // count of occurrences
};

struct stat {
    struct tri *tri;    // list of trigraphs with counts
    int size;           // number of trigraphs
};

/*
 *      Find string 'key' in list of trigraphs. Return the index
 *      or in the array or -1 if it isn't found.
 */
int find_trigraph(const struct tri *tri, int n, const char *key)
{
    for (int i = 0; i < n; i++) {
        int j = 0;

        while (j < 3 && tri[i].str[j] == key[j]) j++;
        if (j == 3) return i;
    }

    return -1;
}

/*
 *      Create an array of trigraphs from the input string.
 */
struct stat getSearchArr(char* input, int length)
{
    int num = 0;

    struct tri *tri = malloc(sizeof(*tri) * (length - 2));

    for(int i = 0; i < length - 2; i++) {
        int index = find_trigraph(tri, num, input + i);

        if (index < 0) {
            snprintf(tri[num].str, 4, "%.3s", input + i);       // see [1]
            tri[num].count = 1;             
            num++;
        } else {
            tri[index].count++;
        }
    }


    for(int i = 0; i < num; i++) {
        printf("#%d %s: %d\n", i, tri[i].str, tri[i].count);
    }

    struct stat stat = { tri, num };

    return stat;
}

/*
 *      Driver code
 */
int main(void)
{
    char *input = "googleapis.com.wncln.wncln.org";
    int length = strlen(input);

    struct stat stat = getSearchArr(input, length);

    // ... do stuff with stat ...

    free(stat.tri);

    return 0;
}

注脚1:我发现snprintf(str, n, "%.*s", len, str + offset)对于复制子字符串非常有用:结果不会溢出缓冲区并且它将以空字符结尾。确实应该有一个标准函数来实现这个功能,但是strcpy可能会导致溢出,而strncpy可能会使缓冲区未被终止。


1
你的代码看起来比我的干净得多,更容易阅读,我会根据你的建议修改我的代码,非常感谢!! - heisthere

0

这个答案试图修复现有的代码,而不是提出替代/更好的解决方案。

修复输出后

printf("-----string----%s\n", nameArr[i-num]);

在这个问题中,还有另一个重要的问题。

您想在nameArr[i-num]中存储3个字符并分配3个字符的空间。稍后,在上面显示的代码中将其打印为字符串。这需要在3个字符后添加一个尾随的'\0',因此您必须为4个字符分配内存,并附加'\0'或使用0初始化已分配的内存。使用calloc而不是malloc会自动将内存初始化为0。

这是源代码的修改版本

我还更改了main()中字符串值及其长度的初始化,以避免内存泄漏。

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


struct strArrIntArr {
    int length;
    char **charArr;
    int *intArr;
};

struct strArrIntArr getSearchArr(char* input, int length) {

    struct strArrIntArr nameIndArr;
    // flag of same bit
    int same;
    // flag/index of identical strings
    int flag = 0;
    // how many identical strings
    int num = 0;
    // array of split strings
    char** nameArr = (char **)malloc(sizeof(char *) * (length - 2));
    if ( nameArr == NULL ) exit(0);
    // numbers of every split string
    int* valueArr = (int* )malloc(sizeof(int) * (length-2));
    if ( valueArr == NULL ) exit(0);

    // loop length of search string -2 times (3-gram)
    for(int i = 0; i<length-2; i++){
        if(flag==0){
            nameArr[i - num] = (char *)malloc(sizeof(char) * 4);
            if ( nameArr[i - num] == NULL ) exit(0);
            printf("----i------------%d------\n", i);
            printf("----i-num--------%d------\n", i-num);
        }
        flag = 0;
        // compare splitting string with existing split strings,
        // if a string exists, it would not be stored
        for(int k=0; k<i-num; k++){
            same = 0;
            for(int j=0; j<3; j++){
                if(input[i + j] == nameArr[k][j]){
                    same ++;
                }
            }
            // identical strings found, if all the three bits are the same
            if(same == 3){
                flag = 1;
                num++;
                break;
            }
        }
        // if the current split string doesn't exist yet
        // put current split string to array
        if(flag == 0){
            for(int j=0; j<3; j++){
                nameArr[i-num][j] = input[i + j];
                valueArr[i-num] = 1;
            }
            nameArr[i-num][3] = '\0';
        }else{
            valueArr[flag]++;
        }
        printf("-----string----%s\n", nameArr[i-num]);
    }

    // number of N-gram strings
    nameIndArr.length = length- 2- num;
    // array of N-gram strings
    nameIndArr.charArr = nameArr;
    nameIndArr.intArr = valueArr;
    return nameIndArr;
}


int main(int argc, const char * argv[]) {
    int length;
    char* input = strdup("googleapis.com.wncln.wncln.org");
    length = strlen(input);

    // split the search string into N-gram strings
    // and count the numbers of every split string
    struct strArrIntArr nameIndArr = getSearchArr(input, length);
}

这个答案包含了更多的改进,我个人更喜欢它比修改后的原始解决方案。


nameArr[i-num][3] = input[i + j]; 这里我认为你的意思是 nameArr[i-num][3] = '\0'? 不管怎样,它运行得很好!!!非常感谢,你救了我的一天!!! - heisthere

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接