【问题标题】:Using strtok in c在 c 中使用 strtok
【发布时间】:2011-11-12 18:57:12
【问题描述】:

我需要使用 strtok 读取名字和姓氏并将其分开。如何将可以独立使用的名称存储在两个单独的 char 数组中?

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

int main ()
{
  char str[] ="test string.";
  char * test;
  test = strtok (str," ");
  while (test != NULL)
  {
    printf ("%s\n",test);
    test= strtok (NULL, " ");
  }
  return 0;
}

【问题讨论】:

  • 我可以用它们来读取一个字符数组直到一个空格吗?
  • 不,我的意思是“将它们与strtok 结合使用”。 IE。将令牌(test 指向)复制到目标字符串中。
  • @KerrekSB 虽然使用 strchrstrndup 会更快、更灵活(无需破坏输入)
  • @sehe:是的。给这只猫剥皮的方法很多。 OP 似乎已经决定使用strtok,所以我就随它去了……
  • @KerrekSB:我添加了一种无 strtok 的方法来给这只猫剥皮。删除所有与 strtok 相关的问题。

标签: c strtok


【解决方案1】:

这是我对一个相当简单的标记化助手的看法

  • 将结果存储在动态增长的数组中
  • 以空值结尾的数组
  • 保持输入字符串的安全(strtok 修改输入字符串,这是在文字 char[] 上的未定义行为,至少我认为在 C99 中)

要使代码可重入,请使用非标准的strtok_r

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

char** tokenize(const char* input)
{
    char* str = strdup(input);
    int count = 0;
    int capacity = 10;
    char** result = malloc(capacity*sizeof(*result));

    char* tok=strtok(str," "); 

    while(1)
    {
        if (count >= capacity)
            result = realloc(result, (capacity*=2)*sizeof(*result));

        result[count++] = tok? strdup(tok) : tok;

        if (!tok) break;

        tok=strtok(NULL," ");
    } 

    free(str);
    return result;
}

int main ()
{
    char** tokens = tokenize("test string.");

    char** it;
    for(it=tokens; it && *it; ++it)
    {
        printf("%s\n", *it);
        free(*it);
    }

    free(tokens);
    return 0;
}

这是 strtok-free 的重新实现(使用 strpbrk 代替):

char** tokenize(const char* str)
{
    int count = 0;
    int capacity = 10;
    char** result = malloc(capacity*sizeof(*result));

    const char* e=str;

    if (e) do 
    {
        const char* s=e;
        e=strpbrk(s," ");

        if (count >= capacity)
            result = realloc(result, (capacity*=2)*sizeof(*result));

        result[count++] = e? strndup(s, e-s) : strdup(s);
    } while (e && *(++e));

    if (count >= capacity)
        result = realloc(result, (capacity+=1)*sizeof(*result));
    result[count++] = 0;

    return result;
}

【讨论】:

  • 我认为realloc 行应该有sizeof(*result),而不是sizeof(result),第一个参数显然应该是result 而不是realloc
  • 添加了一个strtok-free 版本(不需要修改它的输入,使用strpbrk。这样会更有效率)。
  • strdup + strndup 不是 C 标准,strtok_r 也不是,它只是 POSIX。
  • @user411313 AFAICT strdupstrtok_r 都是 IEEE Std 1003.1,2004 Edition 的一部分。显然,strndup 的优点在于,它很容易包装(或者只使用strtok 版本)
  • char str[] ="test string." 这里 str 是一个 char[13] 并且完全可以安全地修改。 (我不清楚您所说的“char[] 文字”是什么意思)
【解决方案2】:

您需要单独存放它们吗?两个指向修改后的 char 数组的指针将产生两个独立的完全可用的字符串。

也就是我们改造这个:

char str[] ="test string.";

进入这个:

char str[] ="test\0string.";
             ^     ^
             |     |
char *s1 -----     |
char *s2 -----------

.

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

int main ()
{
  char str[] ="test string.";
  char *firstname = strtok(str, " ");
  char *lastname = strtok(NULL, " ");
  if (!lastname)
    lastname = "";
  printf("%s, %s\n", lastname, firstname);
  return 0;
}

【讨论】:

    【解决方案3】:

    strcpy怎么样:

    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    
    #define MAX_NAMES 2
    
    int main ()
    {
      char str[] ="test string.";
      char *names[MAX_NAMES] = { 0 };
      char *test;
      int i = 0;
    
      test = strtok (str," ");
      while (test != NULL && i < MAX_NAMES)
      {
        names[i] = malloc(strlen(test)+1);
        strcpy(names[i++], test);
        test = strtok (NULL, " ");
      }
    
      for(i=0; i<MAX_NAMES; ++i)
      {
        if(names[i])
        {
          puts(names[i]);
          free(names[i]);
          names[i] = 0;
        }
      }
      return 0;
    }
    

    维护一个完整的程序并清理其资源包含很多杂乱,但主要是使用strcpy将每个令牌复制到自己的字符串中。

    【讨论】:

      【解决方案4】:
      #include <stdio.h>
      #include <string.h>
      #include <stdlib.h>
      
      
      char** split(const char *str, const char *delimiter, size_t *len){
          char *text, *p, *first, **array;
          int c;
          char** ret;
      
          *len = 0;
          text=strdup(str);
          if(text==NULL) return NULL;
          for(c=0,p=text;NULL!=(p=strtok(p, delimiter));p=NULL, c++)//count item
              if(c==0) first=p; //first token top
      
          ret=(char**)malloc(sizeof(char*)*c+1);//+1 for NULL
          if(ret==NULL){
              free(text);
              return NULL;
          }
          strcpy(text, str+(first-text));//skip until top token
          array=ret;
      
          for(p=text;NULL!=(p=strtok(p, delimiter));p=NULL){
              *array++=p;
          }
          *array=NULL;
          *len=c;
          return ret;
      }
      
      void free4split(char** sa){
          char **array=sa;
      
          if(sa!=NULL){
              free(array[0]);//for text
              free(sa);      //for array
          }
      }
      
      int main(void){
          char str[] ="test string.";
          char **words;
          size_t len=0;
          int i;
      
          words = split(str, " \t\r\n,.", &len);
      
      /*
          for(char **wk = words; *wk ;wk++){
              printf("%s\n", *wk);
          }
      */
          for(i = 0;i<len;++i){
              printf("%s\n", words[i]);
          }
          free4split(words);
          return 0;
      }
      /* result:
      test
      string
      */
      

      【讨论】:

        【解决方案5】:

        使用诸如

        之类的函数将结果从strtok复制到一个新的缓冲区中
        /*
         * Returns a copy of s in freshly allocated memory.
         * Exits the process if memory allocation fails.
         */
        char *xstrdup(char const *s)
        {
            char *p = malloc(strlen(s) + 1);
            if (p == NULL) {
                perror("memory allocation failed");
                exit(1);
            }
            strcpy(p, s);
            return p;
        }
        

        完成后不要忘记free返回值。

        【讨论】:

          【解决方案6】:

          IMO,您根本不需要(也可能不想)使用strtok(例如,“为此,或其他任何事情”)。我想我会使用这样的代码:

          #include <string.h>
          #include <stdlib.h>
          
          static char *make_str(char const *begin, char const *end) { 
              size_t len = end-begin;
              char *ret = malloc(len+1);
              if (ret != NULL) {
                  memcpy(ret, begin, len);
                  ret[len]='\0';
              }
              return ret;
          }
          
          size_t tokenize(char *tokens[], size_t max, char const *input, char const *delims) { 
              int i;
              char const *start=input, *end=start;
          
              for (i=0; *start && i<max; i++) {
                  for ( ;NULL!=strchr(delims, *start); ++start)
                      ;
                  for (end=start; *end && NULL==strchr(delims, *end); ++end)
                      ;
                  tokens[i] = make_str(start, end);
                  start = end+1;
              }
              return i;
          }
          
          #ifdef TEST
          
          #define MAX_TOKENS 10
          
          int main() { 
              char *tokens[MAX_TOKENS];
              int i;
              size_t num = tokenize(tokens, MAX_TOKENS, "This is a longer input string ", " ");
              for (i=0; i<num; i++) {
                  printf("|%s|\n", tokens[i]);
                  free(tokens[i]);
              }
              return 0;
          }
          
          #endif
          

          【讨论】:

            【解决方案7】:

            你也可以这样做。

                int main ()
                {
                char str[] ="test string.";
            
                char * temp1;
                char * temp2; 
            
                temp1 = strtok (str," ");
            
                temp2 = strchr(str, ' '); 
                if (temp2 != NULL)
                    temp2++;
            
                printf ("Splitted string :%s, %s\n" , temp1 , temp2);
                return 
                }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2021-09-24
              • 2023-04-05
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多