如何在C中的strtok令牌之间存储我的子字符串?

时间:2016-09-20 15:00:44

标签: c

基本上,我使用strtok()来删除额外的后续空格,然后使用strcat()将字符串连接回来,因此字符串中只有一个空格需要。 例如: "你好。" string有3个空格。我的程序将成功地strtok()字符串,然后将它重新组合在一起,可以说只有一个空格。但是,当字符串看起来像这样: "你好,你好吗?"我的程序将输出:"你好",丢弃令牌之间的所有内容。

这是我的代码片段:

void stringFunction(struct dh *header){
    int i;
    char *spaceTk, *spaceString, *holder;
    struct dh *temp;

    temp = header->next;
    while(temp != NULL){
        spaceString = malloc(strlen(temp->string) + 1);
        strcpy(spaceString, temp->string);

        for(i = 0; i < strlen(spaceString) + 1; i++){
            if(spaceString[i] == ' '){
                count++; //don't worry about this variable
                if(spaceString[i] == ' ' && spaceString[i + 1] == ' '){
                    spaceTk = strtok(spaceString, " ");
                    while(spaceTk != NULL){
                        holder = malloc(strlen(spaceTk) + 1);
                        strcpy(holder, spaceTk);
                        spaceTk = strtok(NULL, " ");
                    }
                    strcat(spaceString, " ");
                    strcat(spaceString, holder); 
                    strcpy(temp->string, spaceString);
                }
            }
        }
    }   
}

.......

我理解变量&#34; holder&#34;存储令牌,但被最后一个覆盖。我只是不确定如何在第一个和最后一个令牌之间保存单词。

谢谢。

3 个答案:

答案 0 :(得分:1)

您对strtok()的使用非常奇怪。通常会让 it 遍历源字符串以查找分隔符,但您似乎是手动执行此操作,然后才调用strtok()

此外,如果您有strdup(),那么它比strlen() + malloc() + strcpy()更方便,结果相同(包括释放已分配的相同义务)存储时你不再需要它)。如果您没有strdup(),并且需要动态分配字符串副本,那么您应该考虑编写它。

此外,使用strcat()在重叠的对象之间进行复制 - 正如您所做的那样 - 会产生未定义的行为。不惜一切代价避免这样做。由于您已经在创建原始字符串的工作副本,因此避免在重叠对象之间进行复制的一种方法是将这些片段连接到原始字符串中,而不是将它们连接到工作空间中,然后将其复制回原始字符串中。字符串。

但是,任何方式,你需要以收到它的方式处理每个令牌。您当前的代码忽略了第一个和最后一个之间的所有令牌(一直泄漏内存)。以下是您的代码的变体,可以更好地运行:

struct dh *temp;

temp = header->next;
while(temp != NULL){
    char *spaceString = strdup(temp->string);
    // ... need a NULL check on spaceString here, in case allocation failed
    char *first_token = strtok(spaceString, " ");
    char *next_token = strtok(NULL, " ");

    if (next_token) {  // else the original string is OK as-is
        strcpy(temp->string, first_token);
        do {
            strcat(temp->string, " ");
            strcat(temp->string, next_token);
            next_token = strtok(NULL, " ");
        } while (next_token);
    }

    // It is obligatory to free the working string now that we're done with it
    free(spaceString);
}

但这仍然效率很低(虽然比你的好),因为strcat()调用都必须通过从头开始扫描来找到字符串的结尾,更不用说因为动态内存分配(也是一个潜在的失败点)和函数调用开销。编写就地空白压缩的代码并不难,不会遇到任何这些问题。这可能看起来像这样:

char *start_at = strstr(header->next, "  ");

if (start_at) {  // else nothing to do
    char *lag = start_at + 1;  // after the first space
    char *lead = lag + 1;      // after the second space
    int space_count = 2;

    do {
        if (*lead != ' ') {
            // not part of a run of spaces
            space_count = 0;
        } else if (space_count++) {
            // the second or subsequent space in a run of spaces
            continue;
        }

        *lag++ = *lead;
    } while (*lead++);
}

答案 1 :(得分:0)

如果您想要摆脱多个空格(并且具有可变字符串),您可以就地执行此操作,因为输出始终小于或等于输入。例如:

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

char *strip_inter_space(char *s)
{
  int i, j;
  int len;

  len = (int) strlen(s);

  for (i = 0, j = 0; i < len; i++) {
    if (s[i] == ' ') {
      // keep one space
      s[j] = s[i];
      j++;
      i++;
      // skip all others
      while (s[i] == ' ') {
        i++;
      }
    }
    // copy the characters we want from beyond the space
    s[j] = s[i];
    j++;
  }
  // strip trailing space if any
  if (s[j - 2] == ' ') {
    s[j - 2] = '\0';
  }
  // terminate string to get rid of the rest
  else {
    s[j] = '\0';
  }
  return s;
}

int main(int argc, char **argv)
{
  char *cp;

  if (argc < 2) {
    fprintf(stderr, "Usage: %s teststring\n", argv[0]);
    exit(EXIT_FAILURE);
  }
  // work on copy here, just in case
  cp = malloc(strlen(argv[1]) + 1);
  if (cp == NULL) {
    fprintf(stderr, "Malloc failed to allocate a measly %zu bytes\n",
            strlen(argv[1]) + 1);
    exit(EXIT_FAILURE);
  }
  strcpy(cp, argv[1]);

  printf("BEFORE: \"%s\"\n", cp);
  cp = strip_inter_space(cp);
  printf("AFTER:  \"%s\"\n", cp);

  free(cp);

  exit(EXIT_SUCCESS);
}

你不需要使用两个整数,你可以使用一个第二个指针并推动它们(这里没有显示),但你不会节省太多。

答案 2 :(得分:-1)

尝试:

char *holders[MAX_STRINGS];
int k= 0;
while(spaceTk != NULL && k<MAX_STRINGS){
    holders[k] = malloc(strlen(spaceTk)+1);
    strcpy(holders[k], spaceTk);
    spaceTk = strtok(NULL, " ");
    k++;
}