【问题标题】:Removing spaces from strings从字符串中删除空格
【发布时间】:2017-11-20 03:53:50
【问题描述】:

我尝试编写一个函数,它获取一个字符串并创建一个新字符串,但没有多个空格(单词之间只留 1 个空格)。

到目前为止,我写了这个,但由于某种原因它崩溃了,调试器什么也没显示。

我也不知道需要把free函数放在哪里...

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

char* upgradestring(char* oldtext);

int main()
{
    char str1[] =  "Chocolate     Can   Boost   Your Workout" ;

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

    printf("\n%s\n", upgradestring(str1));

    return 0;
}

char* upgradestring(char* oldtext)
{
    int i,j, count = 1;
    char *newstr;

    for (i = 0; oldtext[i] != '\0'; i++)
    {
        if (oldtext[i] != ' ')
            count++;
        else if (oldtext[i - 1] != ' ')
            count++;
    }
    newstr = (char*)malloc(count * sizeof(char));
    if (newstr == NULL)
        exit(1);

    for (i = 0, j = 0; (oldtext[i] != '\0')|| j<(count+1); i++)
    {
        if (oldtext[i] != ' ')
        {
            newstr[j] = oldtext[i];
            j++;
        }
        else if (oldtext[i - 1] != ' ')
        {
            newstr[j] = oldtext[i];
            j++;
        }
    }

    return newstr;
}

【问题讨论】:

  • 是的,我需要返回一个与旧字符串相同但单词之间只有 1 个空格的新字符串
  • count 是新字符串的大小,有 1 个空格,count+1 是 '\0'

标签: c string function pointers memory-management


【解决方案1】:

很好的尝试,但让我们专注于何时需要释放内存。您在函数内部动态分配内存,然后在 printf 中调用该函数,这将允许打印字符串,但是您将如何解除分配呢?使用指针分配函数的返回值,打印它,然后释放它!

此外,您需要为新字符串中的多个字符分配空间,并为 null 终止符分配一个空间,因为 C 字符串要求它与来自标头的函数顺利工作,例如 @ 987654322@.

此外,我们不会在 C 中转换 malloc() 返回的内容,阅读更多 here

还有这个:

else if (oldtext[i - 1] != ' ')

应该写成:

else if (i != 0 && oldtext[i - 1] != ' ')

i为0时,避免访问超出范围的oldtext[-1]

最后,您在填充新字符串时使用的条件最好使用逻辑 AND 而不是 OR,因为一旦任一条件为假,我们就必须停止(我们不想读取 null原始字符串的终止符,或超过新字符串的大小)。

把所有东西放在一起,我们:

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

char* upgradestring(char* oldtext)
{
    int i, j, count = 0;
    // compute 'count'
    for(i = 0; oldtext[i]; i++)
    {
        if (oldtext[i] != ' ')
            count++;
        else if (i != 0 && oldtext[i - 1] != ' ')
            count++;
    }
    char* newstr = malloc(count + 1); // PLUS ONE for the null terminator
    if(!newstr) // check if malloc failed
    {
        printf("Malloc failed\n");
        return 0;
    }
    // populate 'newstr'. We need to stop when either condition is false
    for (i = 0, j = 0; (oldtext[i]) && j<(count+1); i++)
    {
        // Same as your code
    }
    // Assign the null terminator!
    newstr[j] = '\0';
    return newstr;
}

int main(void) {
    char str1[] =  "Chocolate     Can   Boost   Your Workout" ;
    // store the result of your function into 'newstr'
    char* newstr = upgradestring(str1);
    // print it
    printf("%s\n", newstr);
    // free it, since you no longer need it!
    free(newstr);
    return 0;
}

输出:

巧克力可以促进锻炼

【讨论】:

  • 感谢您的帮助!现在我知道我的想法出了什么问题
  • 总是很高兴回答问题@420Friendlly,继续加油! =)
【解决方案2】:

您正在寻址[i-1],如果i==0,它不在原始数组的范围内。

你可以这样做:

简单地一个一个地复制,如果char是' ',则在它是' '时继续跳过,否则前进一个。

static size_t newlen(char const *o)
{
    size_t r=0;
    while(*o){
        r++;
        if(*o==' ') 
            while(*o==' ')
                o++;
        else
           o++;
    }
    return r;

}
char *upgradestring(char const *o)
{
    char *r, *p;
    size_t len = newlen(o);
    if( 0==(r = malloc(len+1)))
        return 0;
    r[len]=0;
    for(p=r;*o;){
        *p++=*o;
        if(*o==' ') 
           while(*o==' ') 
              o++;
        else
           o++;
    }
    return r;
}
int main()
{
    char str1[] =  "Chocolate     Can   Boost   Your Workout" ;
    char *new;
    printf("%s\n", str1);
    if(0 == (new = upgradestring(str1)))
        return 1;
    printf("%s\n", new);
    free(new);
}

分配失败最好通过返回码来表示(您不希望库函数在失败时中止您的程序)。

为了能够释放返回的字符串,首先必须将它捕获到一个变量中。

【讨论】:

    【解决方案3】:

    #include <stdio.h>
    #include <stdlib.h>
    
    char *upgradestring(char *oldtext)
    {
    size_t len,src,dst,spc;
    char *result;
    
            // First pass: count needed size
    for (len=src=spc=0;oldtext[src]; src++){
            if (oldtext[src] != ' ') spc=0;       // non-space needs space
            else if(spc++) continue;              // skip non first space
            len++;
            }
    
    result= malloc (1+len);
    
            // Second pass: copy(K&R style)
    for (dst=src=spc=0; (result[dst] = oldtext[src]) ; src++){
            if (oldtext[src] != ' ') spc=0;      // non-space: rest counter
            else if(spc++) continue;             // skip non-first space
            dst++;
            }
    
    return result;
    }
    

    简化版:不计算第一遍的大小,而是从与原始大小相同的大小开始,并在第二遍之后调整大小。 (strdup()可以替换成strlen+malloc+memcpy)


    char * strdup(char *);
    
    char *upgradestring2(char *oldtext)
    {
    size_t src,dst,spc;
    char *result;
    
    result= strdup (oldtext);
    
            // edit the copy, skipping all spaces except the first
    for (dst=src=spc=0; result[src] ; src++){
            if (result[src] != ' ') spc=0;  // non-space:reset counter
            else if(spc++) continue;        // skip space,except the first
    
            result[dst++] = result[src]; // Copy
            }
    result[dst] = 0;// terminate string;
    
    // result=realloc(result, dst+1);
    
    return result;
    }
    

    【讨论】:

      【解决方案4】:

      首先,您的程序中没有使用来自标题&lt;string.h&gt; 的声明。因此这个指令

      #include <string.h>
      

      可以从程序中删除。

      根据 C 标准,不带参数的函数main 应声明为

      int main( void )
      

      具有奇怪名称upgradestring:) 的函数不会更改参数。因此它应该被声明为

      char* upgradestring( const char* oldtext);
                           ^^^^^
      

      考虑到源字符串可以以空格开头。在这种情况下,像这样的陈述

          else if (oldtext[i - 1] != ' ')
              count++;
      

      导致未定义的行为,因为当i 等于0 时,尝试访问字符串之外的内存。

      条件

      (oldtext[i] != '\0')|| j<(count+1); 
      

      至少应该写成这样

      (oldtext[i] != '\0') && j<(count+1); 
                           ^^^ 
      

      尽管检查索引j 就足够了,因为它不能大于源字符串的长度。

      您忘记在结果字符串后面附加终止零 '\0'

      另外,使用此语句退出函数也不是一个好主意

      exit(1);
      

      在这种情况下,您可以只返回一个空指针。

      并且在退出程序之前应该释放分配的内存。

      正如之前提到的,源字符串可以以空格开头,也可以有多余的尾随空格。我认为从结果字符串中排除它们在逻辑上是一致的。

      通常将空格字符与制表符配对。此外,C 在标题&lt;ctype.h&gt; 中声明了一个特殊函数isblank,用于检查字符是空格还是空白。 (据我所知MS VS不支持这个功能)

      考虑到所有这些,可以按照以下方式定义函数,如演示程序中所示。

      #include <stdio.h>
      #include <stdlib.h>
      
      char * trim_blanks( const char *s )
      {
          size_t n = 0;
      
          const char *p = s;
      
          //  skip leading blanks
          while ( *p == ' ' || *p == '\t' ) ++p;
      
          _Bool last_blank = 0;
      
          for ( ; *p; ++p )
          {
              ++n;
              if ( ( last_blank = ( *p == ' ' || *p == '\t' ) ) )
              {
                  while (  p[1] == ' ' || p[1] == '\t' ) ++p;
              }           
          }
      
          if ( last_blank ) --n;
      
          char *q = malloc( n + 1 );
      
          if ( q )
          {
              p = s;
      
              //  skip leading blanks
              while ( *p == ' ' || *p == '\t' ) ++p;
      
              size_t i = 0;
              for ( ; i < n; i++, ++p )
              {
                  q[i] = *p == '\t' ? ' ' : *p;
                  if ( q[i] == ' ' )
                  {
                      while (  p[1] == ' ' || p[1] == '\t' ) ++p;
                  }
              }
      
              q[i] = '\0';
          }
      
          return q;
      }
      
      int main(void) 
      {
          char s[] =  "\t\tChocolate  \t   Can \t  Boost   Your Workout   ";
      
          printf( "\"%s\"\n", s );
      
          char *t = trim_blanks( s );
      
          printf( "\"%s\"\n", t );
      
          free( t );
      
          return 0;
      }
      

      程序输出是

      "       Chocolate      Can    Boost   Your Workout   "
      "Chocolate Can Boost Your Workout"
      

      【讨论】:

        猜你喜欢
        • 2011-09-21
        • 1970-01-01
        • 2012-03-25
        • 2012-09-05
        • 1970-01-01
        相关资源
        最近更新 更多