2008-11-05 16 views
23

Próbowałem tokenize ciąg znaków przy użyciu SPACE jako ogranicznik, ale to nie działa. Czy ktoś ma sugestie, dlaczego to nie działa?Tokenizing ciągi znaków w C

Edit: tokenizing używając:

strtok(string, " "); 

Kod jest jak na poniższym

pch = strtok (str," "); 
while (pch != NULL) 
{ 
    printf ("%s\n",pch); 
    pch = strtok (NULL, " "); 
} 
+0

Twój przykład dostanie pierwszy token, spójrz na gbjbaanb lub moje odpowiedzi na poprawne użycie. –

+0

OK. Teraz dokądś zmierzamy. Jakiego zachowania się nie spodziewasz? – dmckee

+0

Twój kod jest poprawny. Daj nam znać, jaki jest twój ciąg wejściowy i wynik. –

Odpowiedz

0

Czytając dokumentację strtok, widzę, że trzeba przejść na wskaźnik NULL po pierwszym „inicjowanie " połączenie. Może tego nie zrobiłeś. Oczywiście.

38

Czy to tak:

char s[256]; 
strcpy(s, "one two three"); 
char* token = strtok(s, " "); 
while (token) { 
    printf("token: %s\n", token); 
    token = strtok(NULL, " "); 
} 

Uwaga: strtok modyfikuje ciąg jego tokenising, więc to nie może być const char*.

+0

heh, wysłaliśmy prawie to samo w tym samym czasie :-P –

+0

Widzę, nienawidzę tego. Na szczęście trafiłem Post sekund przed tobą tak nayanayayayaya :-) Masz jednak głos. – gbjbaanb

+6

Zwrócę przysługę;) –

32

Oto przykład strtok użytkowania, należy pamiętać, że strtok jest destrukcyjne jej ciąg wejściowy (a zatem nie może kiedykolwiek być stosowany na sznurku stałej

char *p = strtok(str, " "); 
while(p != NULL) { 
    printf("%s\n", p); 
    p = strtok(NULL, " "); 
} 

Zasadniczo rzecz, którą należy że przechodząc NULL jako pierwszy parametr do strtok informuje go, aby uzyskać następny znak z łańcucha został uprzednio tokenizing.

+1

W jaki sposób 'strtok()' wie, kiedy zdobyć następny token z ciągu, który poprzednio tokenizował? Dla mnie wygląda na to, że 'strtok()' powinno tokenizować wartość 'NULL'. Spodziewałbym się błędu. – polemon

+3

'strtok' ma wewnętrzną zmienną stanu śledzącą łańcuch będący tokenem. Kiedy przekazujesz do niego 'NULL',' strtok' będzie nadal używać tej zmiennej stanu. Gdy przekażesz wartość inną niż null, zmienna stanu zostanie zresetowana. Innymi słowy: przekazanie 'NULL' oznacza" kontynuuj tokenizację tego samego ciągu znaków ". –

+2

Dzięki za wyjaśnienie. Ale i tak wygląda to na okropny projekt. – polemon

3

można uprościć kod wprowadzając dodatkową zmienną.

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

int main() 
{ 
    char str[100], *s = str, *t = NULL; 

    strcpy(str, "a space delimited string"); 
    while ((t = strtok(s, " ")) != NULL) { 
     s = NULL; 
     printf(":%s:\n", t); 
    } 
    return 0; 
} 
6

strtok może być bardzo niebezpieczny. To nie jest bezpieczne dla wątków. Jego zamierzonym zastosowaniem jest wywoływanie w pętli, przekazując dane wyjściowe z poprzedniego połączenia. Funkcja strtok ma wewnętrzną zmienną przechowującą stan wywołania strtok. Ten stan nie jest unikalny dla każdego wątku - jest globalny. Jeśli jakikolwiek inny kod używa strtok w innym wątku, masz problemy. Nie tego rodzaju problemów, które chcesz wyśledzić!

Polecam szukając implementacji regex lub używając sscanf do rozłączenia łańcucha.

Spróbuj:

char strprint[256]; 
char text[256]; 
strcpy(text, "My string to test"); 
while (sscanf(text, "%s %s", strprint, text) > 0) { 
    printf("token: %s\n", strprint); 
} 

Uwaga: 'tekst' string jest zniszczone jak to rozdzielić. To może nie być preferowane zachowanie =)

+3

W rzeczywistości, jeśli spojrzeć na nowoczesne implementacje strtok, mają tendencję do korzystania z lokalnej pamięci wątku (MSVC robi to z pewnością od lat i lat), więc są bezpieczne dla wątków. Jest to nadal archaiczna funkcja, której uniknęłbym, chociaż ... –

+3

strtok_r jest wersją bezpieczną strtok http://pubs.opengroup.org/onlinepubs/009695399/functions/strtok.html –

2

Zrobiłem kilka funkcji ciągu w celu podzielenia wartości, używając mniej wskaźników, jak mogłem, ponieważ ten kod jest przeznaczony do pracy na procesorach PIC18F.Procesory te nie obsługuje naprawdę dobre z wskazówki, gdy masz kilka wolnych RAM dostępne:

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

char POSTREQ[255] = "pwd=123456&apply=Apply&d1=88&d2=100&pwr=1&mpx=Internal&stmo=Stereo&proc=Processor&cmp=Compressor&ip1=192&ip2=168&ip3=10&ip4=131&gw1=192&gw2=168&gw3=10&gw4=192&pt=80&lic=&A=A"; 

int findchar(char *string, int Start, char C) { 
    while((string[Start] != 0)) { Start++; if(string[Start] == C) return Start; } 
    return -1; 
} 

int findcharn(char *string, int Times, char C) { 
    int i = 0, pos = 0, fnd = 0; 

    while(i < Times) { 
     fnd = findchar(string, pos, C); 
     if(fnd < 0) return -1; 
     if(fnd > 0) pos = fnd; 
     i++; 
    } 
    return fnd; 
} 

void mid(char *in, char *out, int start, int end) { 
    int i = 0; 
    int size = end - start; 

    for(i = 0; i < size; i++){ 
     out[i] = in[start + i + 1]; 
    } 
    out[size] = 0; 
} 

void getvalue(char *out, int index) { 
    mid(POSTREQ, out, findcharn(POSTREQ, index, '='), (findcharn(POSTREQ, index, '&') - 1)); 
} 

void main() { 
    char n_pwd[7]; 
    char n_d1[7]; 

    getvalue(n_d1, 1); 

    printf("Value: %s\n", n_d1); 
} 
-1
int not_in_delimiter(char c, char *delim){ 

    while(*delim != '\0'){ 
      if(c == *delim) return 0; 
      delim++; 
    } 
    return 1; 
} 

char *token_separater(char *source, char *delimiter, char **last){ 

char *begin, *next_token; 
char *sbegin; 

/*Get the start of the token */ 
if(source) 
    begin = source; 
else 
    begin = *last; 

sbegin = begin; 

/*Scan through the string till we find character in delimiter. */ 
while(*begin != '\0' && not_in_delimiter(*begin, delimiter)){ 
     begin++; 
} 

/* Check if we have reached at of the string */ 
if(*begin == '\0') { 
/* We dont need to come further, hence return NULL*/ 
    *last = NULL; 
    return sbegin; 
} 
/* Scan the string till we find a character which is not in delimiter */ 
next_token = begin; 
while(next_token != '\0' && !not_in_delimiter(*next_token, delimiter)) { 
    next_token++; 
} 
/* If we have not reached at the end of the string */ 
if(*next_token != '\0'){ 
    *last = next_token--; 
    *next_token = '\0'; 
    return sbegin; 
} 
} 

void main(){ 

    char string[10] = "abcb_dccc"; 
    char delim[10] = "_"; 
    char *token = NULL; 
    char *last = "" ; 
    token = token_separater(string, delim, &last); 
    printf("%s\n", token); 
    while(last){ 
      token = token_separater(NULL, delim, &last); 
      printf("%s\n", token); 
    } 

}

można przeczytać na blogu analizę szczegółowo wymienione w moim profilu :)

+0

Nice, @jitsceait, ale co się dzieje, gdy na wejściu są dwa separatory? Zmienię trochę twój kod. –

+0

Myślę, że dodałem test case dla kolejnych ograniczników i to działało. Czy mógłbyś podświetlić kod, który zmieniłeś? – jitsceait

0

Tutaj to kolejna implementacja strtok(), która ma zdolność rozpoznawania kolejnych ograniczników (nie ma tej biblioteki standardowej strtok())

Ta funkcja jest częścią biblioteki ciągów licencjonowanych BSD o nazwie zString. Jesteś więcej niż mile widziane, aby przyczynić się :)

https://github.com/fnoyanisi/zString

char *zstring_strtok(char *str, const char *delim) { 
    static char *static_str=0;  /* var to store last address */ 
    int index=0, strlength=0;  /* integers for indexes */ 
    int found = 0;     /* check if delim is found */ 

    /* delimiter cannot be NULL 
    * if no more char left, return NULL as well 
    */ 
    if (delim==0 || (str == 0 && static_str == 0)) 
     return 0; 

    if (str == 0) 
     str = static_str; 

    /* get length of string */ 
    while(str[strlength]) 
     strlength++; 

    /* find the first occurance of delim */ 
    for (index=0;index<strlength;index++) 
     if (str[index]==delim[0]) { 
      found=1; 
      break; 
     } 

    /* if delim is not contained in str, return str */ 
    if (!found) { 
     static_str = 0; 
     return str; 
    } 

    /* check for consecutive delimiters 
    *if first char is delim, return delim 
    */ 
    if (str[0]==delim[0]) { 
     static_str = (str + 1); 
     return (char *)delim; 
    } 

    /* terminate the string 
    * this assignmetn requires char[], so str has to 
    * be char[] rather than *char 
    */ 
    str[index] = '\0'; 

    /* save the rest of the string */ 
    if ((str + index + 1)!=0) 
     static_str = (str + index + 1); 
    else 
     static_str = 0; 

     return str; 
} 

Jak wspomniano w poprzednich postach, ponieważ strtok() lub jeden ja implmented powyżej, opiera się na zmiennej static *char zachować położenie ostatniego separatora pomiędzy kolejne połączenia, należy zachować szczególną ostrożność podczas pracy z wielowątkowymi aplikacjami.