Использование strtok в c

Мне нужно использовать strtok, чтобы прочитать имя и фамилию и разделить их. Как я могу хранить имена, где я могу использовать их независимо друг от друга в двух отдельных массивах символов?

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

person shinjuo    schedule 12.11.2011    source источник
comment
могу ли я использовать их для чтения массива символов до пробела?   -  person shinjuo    schedule 12.11.2011
comment
Нет, я имел в виду использовать их вместе с strtok. т.е. скопируйте токен (на который указывает test) в целевую строку.   -  person Kerrek SB    schedule 12.11.2011
comment
@KerrekSB, хотя использование strchr и strndup было бы быстрее и гибче (не нужно забивать ввод)   -  person sehe    schedule 12.11.2011
comment
@sehe: Верно. Много способов снять шкуру с этой кошки. ОП, кажется, уже решил strtok, поэтому я просто согласился с этим...   -  person Kerrek SB    schedule 12.11.2011
comment
@KerrekSB: тем не менее, я добавил бесплатный способ снять шкуру с этого кота. Удаляет все проблемы, связанные с strtok.   -  person sehe    schedule 12.11.2011


Ответы (6)


Вот мой взгляд на достаточно простой помощник по токенизации, который

  • сохраняет результаты в динамически растущем массиве
  • нулевое завершение массива
  • сохраняет входную строку в безопасности (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;
}
person sehe    schedule 12.11.2011
comment
Я думаю, что в строке realloc должно быть sizeof(*result), а не sizeof(result), и первый аргумент, очевидно, должен быть result, а не realloc. - person Arkku; 12.11.2011
comment
Добавлена ​​strtok-бесплатная версия (которой не нужно изменять входные данные, используя strpbrk. Это будет более эффективно). - person sehe; 12.11.2011
comment
strdup + strndup не являются стандартом C, также strtok_r не является стандартом, это только POSIX. - person user411313; 13.11.2011
comment
@user411313 AFAICT как strdup, так и strtok_r являются частью Стандарт IEEE 1003.1, издание 2004 г.. Очевидно, это хорошая точка зрения на strndup, но ее легко обернуть (или просто использовать версию strtok) - person sehe; 13.11.2011
comment
char str[] ="test string." здесь str — это char[13], и его совершенно безопасно изменять. (Мне было непонятно, что вы имели в виду под литералом char[]) - person u0b34a0f6ae; 13.11.2011
comment
@kaizer.se: возможно, я перепутал C99 и C++03 (в основном я специалист по C++) - person sehe; 13.11.2011

Нужно ли хранить их отдельно? Два указателя на модифицированный массив символов дадут две отдельные строки, которые можно использовать.

То есть мы преобразуем это:

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;
}
person u0b34a0f6ae    schedule 12.11.2011

Как насчет использования 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 для копирования каждого токена в свою собственную строку.

person Christian Rau    schedule 12.11.2011

Скопируйте результаты из 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 возвращаемые значения, когда закончите с ними.

person Fred Foo    schedule 12.11.2011

ИМО, вам не нужно (и, вероятно, не нужно) вообще использовать 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
person Jerry Coffin    schedule 12.11.2011

Вы тоже можете сделать что-то подобное.

    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 
    }
person Thulasi    schedule 16.01.2014