【问题标题】:Separating a string that can have arbitrary whitespace in C在 C 中分隔可以具有任意空格的字符串
【发布时间】:2012-05-28 07:29:41
【问题描述】:

我有一个字符串数组,我需要将每个字符串分成几个不同的部分。字符串可能有也可能没有任意空格和制表符。

Example string:
str[0]: "   apple  e     3   a     a fruit    "
I need it to become:
word[0] = "apple"
row[0] = "e"
column[0] = "3"
direction[0] = "a"
clue[0] = "a fruit"

所以我需要删除任何前导/尾随空格,以及字段之间的任何空格(线索字段除外。线索内的空格需要保留)。我真的不知道该怎么做。我有一些基本的想法,但我不知道如何去实现它们,或者它们是否可行(编码新手并且非常无能)。到目前为止,我尝试过的所有东西要么无法编译,要么不起作用。

我最近尝试拉出第一个字段:

for (i=0; i<MAX_LENGTH; i++) {
   for (j=0; j<MAX_INPUT; j++) {
      if (isSpace(&input[i][j]) == FALSE) {
      //if whitespace is not present, find the location of the next
      //space and copy the string up til there
         static int n = j;

         for (n=0; n<MAX_INPUT; n++) {
            if (isSpace(&input[i][n]) == TRUE) {
               strncpy(word[i],&input[i][j],(n-j));
               //printf("Word[%d]: %s\n",i,word[i]);
               break;
            }
         }

      }
   }
}

对它不起作用并不感到惊讶。我还没有完全解决这个问题。请帮忙?

【问题讨论】:

  • 您首先将n 设置为j,然后在最内层循环中始终将n 重置为零。
  • 完全可以一次性解析,但是你可以在解析前修剪/规范化输入中的空格。

标签: c whitespace c-strings


【解决方案1】:

您当前的代码似乎太复杂了。这是一个三步算法,如果我编写它,我会实现它:

  1. 当当前字符为空格时:移动到下一个字符;
  2. 当当前字符不是空格时:将其附加到输出并移动到下一个字符;
  3. 重复步骤 1 和 2,直到到达输入字符串的末尾。

【讨论】:

  • 这不会对允许空格的最后一部分(“线索”)做正确的事情。
  • 或者,您可以递归地将每次出现的“”(两个空格)替换为“”(一个空格)。它不是很有效,但最终会将任何空间集群减少到一个空间,然后停在那里。 (如果您的输入混合了空格/制表符,它会变得更加复杂)。
【解决方案2】:

只需使用 sscanf 读取由空格分隔的“单词”:

char str[][100] = { "   apple  e     3   a     a fruit    ", ... }
char word[100][100],row[100][100],column[100][100],direction[100][100],clue[100][100];
int i;

for( i=0; i<...; ++i )
  sscanf(str[i],"%s%s%s%s%[^\n]",word[i],row[i],column[i],direction[i],clue[i]);

【讨论】:

    【解决方案3】:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <ctype.h>
    
    char *trimEnd(char *str){
        char *p;
        if(str == NULL || *str == '\0') return str;
        p=str+strlen(str);
        while(isspace(*--p) && p >= str){
            *p = '\0';
        }
        return str;
    }
    
    int main(){
        char str[100][100] = { "   apple  e     3   a     a fruit    " };
        char word[100][100], row[100][100], column[100][100], direction[100][100], clue[100][100];
        const char *delim= " \t";
        int i;
        const dataSize = 1;//for sample
    
        for(i=0;i<dataSize;++i){
            char *pwk, *p = strdup(str[i]);
            strcpy(word[i], strtok(p, delim));
            strcpy(row[i], strtok(NULL, delim));
            strcpy(column[i],strtok(NULL, delim));
            strcpy(direction[i], pwk=strtok(NULL, delim));
            pwk = pwk+strlen(pwk)+1;
            strcpy(clue[i], trimEnd(pwk+strspn(pwk, delim)));
    fprintf(stderr,"DEBUG:%s,%s,%s,%s,%s.",word[i],row[i],column[i],direction[i],clue[i]);
            free(p);
        }
    
        return 0;
    }
    

    【讨论】:

    • 如果 strlen() 恰好为零,trimEnd() 会做一些讨厌的事情。
    【解决方案4】:

    您可以使用strtok_r 函数。
    它将字符串分解为标记,由您选择的任何内容分隔。

    const char *delim= " \t";
    char *tok = NULL;
    
    word[0] = strtok_r(str[0],delim,&tok);
    row[0] = strtok_r(NULL,delim,&tok);
    column[0] = strtok_r(NULL,delim,&tok);
    direction[0] = strtok_r(NULL,delim,&tok);
    clue[0] = strtok_r(NULL,"",&tok);  // delim="" - won't break by whitespace
    

    【讨论】:

    • 什么是strtok_r?好像不是标准的。 strtok 已经足够了。
    • strtok_r 是 strtok 的可重入版本。后者使用静态变量,使其在多线程情况下无法使用。
    • @nhahtdh,我更喜欢避免使用strtok,不管多线程,因为它很脆弱(例如,如果在嵌套循环中使用会失败)。我认为只使用strtok_r 是一种好习惯。
    • @ugoren:我认为您应该将这些信息添加到您的答案中,以及 strtok_r 仅在 POSIX 平台上可用这一事实。
    猜你喜欢
    • 2010-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-02
    • 2021-10-26
    • 2015-01-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多