【问题标题】:Remove extra white space from inside a C string?从 C 字符串中删除多余的空格?
【发布时间】:2009-09-22 04:40:21
【问题描述】:

我已将几行文本读入一个 C 字符串数组。这些行有任意数量的制表符或空格分隔的列,我试图弄清楚如何删除它们之间的所有额外空格。最终目标是使用 strtok 分解列。这是列的一个很好的例子:

卡特赖特温迪 93 威廉姆森马克 81 汤普森马克 100 安德森约翰 76 特纳丹尼斯 56

如何消除列之间的所有空格或制表符,使输出看起来像这样?

卡特赖特温迪 93

或者,我可以将列之间的所有空格替换为不同的字符以使用 strtok 吗?像这样?

卡特赖特#温迪#93

编辑:多个很好的答案,但必须选择一个。感谢大家的帮助。

【问题讨论】:

    标签: c string whitespace


    【解决方案1】:

    如果我可以表达“你做错了”的观点,为什么不在阅读时消除空格?使用fscanf("%s", string); 读取“单词”(非空白),然后读取空白。如果是空格或制表符,请继续读入一“行”数据。如果是换行符,则开始一个新条目。在 C 语言中,尽快将数据转换为您可以使用的格式可能是最简单的,而不是尝试进行繁重的文本操作。

    【讨论】:

    • 这是我没有想到的。嗯。
    • 如果您知道要读取固定数量的列,则可以创建一个链表结构,其中每个列都有命名条目。如果不这样做,您可能仍希望将数据封装在链表结构中以跟踪条目数。或者您可以使用动态数组,但请注意,无论走到哪里,您都必须传递数组的大小(如果您曾经阅读过有关 C 的任何内容,您可能已经被警告过大约一百万次) .
    • 脚本语言没有错。我的母语是 Perl,我目前正在扩展我的 Python,但我也喜欢 C。学习 C 语言很好,因为你学会了用新的方式做事。它几乎让我的 Perl 无法阅读,因为我想用 getc() 一次处理一个字符大约一个小时,直到我想起按照 Perl 想要的方式来处理它是多么容易。
    • 小心使用 fscanf 的缓冲区溢出。
    • 呼应@Robert,将fgetssscanf结合使用。
    【解决方案2】:

    为什么不直接使用strtok()?无需修改输入

    您需要做的就是重复strtok(),直到您获得 3 个非空格标记,然后您就完成了!

    【讨论】:

    • 发生在我们最好的伙伴身上 ;) 看到我对 Alex Martelli 的回答的评论,我说了同样的话,哈哈
    【解决方案3】:

    编辑:我最初有一个 malloced 工作区,但我可能会更清楚。但是,在没有额外内存的情况下执行此操作几乎很简单,而且我在 cmets 和个人 IM 中被推到了这种方式,所以,来了...:-)

    void squeezespaces(char* row, char separator) {
      char *current = row;
      int spacing = 0;
      int i;
    
      for(i=0; row[i]; ++i) {
        if(row[i]==' ') {
          if (!spacing) {
            /* start of a run of spaces -> separator */
            *current++ = separator
            spacing = 1;
          }
        } else {
          *current++ = row[i];
          spacing = 0;
      }
      *current = 0;    
    }
    

    【讨论】:

    • 做一个malloc似乎有点笨手笨脚!
    • 因为你会如何分配内存?
    • 确实 :) 没想到
    • 只覆盖现有行是可行的(只需确保附加结束 '\0'!-),我只是想我会让 OP 的事情变得更简单。如果 OP cmets 和/或编辑(最好:both;-) 要求“不分配辅助内存”,我将相应地编辑答案。
    • 好吧,我放弃了(而且,你知道你是谁,停止 IRC'ing 和 IM'ing 我),这是没有额外内存的版本——希望同样简单;- )。现在开心吗?-)
    【解决方案4】:

    以下代码在原地修改字符串;如果您不想破坏原始输入,可以传递第二个缓冲区来接收修改后的字符串。应该是不言自明的:

    #include <stdio.h>
    #include <string.h>
    
    char *squeeze(char *str)
    {
      int r; /* next character to be read */
      int w; /* next character to be written */
    
      r=w=0;
      while (str[r])
      {
        if (isspace(str[r]) || iscntrl(str[r]))
        {
          if (w > 0 && !isspace(str[w-1]))
            str[w++] = ' ';
        }
        else
          str[w++] = str[r];
        r++;
      }
      str[w] = 0;
      return str;
    }
    
    int main(void)
    {
      char test[] = "\t\nThis\nis\ta\b     test.";
      printf("test = %s\n", test);
      printf("squeeze(test) = %s\n", squeeze(test));
      return 0;
    }
    

    【讨论】:

      【解决方案5】:
      char* trimwhitespace(char *str_base) {
          char* buffer = str_base;
          while((buffer = strchr(str_base, ' '))) {
              strcpy(buffer, buffer+1);
          }
      
          return str_base;
      }
      

      【讨论】:

        【解决方案6】:

        您可以读取一行然后扫描它以找到每一列的开头。然后随意使用列数据。

        #include <stdio.h>
        #include <string.h>
        #include <ctype.h>
        
        #define MAX_COL 3
        #define MAX_REC 512
        
        int main (void)
        {
            FILE *input;
            char record[MAX_REC + 1];
            char *scan;
            const char *recEnd;
            char *columns[MAX_COL] = { 0 };
            int colCnt;
        
            input = fopen("input.txt", "r");
        
            while (fgets(record, sizeof(record), input) != NULL)
            {
                memset(columns, 0, sizeof(columns));  // reset column start pointers
        
                scan = record;
                recEnd = record + strlen(record);
        
                for (colCnt = 0; colCnt < MAX_COL; colCnt++ )
                {
                  while (scan < recEnd && isspace(*scan)) { scan++; }  // bypass whitespace
                  if (scan == recEnd) { break; }
                  columns[colCnt] = scan;  // save column start
                  while (scan < recEnd && !isspace(*scan)) { scan++; }  // bypass column word
                  *scan++ = '\0';
                }
        
                if (colCnt > 0)
                {
                    printf("%s", columns[0]);
                    for (int i = 1; i < colCnt; i++)
                    {
                     printf("#%s", columns[i]);
                    }
                    printf("\n");
                }
            }
        
            fclose(input);
        }
        

        注意,代码仍然可以使用一些鲁棒性:检查文件错误 w/ferror;确保 eof 被击中 w/feof;确保处理了整个记录(所有列数据)。它还可以通过使用链表而不是固定数组来变得更加灵活,并且可以修改为不假设每列仅包含一个单词(只要列由特定字符分隔)。

        【讨论】:

          【解决方案7】:

          这是一个替代函数,可以挤出重复的空格字符,如&lt;ctype.h&gt; 中的isspace() 所定义。它返回“squidged”字符串的长度。

          #include <ctype.h>
          
          size_t squidge(char *str)
          {
              char *dst = str;
              char *src = str;
              char  c;
              while ((c = *src++) != '\0')
              {
                  if (isspace(c))
                  {
                      *dst++ = ' ';
                      while ((c = *src++) != '\0' && isspace(c))
                          ;
                      if (c == '\0')
                          break;
                  }
                  *dst++ = c;
              }
              *dst = '\0';
              return(dst - str);
          }
          
          #include <stdio.h>
          #include <string.h>
          
          int main(void)
          {
              char buffer[256];
              while (fgets(buffer, sizeof(buffer), stdin) != 0)
              {
                  size_t len = strlen(buffer);
                  if (len > 0)
                      buffer[--len] = '\0';
                  printf("Before: %zd <<%s>>\n", len, buffer);
                  len = squidge(buffer);
                  printf("After:  %zd <<%s>>\n", len, buffer);
              }
              return(0);
          }
          

          【讨论】:

            【解决方案8】:

            我在 John Bode 的基础上做了一个小改进,也删除了尾随空格:

            #include <ctype.h>
            
            char *squeeze(char *str)
            {
              char* r; /* next character to be read */
              char* w; /* next character to be written */
              char c;
              int sp, sp_old = 0;
            
              r=w=str;
            
              do {
                c=*r;
                sp = isspace(c);
                if (!sp) {
                  if (sp_old && c) {
                    // don't add a space at end of string
                    *w++ = ' ';
                  }
                  *w++ = c;
                }
                if (str < w) {
                  // don't add space at start of line
                  sp_old = sp;
                }
                r++;
              }
              while (c);
            
              return str;
            }
            
            #include <stdio.h>
            
            int main(void)
            {
              char test[] = "\t\nThis\nis\ta\f     test.\n\t\n";
              //printf("test = %s\n", test);
              printf("squeeze(test) = '%s'\n", squeeze(test));
              return 0;
            }
            

            br.

            【讨论】:

              【解决方案9】:

              下面的代码只是简单地接受输入字符,然后检查每个字符是否有空格多次跳过它,否则打​​印字符。 您也可以将相同的逻辑用于选项卡。 希望它有助于解决您的问题。如果此代码有任何问题,请告诉我。

                  int c, count = 0;
                  printf ("Please enter your sentence\n");
                  while ( ( c = getchar() ) != EOF )  {
                      if ( c != ' ' )  {
                          putchar ( c );
                          count = 0;
                      }
                      else  {
                          count ++;
                          if ( count > 1 )
                              ;    /* Empty if body */
                          else
                              putchar ( c );
                       }
                   }
              }
              

              【讨论】:

              • 请在你的答案中给出一点解释,而不是仅仅丢掉一段代码
              猜你喜欢
              • 2013-07-20
              • 2011-08-05
              • 1970-01-01
              • 2013-06-03
              • 2018-11-10
              • 1970-01-01
              相关资源
              最近更新 更多