从 C 中的字符串中弹出多余的 space

Eject excess space from string in C

我需要编写一个函数来从 C 中的字符串中弹出多余的 space。

示例:

char s[]="  abcde    abcde    "; 

输出:

"abcde abcde"

代码:

#include <stdio.h>
#include <ctype.h>
char *eject(char *str) {
  int i, x;
  for (i = x = 0; str[i]; ++i)
    if (!isspace(str[i]) || (i > 0 && !isspace(str[i - 1])))
      str[x++] = str[i];
  if(x > 0 && str[x-1] == ' ') str[x-1] = '[=13=]';
  return str;
}

int main() {
  char s[] = "  abcde    abcde    ";
  printf("\"%s\"", eject(s));
  return 0;
}

此代码不适用于字符串 " " 如果找到这个字符串,程序应该打印:

""

如何解决这个问题?

您可以编写两个 trim 前导和尾随空白字符的函数。

void trim_front(char *src) {
    size_t i = 0, j = 0;
    while (isspace(src[i])) i++;
    while (i < strlen(src)) src[j++] = src[i++];
    src[j] = '[=10=]';
}

void trim_back(char *src) {
    char *ch = src + strlen(src) - 1;
    while (isspace(*ch)) *ch-- = '[=10=]';
}

如果您知道不必处理尾随空格或前导空格,您的任务就会变得简单得多。

void reduce_spaces(char *src) {
    size_t i = 0, j = 0;

    for (; i < strlen(src); ++i) {
        if (i == strlen(src) - 1 || 
            (isspace(src[i]) && !isspace(src[i + 1])) ||
            !isspace(src[i])) {
            src[j++] = src[i];
        }
    }

    src[j] = '[=11=]';
}

并测试这个:

int main(void) {
    char s[] = "     hello    world     ";

    trim_front(s);
    trim_back(s);
    reduce_spaces(s);

    printf(">%s<\n", s);

    return 0;
}
% gcc test.c
% ./a.out
>hello world<
%

当然,如果你真的想,你可以将这些函数的代码移植到reduce_spaces,但是将一个问题分解成多个更小的问题可以使事情变得容易得多。

基本上,您需要删除输入字符串中单词之间的连续 space 个字符以及输入字符串的所有前导和尾随 space 个字符。这意味着,编写代码以删除输入字符串中连续的 space 个字符,并在删除连续的 space 个字符的同时,完全删除前导和尾随 space 个字符。

您只需一次迭代即可完成。无需编写用于删除输入字符串前导和尾随 space 的不同函数,如其他 post.

所示

你可以这样做:

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

char * eject (char *str) {
    if (str == NULL) {
        printf ("Invalid input..\n");
        return NULL;
    }

    /* Pointer to keep track of position where next character to be write 
     */
    char * p = str;
    for (unsigned int i = 0; str[i] ; ++i) {
        if ((isspace (str[i])) && ((p == str) || (str[i + 1] == '[=10=]') || (str[i] == (str[i + 1])))) {
            continue;
        }
        *p++ = str[i];
    }

    /* Add the null terminating character. 
     */
    *p = '[=10=]';
    return str;
}

int main (void) {
  char s[] = "  abcde    abcde    ";
  printf("\"%s\"\n", eject(s));

  char s1[] = "      ";
  printf("\"%s\"\n", eject(s1));

  char s2[] = "ab  yz   ";
  printf("\"%s\"\n", eject(s2));

  char s3[] = "  ddd xx  jj m";
  printf("\"%s\"\n", eject(s3));

  char s4[] = "";
  printf("\"%s\"\n", eject(s4));

  return 0;
}

输出:

# ./a.out
"abcde abcde"
""
"ab yz"
"ddd xx jj m"
""

一个稍微高级的答案,仅供参考 - 假设您的任务是编写一个专业的库,供在现实世界的程序中使用。然后首先列出所有有意义的要求:

  • 将字符串视为“不可变”是一种很好的做法 - 也就是说,基于旧字符串而不是 in-place 替换来构建新字符串。
  • 将目标字符串作为参数,同时 return 指向它的指针(类似于 strcpy 等函数)。
  • 如果是空字符串,也将目标字符串设置为空。
  • 删除所有“白色 space”,而不仅仅是 ' ' 字符。
  • 与其总是在每个单词后插入一个 space 字符,不如插入一个可变定界符?也可能是 ,;.
  • 最后一个词后不应插入定界符。
  • 出于性能原因,该算法应该只遍历一次数据。也就是说,像 strlen 等内部调用是不可接受的。
  • 逐字节迭代很好 - 我们不需要关心对齐。

那么我们可能会想到这样的事情:

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

char* trim_delimit (char* restrict dst, const char* restrict src, char delim)
{
  char* start = dst;
  *dst = '[=10=]';
  
  bool remove_spaces = true;
  char* insert_delim_pos = NULL;
  
  for(; *src != '[=10=]'; src++)
  {
    if(remove_spaces)
    {
      if(!isspace(*src))
      {
        remove_spaces = false;
        if(insert_delim_pos != NULL)
        {
          // we only get here if more words were found, not yet at the end of the string
          *insert_delim_pos = delim; 
          insert_delim_pos = NULL;
        }
      }
    }

    if(!remove_spaces)
    {
      if(isspace(*src))
      {
        remove_spaces = true;
        insert_delim_pos = dst; // remember where to insert delimiter for later
      }
      else
      {
        *dst = *src;
      }
      dst++;
    }
  }

  return start;
}

测试用例:

int main (void)
{
  char s[]="  abcde    abcde    "; 
  char trimmed[100];

  puts(trim_delimit(trimmed, s, ' '));
  puts(trim_delimit(trimmed, "", ' '));
  puts(trim_delimit(trimmed, s, ';'));
}

输出:

abcde abcde

abcde;abcde