C从字符串中删除特殊字符

时间:2019-06-27 17:04:50

标签: c string replace char segmentation-fault

我是C语言的新手,我创建了一个函数,该函数从字符串中删除特殊字符并返回一个新字符串(不包含特殊字符)。

乍一看,这似乎运行良好,我现在需要在(巨大)文本文件(一百万个句子)的行上运行此功能。在几千行/句(大约4,000个句子)之后,我遇到了段错误。

我对C语言中的内存分配和字符串没有太多的经验,我试图弄清楚我的代码有什么问题,不幸的是,没有运气。 这是代码:

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

char *preproccessString(char *str) {
    // Create a new string of the size of the input string, so this might be bigger than needed but should never be too small
    char *result = malloc(sizeof(str));
    // Array of allowed chars with a 0 on the end to know when the end of the array is reached, I don't know if there is a more elegant way to do this
    // Changed from array to string for sake of simplicity
    char *allowedCharsArray = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    // Initalize two integers
    // i will be increased for every char in the string
    int i = 0;
    // j will be increased every time a new char is added to the result
    int j = 0;
    // Loop over the input string
    while (str[i] != '\0') {
        // l will be increased for every char in the allowed chars array
        int l = 0;
        // Loop over the chars in the allowed chars array
        while (allowedCharsArray[l] != '\0') {
            // If the char (From the input string) currently under consideration (index i) is present in the allowed chars array
            if (allowedCharsArray[l] == toupper(str[i])) {
                // Set char at index j of result string to uppercase version of char currently under consideration
                result[j] = toupper(str[i]);
                j++;
            }
            l++;
        }
        i++;
    }
    return result;
}

这是程序的其余部分,我认为问题可能在这里。

int main(int argc, char *argv[]) {
    char const * const fileName = argv[1];
    FILE *file = fopen(fileName, "r");
    char line[256];

    while (fgets(line, sizeof(line), file)) {
        printf("%s\n", preproccessString(line)); 
    }

    fclose(file);

    return 0;
}

5 个答案:

答案 0 :(得分:5)

您有几个问题。

  1. 您没有分配足够的空间。 sizeof(str)是指针的大小,而不是字符串的长度。您需要使用
char *result = malloc(strlen(str) + 1);

+ 1用于终止空字节。

  1. 您没有在结果字符串中添加一个终止的空字节。添加
result[j] = '\0';

return result;之前

  1. 一旦发现该字符与允许的字符匹配,就无需继续遍历其余允许的字符。在break之后添加j++

  2. 您的main()函数永远不会释放preprocessString()的结果,因此您可能内存不足。

while (fgets(line, sizeof(line), file)) {
    char *processed = preproccessString(line);
    printf("%s\n", processed); 
    free(processed);
}

如果在结果字符串中有调用者传递,而不是在函数中分配它,则可以解决其中的大多数问题。只需在char[256]函数中使用两个main()数组即可。

int main(int argc, char *argv[])
{
    char const* const fileName = argv[1];
    FILE* file = fopen(fileName, "r");
    char line[256], processed[256];

    while (fgets(line, sizeof(line), file)) {
        processString(line, processed);
        printf("%s\n", processed); 
    }

    fclose(file);

    return 0;
}

然后只需更改功能,以使参数为:

void preprocessString(const char *str, char *result)

答案 1 :(得分:1)

一个好的经验法则是确保每个malloc / calloc调用都有一个空闲空间。

Valgrind是一个值得关注的好工具。很好地捕获此类错误。

答案 2 :(得分:1)

以下建议的代码:

  1. 干净地编译
  2. 执行所需的功能
  3. 正确检查错误
  4. 正确检查输入字符串参数的长度
  5. 利用strchr()的特性还检查终止的NUL字节
  6. 限制了局部变量的可见范围
  7. 希望通过将返回值传递给free()
  8. 来正确清除调用函数
  9. 调用函数应检查返回值是否为NULL
  10. 在进行隐式转换时通知用户知道并接受的编译器。
  11. allowedCharsArray移动到“文件静态范围”,因此不必在每次通过循环时都重新初始化,并标记为“ const”以帮助编译器捕获错误

,现在是建议的代码:(注:按评论编辑)

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

char *preproccessString(char *str) 
{
    // Create a new string of the size of the input string, so this might be bigger than needed but should never be too small
    char *result = calloc( sizeof( char ),  strlen(str)+1);
    if( !result )
    {
        perror( "calloc failed" );
        return NULL;
    }

    // Array of allowed chars 
    static const char *allowedCharsArray = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

    // Loop over the input string
    for( int  j=0, i=0; str[i]; i++) 
    {
        if( strchr( allowedCharsArray, (char)toupper( str[i] ) ) )
        {
            // Set char at index j of result string to uppercase version of char currently under consideration
            result[j] = (char)toupper(str[i]);
            j++;
        }
    }
    return result;
}

答案 3 :(得分:1)

您的代码中存在一些主要问题:

  • 分配的内存量不正确,sizeof(str)指针中的字节数,而不是它指向的字符串的长度,这也是不正确的。您应该写char *result = malloc(strlen(str) + 1);

  • 从未释放在preproccessString中分配的内存,这会导致内存泄漏,并可能导致程序在很多大文件上耗尽内存。

  • 您不要在result字符串的末尾设置空终止符

较少的问题:

  • 您不检查是否传递了文件名,也不检查fopen()是否成功。
  • preproccessString中有一个错字,应该是preprocessString
  • 您可以通过传递适当大小的目标数组来避免内存分配。
  • 您可以使用isalpha代替测试每个字母
  • 在将char值传递给unsigned char时,应将toupper的值强制转换为char,因为toupper可能是带符号的类型,而EOF对于负值是未定义的除了#include <ctype.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> // transform the string in `str` into buffer dest, keeping only letters and uppercasing them. char *preprocessString(char *dest, const char *str) { int i, j; for (i = j = 0; str[i] != '\0'; i++) { if (isalpha((unsigned char)str[i]) dest[j++] = toupper((unsigned char)str[i]); } dest[j] = '\0'; return dest; } int main(int argc, char *argv[]) { char line[256]; char dest[256]; char *filename; FILE *file; if (argc < 2) { fprintf(stderr, "missing filename argument\n"); return 1; } filename = argv[1]; if ((file = fopen(filename, "r")) == NULL) { fprintf(stderr, "cannot open %s: %s\n", filename, strerror(errno)); return 1; } while (fgets(line, sizeof(line), file)) { printf("%s\n", preprocessString(dest, line)); } fclose(file); return 0; }
  • 源文件中的注释太多,大多数注释是显而易见的,但是会使代码的可读性降低。

这是修改后的版本:

mogrify -resize 350 *.jpeg

答案 4 :(得分:0)

我认为问题在于您正在使用malloc从堆中分配内存,并且由于您一次又一次地调用此函数而导致内存不足。 要解决此问题,您必须在preprocessString函数返回的指针上调用free()函数 在您的主要区块

char *result=preprocessString(inputstring);
//Do whatever you want to do with this result
free(result);