删除C语言中的字符串空格-为什么白色空格后面的单词也被删除了?

cvxl0en2  于 11个月前  发布在  其他
关注(0)|答案(6)|浏览(94)

我在Codewars上尝试一个初学者的C问题:我应该写一个函数,从给定的字符串中删除空格,然后返回一个新的(动态分配的)字符串。
范例:str_in中的输入字符串HELLO WORLD需要输出HELLOWORLD,但我的代码在noSpace_str中返回的是HELLOWORLD无处可寻,我试图理解为什么。
(提前感谢您的回答)
我的代码:

char *no_space(const char *str_in)
{
    int len = strlen(str_in);
    char *noSpace_str = (char *)malloc(len * sizeof(char)); 
  
    for (int i = 0; i < len; i++) {
        if (str_in[i] != ' ')
            noSpace_str[i] = str_in[i];
    }
  
    return noSpace_str;
}

字符串

nfg76nw0

nfg76nw01#

当你的代码遇到一个空格时,它不会复制任何东西。这会在缓冲区中留下一个空白,其中包含之前存在的任何东西,在这种情况下,是一个空字节(0x00)。10个字节用于终止字符串,因此字符串在单词“HELLO”之后结束。在不立即给出答案的情况下,你需要开始复制 * 在 * 空格之后,而不是忽略它。

r1wp621o

r1wp621o2#

除了其他好的答案,如果你只想分配所需的大小,而不是额外的,做2遍,计算你需要的第一遍。
第一个循环替换了strlen()strlen()本身就是一个遍历字符串的过程。只要代码遍历字符串,就可以计算非空格。

#include <stdlib.h>

char* no_space(const char *str_in) {
  size_t size_needed = 1; // For the null character.
  for (const char *s = str_in; *s; s++) {
    if (*s != ' ') {
      size_needed++;
    }
  }

  char *noSpace_str = malloc(size_needed); // Cast not needed
  if (noSpace_str) { // If allocation succeeded ...
    char *destination = noSpace_str;
    for (const char *s = str_in; *s; s++) {
      if (*s != ' ') {
        *destination++ = *s;
      }
    }
    *destination = '\0';
  }
  return noSpace_str;
}

字符串

xsuvu9jc

xsuvu9jc3#

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

  • 为新字符串分配的大小太小:没有空终止符的空间。您应该至少分配len + 1字节。
  • 你只复制不同于空格的字符(' '),而不初始化分配的字符串中的其他条目。分配块中的第六个字节可能是空字节,这可以解释为什么你得到HELLO作为输出,但其他任何事情都可能发生。
  • 还要注意,leni应该具有size_t类型,以与strlen的返回类型保持一致。

要实现此目标,您应该为源数组和目标数组使用不同的索引,并确保追加了空终止符。
以下是修改后的版本:

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

char *no_space(const char *str_in)
{
    size_t len = strlen(str_in);
    char *noSpace_str = malloc(len + 1);
  
    if (noSpace_str) {
        size_t j = 0;
        for (size_t i = 0; i < len; i++) {
            if (str_in[i] != ' ')
                noSpace_str[j++] = str_in[i];
        }
        noSpace_str[j] = '\0';
    }
    return noSpace_str;
}

字符串

tvokkenx

tvokkenx4#

其他(当前)答案指出,代码需要两个不同的索引计数器;一个用于源数组,一个用于目标数组。
其他的答案表明,分配的缓冲区需要容纳一个终止NUL(不按源字符串的strlen()计数)。
其他的答案已经证明了从malloc()返回的指针可以是NULL。不要使用NULL指针。总是测试来自系统函数的返回代码(如malloc()fopen()),如果需要的话减少处理。
这里有一个紧凑的替代方案。“* 更少的(工作!)代码是更好的代码 *”

char *no_space( const char *str_in ) {
    if( !str_in ) // can happen!
        return NULL;

    char *str_out = malloc( strlen( str_in ) + 1 ); // casting can lead to bugs

    if( str_out )
        for( char *p = str_out; ( *p = *str_in++ ) != '\0'; ) // copy everything
            p += *p != ' '; // retain non-SP characters

    return str_out;
}

字符串
更强大的版本将采用第二个char参数,允许此函数过滤掉调用者想要的任何字符(不仅仅是SP)的所有出现。
一个功能更强大的版本将采用第二个char*参数,允许此函数过滤掉调用者希望排除的任何字符(以char *excl作为参数传递给函数)。
简单的测试
cp += *cp != ch;
可能成为
cp += !strchr( excl, *cp );
最后一种可能性留给OP去探索和理解。

lpwwtiir

lpwwtiir5#

每个数组需要一个变量来遍历它们。只有当有字符副本时,目标数组的变量才必须递增。

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

char *no_space(const char *str_in)
{
    int len = strlen(str_in);
    char *noSpace_str = malloc(len+1);

    int j=0;
    for(int i = 0; i < len; i++)
    {
      if(str_in[i] != ' ')
      {
          noSpace_str[j] = str_in[i];
          j++;
      }
    }
    noSpace_str[j] = 0;

    return noSpace_str;
}

int main()
{
    char tab[] = "   123456    7890  -   12345  67890   ";
    puts(no_space(tab));
    return 0;
}

字符串

8qgya5xd

8qgya5xd6#

#include <stdlib.h> /* for malloc() */

char *no_space(const char *str_in)
{
    int len = strlen(str_in);

    /* don't cast the return value of malloc()  Only do it if you are
     * considering using the code in C++ programs, which is, by the
     * way, discouraged (use new/delete for this in C++).   
     * Doing that will make the compiler check that you have
     * actually #include <stdlib.h> which will pass unseen (putting the
     * cast informs the compiler you are doing the right thing and will
     * silent the lack of the proper prototype.  If you use
     * the cast, and the error that it produces is still provoking a cism
     * between C entusiasts. */
    char *noSpace_str = malloc(len + 1); /* sizeof (char) is always 1 */

字符串
strlen()返回字符串的长度,但是这个长度不包括最后一个'\0'的空间,strlen()需要正确地找到字符串的结尾并计算字符数,直到到。你需要在结尾处为null多分配一个字节,所以使用len = strlen(str_in) + 1;。这确实是一个错误,如果输入字符串根本没有空格,在这种情况下,它将溢出分配的缓冲区,使您将'\0'放置在分配的缓冲区末尾之外的一个空间,这是错误的。
如果你打算复制到一个新分配的字符串,那么你必须只复制字符,但跳过空格。这意味着你将不得不使用两个索引,而不是一个,因为它们之间的差异将说明跳过的字符数,所以你可以做得更好:

int src_index = 0, dst_index = 0;
    while (src_index < len) {
        if (str_in[src_index] != ' ')
            noSpace_str[dst_index++] = str_in[src_index];
        src_index++;
    }
    noSpace_str[dst_index] = '\0'; /* put a '\0' at the end to properly terminate the string */


(注意,如果源索引处的源字符串显示空格,则不会复制空格,并且dst_index也不会递增)
或者,如果你更喜欢使用指针:

char *str_out = malloc(strlen(str_in) + 1),
         *src, *dst;
    for (src = str_in, dst = str_out; *src; src++)
         if (*src != ' ')
             *dst++ = *src; /* copy the chars pointed by src and dst */
    *dst = '\0';  /* properly terminate the string */

    return str_out;

相关问题