2015-09-04 2 views
0

strtok_r ставит нулевой символ в разные места входной строки при разборе. Исходная строка восстанавливается только после того, как strtok_r возвращает NULL.strtok/strtok_r quit разбор в середине

Что делать, если мне нужно извлечь токен где-то рядом с началом длинной строки? Если я покину цикл, входная строка останется сломанной. Я мог бы попытаться восстановить разделитель вручную, но я не знаю, является ли это последним токеном. Проблема в том, что значение saveptr не задокументировано.

void extract_nth_token(char *res, size_t reslen, char *str, const char *delim, int n) { 
    int i; 
    char *token; 
    char *save_ptr; 

    token = strtok_r(str, delim, &save_ptr); 
    for(i = 0; token != NULL; i++) { 
    token = strtok_r(NULL, delim, &save_ptr); 
    if (i == n) { 
     snprintf(res, reslen, "%s", token); 
     /* token[strlen(token)] = delim[0]; */ 
     /* break; */ 
    } 
    } 
} 
+1

'Проблема заключается в том, что saveptr значение не задокументировано. 'какое' значение' вы ищете? –

+2

Сделайте копию строки и используйте ее. – Barmar

+0

@SouravGhosh значение, чтобы сказать мне, является ли это последним токеном – basin

ответ

2

strtok() и strtok_r() жуткие функции:

  • они изменяют строку ввода
  • Они относятся последовательные разделители как один, который может быть запланированными при пропуске пропуски, но это не предназначен для разбора .CSV (или разделенного вкладками) ввода.

Лучше всего избегать strtok() и strtok_r() и использовать strspn() и strcspn(). Эта функция ниже. Возвращаемое значение аналогично snprintf(): число символов в найденной знак (не считая завершающий NUL байт)

  • , если нет маркера # п: «\ 0» записывается в буфера и 0
  • Если буфер слишком мал для найденного токена плюс завершающий байт NUL, в буфер записывается '\ 0', а длина маркера возвращается
  • - это буфер достаточно большой, token + '\ 0', и возвращается strlen (токен).

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

size_t extract_nth_token_ohne_strtok_r(char *res, size_t maxlen, const char *str, const char *delim, int n) 
{ 
size_t pos, len; 
int itok; 

for (itok=0,pos=0; str[pos];) { 
     len = strcspn(str+pos, delim); 
     if (itok++ == n) { 
       if (len < maxlen) memcpy(res, str+pos, len), res[len] = 0; 
       else res[0] = 0; 
       return len; 
       } 
     pos += len; 
     if (str[pos]) pos++; 
     } 
res[0] = 0; 
return 0; 
} 

int main(void) 
{ 
char * omg = "zero one\ttwo \tfour\nfive" ; 
char token[80]; 
size_t toklen; 
int ii; 

printf("\n## With a large enough buffer:\n"); 
for (ii=0; ii < 7; ii++) { 
     toklen = extract_nth_token_ohne_strtok_r(token, sizeof token 
       , omg, " \t\n", ii); 
     printf("%d: res=%zu \"%s\"\n" , ii, toklen, token); 
     } 

printf("\n## With 4-character buffer:\n"); 
for (ii=0; ii < 7; ii++) { 
     toklen = extract_nth_token_ohne_strtok_r(token, 4 
       , omg, " \t\n", ii); 
     printf("%d: res=%zu \"%s\"\n" , ii, toklen, token); 
     } 

return 0; 
} 

Примечание: если вы сделать хотите, чтобы лечить подряд whitspace как один, вы могли бы заменить if (str[pos]) pos++; по:

pos += strspn(str+pos, delim);