Разбираем пустые токены из строки с помощью strtok - PullRequest
5 голосов
/ 31 июля 2010

Мое приложение создает строки, подобные приведенной ниже. Мне нужно разобрать значения между разделителем на отдельные значения.

2342|2sd45|dswer|2342||5523|||3654|Pswt

Я использую strtok, чтобы сделать это в цикле. Для пятого токена я получаю 5523. Однако мне нужно учитывать пустое значение между двумя разделителями ||. 5523 должен быть шестым жетоном согласно моему требованию.

token = (char *)strtok(strAccInfo, "|");

for (iLoop=1;iLoop<=106;iLoop++) { 
            token = (char *)strtok(NULL, "|");
}

Есть предложения?

Ответы [ 8 ]

6 голосов
/ 31 июля 2010

В этом случае я часто предпочитаю петлю p2 = strchr(p1, '|') с memcpy(s, p1, p2-p1) внутри.Он быстрый, не разрушает входной буфер (поэтому его можно использовать с const char *) и действительно переносим (даже для встроенного).

Он также реентерабелен;strtok нет.(Кстати: reentrant не имеет ничего общего с многопоточностью. strtok разрывается уже с вложенными циклами. Можно использовать strtok_r, но это не так переносимо.)

2 голосов
/ 05 августа 2010
char *mystrtok(char **m,char *s,char c)
{
  char *p=s?s:*m;
  if( !*p )
    return 0;
  *m=strchr(p,c);
  if( *m )
    *(*m)++=0;
  else
    *m=p+strlen(p);
  return p;
}
  • возвратный
  • поточно
  • строго соответствует ANSI
  • нужен неиспользуемый указатель помощи от звонка Контекст

1012 *, например *

char *p,*t,s[]="2342|2sd45|dswer|2342||5523|||3654|Pswt";
for(t=mystrtok(&p,s,'|');t;t=mystrtok(&p,0,'|'))
  puts(t);

, например

char *p,*t,s[]="2,3,4,2|2s,d4,5|dswer|23,42||5523|||3654|Pswt";
for(t=mystrtok(&p,s,'|');t;t=mystrtok(&p,0,'|'))
{
  char *p1,*t1;
  for(t1=mystrtok(&p1,t,',');t1;t1=mystrtok(&p1,0,','))
    puts(t1);
}

твоя работа :) реализовать char * c в качестве параметра 3

2 голосов
/ 31 июля 2010

Это ограничение strtok. Дизайнеры имели в виду разделенные пробелами токены. strtok все равно мало что делает; просто сверните свой собственный парсер. В C FAQ есть пример .

2 голосов
/ 31 июля 2010

При первом вызове функция ожидает строка C в качестве аргумента для str, чья первый символ используется в качестве исходное местоположение для сканирования токенов. При последующих вызовах функция ожидает нулевой указатель и использует позиция сразу после конца последнего токен как новое начальное местоположение для сканирование.

Для определения начала и конца токена, функция сначала сканирует из начальной точки для первый символ не содержится в разделители (который становится начало токена). А потом сканы, начиная с этого начала токен для первого персонажа содержится в разделителях, который становится конец токена.

Что это говорит о том, что он пропустит любой '|' символы в начале токена. Создание 5523 5-го токена, который вы уже знали. Просто подумал, что объясню почему (пришлось самому искать). Это также говорит о том, что вы не получите пустых токенов.

Поскольку ваши данные настроены таким образом, у вас есть несколько возможных решений:
1) найти все вхождения || и заменить на | | (вставьте там пробел)
2) сделайте strstr 5 раз и найдите начало 5-го элемента.

1 голос
/ 06 апреля 2019

Вдохновленный Ответ Патрика Шлютера Я сделал эту функцию, она должна быть поточно-ориентированной и поддерживать пустые токены и не меняет исходную строку

char* strTok(char** newString, char* delimiter)
{
    char* string = *newString;
    char* delimiterFound = (char*) 0;
    int tokLenght = 0;
    char* tok = (char*) 0;

    if(!string) return (char*) 0;

    delimiterFound = strstr(string, delimiter);

    if(delimiterFound){
        tokLenght = delimiterFound-string;
    }else{
        tokLenght = strlen(string);
    }

    tok = malloc(tokLenght + 1);
    memcpy(tok, string, tokLenght);
    tok[tokLenght] = '\0';

    *newString = delimiterFound ? delimiterFound + strlen(delimiter) : (char*)0;

    return tok;
}

Вы можете использовать его как

char* input = "1,2,3,,5,";
char** inputP = &input;
char* tok;
while( (tok=strTok(inputP, ",")) ){
    printf("%s\n", tok);
}

Предположим, что для вывода

1
2
3

5

Я проверил его на простые строки, но еще не использовал его в производстве, и разместил его также в обзоре кода , чтобы вы могли видеть, что другие думают об этом

1 голос
/ 31 июля 2010

Используйте что-то отличное от strtok. Это просто не предназначено делать то, что вы просите. Когда я нуждался в этом, я обычно использовал strcspn или strpbrk и обрабатывал остальную часть токенинга самостоятельно. Если вы не возражаете против изменения входной строки, например strtok, это должно быть довольно просто. По крайней мере, сразу кажется, что это должно работать:

// Warning: untested code. Should really use something with a less-ugly interface.
char *tokenize(char *input, char const *delim) { 
    static char *current;    // just as ugly as strtok!
    char *pos, *ret;
    if (input != NULL)
        current = input;

    if (current == NULL)
        return current;

    ret = current;
    pos = strpbrk(current, delim);
    if (pos == NULL) 
        current = NULL;
    else {
        *pos = '\0';
        current = pos+1;
    }
    return ret;
}
1 голос
/ 31 июля 2010

Используйте вместо этого strsep: ссылка на strsep

0 голосов
/ 03 августа 2010

Ниже приведено решение, которое работает для меня сейчас.Спасибо всем, кто откликнулся.

Я использую LoadRunner.Отсюда и некоторые незнакомые команды, но я считаю, что поток можно понять достаточно легко.

char strAccInfo[1024], *p2;
int iLoop;

Action() {  //This value would come from the wrsp call in the actual script.
    lr_save_string("323|90||95|95|null|80|50|105|100|45","test_Param");

    //Store the parameter into a string - saves memory. 
    strcpy(strAccInfo,lr_eval_string("{test_Param}"));
    //Get the first instance of the separator "|" in the string
    p2 = (char *) strchr(strAccInfo,'|');

    //Start a loop - Set the max loop value to more than max expected.
    for (iLoop = 1;iLoop<200;iLoop++) { 

        //Save parameter names in sequence.
        lr_param_sprintf("Param_Name","Parameter_%d",iLoop);

        //Get the first instance of the separator "|" in the string (within the loop).
        p2 = (char *) strchr(strAccInfo,'|');           

        //Save the value for the parameters in sequence. 
        lr_save_var(strAccInfo,p2 - strAccInfo,0,lr_eval_string("{Param_Name}"));   

        //Save string after the first instance of p2, as strAccInfo - for looping.
        strcpy(strAccInfo,p2+1);

        //Start conditional loop for checking for last value in the string.
        if (strchr(strAccInfo,'|')==NULL) {
            lr_param_sprintf("Param_Name","Parameter_%d",iLoop+1);
            lr_save_string(strAccInfo,lr_eval_string("{Param_Name}"));
            iLoop = 200;    
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...