删除空格后的垃圾输出

garbage output after whitespace removing

我有这个代码:

     int i =0;
  char * str = "ar   bitrary             whitespace";

  int whitespace=0,index;
  for(index = 0;index < strlen(str);index++)
    {
      if(isspace(str[index]) != 0)
    {
      whitespace++;
    }
    }


  char * tmp = (char *)calloc(strlen(str)-whitespace +1,sizeof(char));

  memset(tmp,'[=10=]',strlen(tmp)+1);

  while(i < strlen(str))
    {
      if(isspace(str[i]) != 0)
    {
      i++;
      continue;
    }else if(isspace(str[i]) == 0)
    {

      strcat(tmp,&str[i]);
      i++;
    }

    }


  printf("\nnew string is: %s \n",tmp);

问题是输出是一个没有删除空格的字符串+一些垃圾字符。 我已经使用 memset 来 null 终止 tmp,有问题吗?

我不会看你的问题,你肯定会覆盖'[=12=]'终止符。

既然我看了你的问题,看来你需要更好地理解字符串和数组,

  1. 永远不要写while (i < strlen(str))
  2. 不要使用 strcat() 添加单个字符,您显然确实覆盖了那里的 '[=12=]'。此外,永远不要使用 strcat() 来连接多个字符串。

也值得注意,

  • memset()calloc() 之后已经初始化为 0。这意味着您正在执行一些不必要的事情,并且尝试了两次,就好像第一次失败了一样,我可以保证它没有。

    事实上,既然你已经使用了calloc()并且tmp指向的所有字节都是0那么strlen(tmp)将return 0,因此您的 memset() 等同于

    tmp[0] = '[=10=]';
    

    你真的不需要初始化 tmp 除非你最终从 str.

  • 复制实际字节

我总是建议不要 calloc() 字符串,因为

  1. 你真的不需要对某些东西进行两次初始化。
  2. 您应该确保您的代码确实考虑了终止 '[=12=]',而不是因为您 calloc() 就简单地假设它在那里。这是一个你用 calloc() 隐藏的错误,但它会在某个时候出现。

试试这个,看看你能不能理解我改变的原因

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

int main(void)
{
    int whitespace;
    int length;
    char *str = "ar   bitrary             whitespace";
    char *tmp;

    whitespace = 0;
    for (length = 0; str[length] != '[=11=]'; ++length) {
        if (isspace(str[length]) != 0) {
            whitespace++;
        }
    }
    tmp = malloc(length - whitespace + 1);
    if (tmp == NULL)
        return -1;
    for (int i = 0, j = 0; str[i] != '[=11=]'; ++i) {
        if (isspace(str[i]) != 0)
            continue;
        tmp[j++] = str[i];
    }
    tmp[length - whitespace] = '[=11=]';
    printf("new string is: %s\n",tmp);

    free(tmp);
    return 0;
}

这是你的问题

 memset(tmp,'[=10=]',strlen(tmp)+1);

strlen(tmp) 通过在 tmp 中寻找 '[=12=]' 来工作,这里是先有鸡还是先有蛋的情况。

你不应该以任何方式进行 memset,只需在完成复制后添加 '[=12=]'

并且不要使用 strcat,而是维护一个指向 tmp 的指针,只需执行 *p = str[i] 然后递增 p

源字符串的长度可以在这个循环之前计算出来

for(index = 0;index < strlen(str);index++)

否则,如果代码不会被优化,函数 strlen 将在循环的每次迭代中被调用。事实上,使用该功能对于这样的任务来说是多余的。

此声明

memset(tmp,'[=11=]',strlen(tmp)+1);

没有意义,因为 calloc 的调用已经用零初始化了内存。

此声明

strcat(tmp,&str[i]);

还会从源字符串中复制位置 i 之后的空格。因此它可以写入超出为指针指向的数组分配的内存 tmp.

您可以编写一个单独的函数,看起来就像这个演示程序中显示的那样

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

char * remove_blanks( const char *s )
{
    size_t n = 0;

    const char *p = s;

    do
    {
        if ( !isspace( ( unsigned char )*p ) ) ++n;
    } while ( *p++ );

    char *t = malloc( n );

    if ( t )
    {
        char *q = t;
        p = s;

        do
        {
            if ( !isspace( ( unsigned char )*p ) ) *q++ = *p;
        } while ( *p++ );
    }

    return t;
}

int main(void) 
{
    char * str = "ar   bitrary             whitespace";

    printf( "\"%s\"\n", str );

    char *t = remove_blanks( str );

    printf( "\"%s\"\n", t );

    free( t );
}   

程序输出为

"ar   bitrary             whitespace"
"arbitrarywhitespace"