2014-02-03 2 views
0

В следующей программе C strtok используется для разделения строки. Программа дает исключение, но я не могу понять, как это работает.Как и почему strtok запоминает строку?

Во-первых, мы прошли строка tokenize и разделитель. Но в последующих итерациях мы просто проходим NULL. Как и почему функция запоминает строку?

Что делать, если я хочу использовать tokenize для разных строк одновременно?

#include "stdafx.h" 
#include <cstdio> 
#include <cstring> 

int main(int argc, char* argv[]) 
{ 
    char arr[] = "This is string to split"; 

    char * subStr = new char[10]; 
    subStr = strtok(arr, " "); 

    while (subStr) 
    { 
     printf("%s\n", subStr); 
     subStr = strtok(NULL, " "); 
    } 

    return 0; 
} 

Выход:

This 
is 
string 
to 
split 
+0

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

+0

@MadHatter, Могут быть способы, мы можем снова и снова передавать новую строку. Вопрос в том, что текущий подход не позволяет ему работать только по одной строке. –

+0

Для «как он помнит» может быть «статический указатель». Я не думаю, что можно использовать динамическое распределение, так как когда он будет знать, что «освобождает» память. –

ответ

4

strtok функция имеет внутреннее состояние, которое запоминает последнюю позицию, которую она достигла. Поскольку он перезаписывает исходную строку, заменяя токен нулем, все, что нужно запомнить, это следующая позиция в строке. Если вы вызываете strtok с непустым строковым аргументом, внутреннее состояние сбрасывается в новую строку. Так что вы не можете использовать его сразу для нескольких строк, только один за другим. (Некоторые платформы обеспечивают вариант strtok_r возвратных, который позволяет передавать ваше собственное переменное состояние.)

Вот пример реализация:

char * my_strtok(char * in, char delim) // not quite the same signature 
{ 
    _Thread_local static char * pos = NULL; 

    if (in) { pos = in; } 

    char * p = find_next_delimiter(pos, delim); // NULL if not found 
    if (p) { *p = '\0'; ++p; pos = p; } 

    return p; 
} 

(Реальный strtok ищет любой ограничитель данного списка, а также пропускает пустые поля.) Повторный вариант этого заменит статическую переменную pos с параметром функции.

0

Функция strtok использует статическую переменную для отслеживания состояния из предыдущих вызовов. По этой причине он не является потокобезопасным (вместо этого выберете strtok_r), и вы не должны использовать его для одновременной маркировки различных строк в разных потоках.

Here это один из способов его реализации.

+0

Примечание: 'strtok_r' является нестандартным и недоступным на каждой платформе. – DevSolar

+0

Зачем это не быть потокобезопасным? Весьма правдоподобно, что внутреннее состояние является локальным потоком, как 'errno'. –

+0

@KerrekSB Хорошая точка. Это могло бы*. Но гарантировано ли это где-то? (Я не знаю - я честно спрашиваю.) Если нет, я хотел бы знать специфику версии, которую я использую. (И, например, эта ссылка, которую я включил, заставляет меня поверить, что нужно проявлять осторожность ...) В таких обстоятельствах, когда необходимо сделать предположение, я решил предположить, что это не так, как есть. – Turix

0

«How» = использование статической переменной.

«Почему» = для продолжать требовать следующего за ноль, если вы снова передать исходную строку - вам нужно будет снова пропустить первые жетоны, которые россыпью циклов процессора

0

Как и почему strtok помнит строку?

Функция strtok() использует статический буфер при разборе.

Что делать, если я хочу использовать tokenize для разных строк одновременно?

Вы можете создать свои собственные:

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

char *scan(char **pp, char c) 
{ 
    char *s = *pp, *p; 

    p = strchr(*pp, c); 
    if (p) *p++ = '\0'; 
    *pp = p; 
    return s; 
} 

int main(void) 
{ 
    char s[] = "This is string to split"; 
    char *p = s; 

    while (p) { 
     printf("%s\n", scan(&p, ' ')); 
    } 
    return 0; 
} 
  • Обратите внимание, что scan() заменяет все вхождения разделителя с \0 в исходной строке
Смежные вопросы