strtok()

11133 ワード

次の説明は、最新のLinuxカーネル2.6.29から抜粋し、strtok()という関数が使用されなくなり、より高速なstrsep()に取って代わったことを示しています.
/* * linux/lib/string.c * * Copyright (C) 1991, 1992 Linus Torvalds *//* * stupid library routines.. The optimized versions should generally be found * as inline code in * * These are buggy as well.. * * * Fri Jun 25 1999, Ingo Oeser * - Added strsep() which will replace strtok() soon (because strsep() is * reentrant and should be faster). Use only strsep() in new code, please. * * * Sat Feb 09 2002, Jason Thomas , * Matthew Hawkins * - Kissed strtok() goodbye */
strtok()という関数はみんな出会ったはずですが、いつも問題があるようです.ここでもう一度お話しします.
次に例を示します.
int main() {
char test1[] = "feng,ke,wei";
char *test2 = "feng,ke,wei";
char *p; p = strtok(test1, ",");
while(p)
{   
printf("%s", p);   
p = strtok(NULL, ",");   
}
return 0;
}
実行結果:
feng
ke
wei
ただしp= strtok(test 2,",")ではメモリエラーが発生するのはなぜですか?その中の静的変数と関係があるのではないでしょうか.元のコードを見てみましょう
/***
*strtok.c - tokenize a string with given delimiters
*
*         Copyright (c) Microsoft Corporation. All rights reserved.
*
*Purpose:
*         defines strtok() - breaks string into series of token
*         via repeated calls.
*
*******************************************************************************/
#include <cruntime.h>
#include <string.h>
#ifdef _MT
#include <mtdll.h>
#endif  /* _MT */
/***
*char *strtok(string, control) - tokenize string with delimiter in control
*
*Purpose:
*         strtok considers the string to consist of a sequence of zero or more
*         text tokens separated by spans of one or more control chars. the first
*         call, with string specified, returns a pointer to the first char of the
*         first token, and will write a null char into string immediately
*         following the returned token. subsequent calls with zero for the first
*         argument (string) will work thru the string until no tokens remain. the
*         control string may be different from call to call. when no tokens remain
*         in string a NULL pointer is returned. remember the control chars with a
*         bit map, one bit per ascii char. the null char is always a control char.
*       //          !! MSDN  ! 
*Entry:
*         char *string - string to tokenize, or NULL to get next token
*         char *control - string of characters to use as delimiters
*
*Exit:
*         returns pointer to first token in string, or if string
*         was NULL, to next token
*         returns NULL when no more tokens remain.
*
*Uses:
*
*Exceptions:
*
*******************************************************************************/
char * __cdecl strtok (
          char * string,
          const char * control
          )
{
          unsigned char *str;
          const unsigned char *ctrl = control;
          unsigned char map[32];
          int count;
#ifdef _MT
          _ptiddata ptd = _getptd();
#else  /* _MT */
          static char *nextoken;                       //                 
#endif  /* _MT */
          /* Clear control map */
          for (count = 0; count < 32; count++)
                  map[count] = 0;
          /* Set bits in delimiter table */
          do {
                  map[*ctrl >> 3] |= (1 << (*ctrl & 7));
          } while (*ctrl++);
          /* Initialize str. If string is NULL, set str to the saved
           * pointer (i.e., continue breaking tokens out of the string
           * from the last strtok call) */
          if (string)
                  str = string;                             //                       
else
#ifdef _MT
                  str = ptd->_token;
#else  /* _MT */
               str = nextoken;                      //          NULL      
#endif  /* _MT */
          /* Find beginning of token (skip over leading delimiters). Note that
           * there is no token iff this loop sets str to point to the terminal
           * null (*str == '\0') */
          while ( (map[*str >> 3] & (1 << (*str & 7))) && *str )
                  str++;
        string = str;                                  //   string          
          /* Find the end of the token. If it is not the end of the string,
           * put a null there. */
//          ,      ,      '\0',  '\0'          
          for ( ; *str ; str++ )
                  if ( map[*str >> 3] & (1 << (*str & 7)) ) {
                        *str++ = '\0';              //              
                          break;
                  }
          /* Update nextoken (or the corresponding field in the per-thread data
           * structure */
#ifdef _MT
          ptd->_token = str;
#else  /* _MT */
        nextoken = str;                 //           ,      
#endif  /* _MT */
          /* Determine if a token has been found. */
          if ( string == str )
                return NULL;
          else
                  return string;


1. strtok  

strtok             (             “,。”)

            "\0".



  ,   =“,”    =“Fred,John,Ann”

  strtok     3     “Fred”      “John”       “Ann”    。

   C   

QUOTE:
int in=0;
char buffer[]="Fred,John,Ann"
char *p[3];
char *buff = buffer;
while((p[in]=
strtok(buf,","))!=NULL) {
i++;
buf=NULL; }
上記のコードでは、strtokを最初に実行するには、ターゲット文字列のアドレスを第1パラメータ(buf=buffer)とし、その後strtokはNULLを第1パラメータ(buf=NULL)とする必要がある.ポインタ列p[]は、分割された結果が格納され、p[0]=「John」、p[1]=「John」、p[2]=「Ann」となり、bufは    Fred\0John\0Ann\0. 2. strtokの弱点は私たちの計画を変更しましょう.私たちは「Fred male 25,John male 62,Anna female 16」という文字列をstructに整理して入力したいと思っています.
QUOTE:
struct person { 
char [25] name ; 
char [6] sex;
char [4] age;
}
これを行うには、「,」で分割された文字列を抽出してから、「」(スペース)で分割する方法があります.例えば、「Fred male 25」を切り取って「Fred」「male」「25」に分割します.以下、この過程を表現するために小さなプログラムを書きました.
QUOTE:
#include
#include
#define INFO_MAX_SZ 255
int main()
{
int in=0;
char buffer[INFO_MAX_SZ]="Fred male 25,John male 62,Anna female 16";
char *p[20];
char *buf=buffer;
while((p[in]=
strtok(buf,","))!=NULL) {
buf=p[in];
while((p[in]=
strtok(buf,""))!=NULL) {
in++;
buf=NULL;
}
p[n+]=「**」;//表現分割
buf=NULL; }
printf("Here we have %d strings",i);
for (int j=0; jprintf(">%s<",p[j]);
return 0;
}
このプログラムの出力は、Here we have 4 strings>Fred<>male<>25<>**<これはわずかなデータであり、私たちが必要とするものではありません.しかし、これはなぜですか?これはstrtokがstatic(静的)ポインタを使用してデータを操作するため、上記のコードの実行過程を分析させてください.赤はstrtokの内蔵ポインタが指す位置で、青はstrtokの文字列に対する修正1.「Fred male 25,John male 62,Anna female 16」//外循環2.「Fred male 250 John male 62,Anna female 16」//内循環3に入ります.    "Fred\0male 25\0John male 62,Anna female 16" 4.    「Fred0 male0250 John male 62,Anna female 16」5「Fred0 male0250 John male 62,Anna female 16」//内ループが「0」に遭遇して外ループ6に戻る   「Fred0 male0250 John male 62,Anna female 16」//外部ループが「0」に遭遇して運転が終了しました.3.strtok_を使うrこの場合strtok_を使用するべきですr, strtok reentrant.  char *strtok_r(char *s, const char *delim, char **ptrptr); strtokに対して、strtokのようにセットのポインタを使用するのではなく、strtokにポインタを提供して操作する必要があります.コード:
QUOTE:
#include
#include
#define INFO_MAX_SZ 255
int main()
{
int in=0;
char buffer[INFO_MAX_SZ]="Fred male 25,John male 62,Anna female 16";
char *p[20];
char *buf=buffer;
char *outer_ptr=NULL;
char *inner_ptr=NULL;
while((p[in]=
strtok_r(buf,",",&outer_ptr))!=NULL) {
buf=p[in];
while((p[in]=
strtok_r(buf,"",&inner_ptr))!=NULL) {
in++;
buf=NULL;
}
p[in++]="***";
buf=NULL; }
printf("Here we have %d strings",i);
for (int j=0; jnprintf(">%s<",p[j]);
return 0;
}
今回の出力は、Here we have 12 strings>Fred<>male<>25<>***<>John<>male<>62<>**<>Anna<>female<>16<>***<以上のコードの実行手順を分析させていただきます:赤はstrtok_rのouter_ptrが指す位置、紫色はstrtok_rのinner_ptrが指す位置、青はstrtokによる文字列の修正1.「Fred male 25,John male 62,Anna female 16」//外循環2.「Fred male 250 John male 62,Anna female 16」//内循環3.   "Fred\0male 25\0John male 62,Anna female 16" 4   「Fred0 male0250 John male 62,Anna female 16」5「Fred0 male0250 John male 62,Anna female 16」//内ループが「0」に遭遇して外ループ6に戻る   「Fred0 male0250 John male 620 Anna female 16」//インループ
}

本来、この関数は元の列を修正する.
したがって、char*test 2="feng,ke,wei"を最初のパラメータとして用いることにより、位置1ではtest 2が指す内容が文字定数領域に保存されているため、その領域の内容は修正できないためメモリエラーが発生する.char test 1[]="feng,ke,wei"のtest 1が指す内容はスタック領域に保存されているため、修正可能である.
ここを見ると、文字定数区についてもっと理性的な認識があるのではないでしょうか.