从C字符串中删除多余的空格?

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

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

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

卡特赖特温迪93 

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

卡特赖特#温迪#93

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

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

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

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

编辑 :我最初有一个malloced工作区,我可能会更清楚。 然而,没有额外的内存这样做几乎就是这么简单,我正在评论和个人即时通讯中推送,所以,这里来了…… 🙂

 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; } 

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

 #include  #include  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; } 
 char* trimwhitespace(char *str_base) { char* buffer = str_base; while((buffer = strchr(str_base, ' '))) { strcpy(buffer, buffer+1); } return str_base; } 

您可以读取一行,然后扫描它以查找每列的开头。 然后使用您想要的列数据。

 #include  #include  #include  #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); } 

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

这是一个替代函数,用于挤出重复的空格字符,由 isspace()定义。 它返回’squidged’字符串的长度。

 #include  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  #include  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); } 

我对约翰·博德做了一个小小的改进,以删除尾随的空格:

 #include  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  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。

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

  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 ); } } }