関数strtokがピットを踏んだため、私は古いエンジニア(1)に容赦なくあざけられました

フォロー、スター付きパブリックアカウント、エキサイティングなコンテンツへの直接アクセス

ID:テクノロジーが夢を大きくする

著者:李暁八尾

C / C ++による文字列切り出しの実現では、strtok関数がよく使用され、その主な機能は、指定された文字セットに従って文字列を分離し、各部分文字列を返すことです。

しかし実際には、strtok()だけでなく、strtok、strtok_s、およびstrtok_r関数も存在しますこの記事は、簡単な紹介をするための基本的な記事です。私はこの機能を悪用したため、私は古いエンジニアに馬鹿にされました。

strtok()関数の詳細説明

解説

この関数は、文字列をセグメントに分割し、各部分文字列を返すために使用されます。

関数プロトタイプ

char *strtok(char *str, const char *delim)

パラメータ

  • str、分割する文字列

  • delim、区切り文字列

戻り値

この関数は、分解される最初の部分文字列、または取得可能な文字列がない場合はnullポインターを返します。

インスタンス

//https://tool.lu/coderunner/
//来源:技术让梦想更伟大
//作者:李肖遥
#include <string.h>
#include <stdio.h>
#define INFO_MAX_SZ 80

int main () {
   char str[INFO_MAX_SZ] = "dream - coder - lixiaoyao";
   const char delim[2] = "-";
   char *token;
   
   //获取第一个子字符串
   token = strtok(str,delim);
   
   //继续获取其他的子字符串
   while( token != NULL ) 
   {
      printf( "%s\n", token );
    
      token = strtok(NULL, delim);
   }
   
   return(0);
}

操作の結果は次のとおりです。

予防

この関数を使用して文字列を分割すると、分解された文字列の整合性が失われ、呼び出しの前後のsが異なります。最初の分割後、元の文字列strは分割が完了した後の最初の文字列であり、残りの文字列は静的変数に格納されます。

strtok関数は、文字列を抽出するときに静的バッファを使用するため、スレッドセーフではありません複数のスレッドが同時に静的変数にアクセスすると、エラーが発生します。この記事は基本的な記事であり、フォローアップでさらに分析されます

応用例を拡大

インターネットでの典型的な例は、文字列を構造に分割することですが、それを整理してコードを調べました。

//https://tool.lu/coderunner/
//来源:技术让梦想更伟大
//作者:李肖遥

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
#define INFO_MAX_SZ 80
 
typedef struct person{ 
 char name[25]; 
 char sex[10]; 
 char age[4]; 
}Person;

int main()
{
 int in=0;  
 int j;
 char buffer[INFO_MAX_SZ]="Aob male 18,Bob male 19,Cob female 20";      
 char *p[20];  
 char *buf = buffer;  
 while((p[in]=strtok(buf,","))!=NULL)//先以,为分界符,将三个人的信息分开
 {  
  buf=p[in];//调用strtok,先将子串先一一保存到字符串指针数组中,  
  while((p[in]=strtok(buf," "))!=NULL)//以空格为分界符
  {  
   in++;  
   buf=NULL;  
  }  
  buf=NULL;  
 }  
 printf("Here we have %d strings\n", in);  
 for (j=0; j<in; j++)  
 {  
    //打印指针数组中保存的所有子串
  printf(">%s<\n",p[j]);  
 }  
    return 0;
}

結果は以下の通りです

この結果によると、望んだ結果が得られず、一人称の情報しか抽出できませんでした。

だから問題は何ですか?

分析によると、最初のループでは、strtok関数は最初の個人情報の後のコンマを '\ 0に変更します。このとき、strtokのthisポインターはコンマの後の文字を指します。

最初のループの終了後、関数の最初のパラメーターがNULLに設定されます。strtokは、このポインターが指す位置を分解の開始位置として使用します。このとき、このポインターは '\ 0'を指し、strtokはnullです。文字列は分割できず、NULLが返されるため、上記の結果が得られます。

では、この問題をどのように解決すればよいでしょうか?

この望ましい結果を達成するためのコードを見てみましょう

//https://tool.lu/coderunner/
//来源:技术让梦想更伟大
//作者:李肖遥

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
#define INFO_MAX_SZ 80
 
typedef struct person{ 
 char name[25]; 
 char sex[10]; 
 char age[4]; 
}Person;

int main()
{
 int in=0;  
 int j;
 char buffer[INFO_MAX_SZ]="Aob male 18,Bob male 19,Cob female 20";      
 char *p[20];  
 char *buf = buffer;  
 while ((p[in] = strtok(buf, " ,")) != NULL)//同时以逗号和空格为分界符
 {  
  switch (in % 3)  
  {  
  case 0:  
   printf("第%d个人:Name!\n", in/3+1);  
   break;  
  case 1:  
   printf("第%d个人:Sex!\n", in/3+1);  
   break;  
  case 2:  
   printf("第%d个人:Age!\n", in/3+1);  
   break;  
  }  
  in++;  
  buf = NULL;  
 }  
 printf("Here we have %d strings\n", in);  
 for (j=0; j<in; j++)  
 {     
  printf(">%s<\n",p[j]);  
 } 
    return 0;
}

最終的な実行結果は次のとおりです

この種類のコードはもう見ることができません。実装するには、事前に構造体に含まれているデータメンバーの数を知っておく必要があるため、strtokを置き換える適切な関数はありますか?

はい、それはstrtok_rです。

Linuxでのstrtok_r関数

解説

strtok_rは、Linuxプラットフォームでのstrtok関数のスレッドセーフバージョンです。ウィンドウのstring.hには含まれていません。この関数を使用するには、Linuxで実装ソースコードを見つけてプログラムにコピーするか、GNU Cライブラリを使用します。

strtok_r関数は、strtok関数の再入可能バージョンです。char **saveptrこのパラメーターは、char *strtok_rにセグメンテーションのコンテキストを格納するために使用されるポインター変数であり、連続した呼び出しに応答して同じソース文字列を解決します。

初めてstrtok_rを呼び出すとき、strパラメータは抽出する文字列を指す必要があり、saveptrパラメータの値は無視できます。継続的に呼び出す場合、strにはNULLが割り当てられ、saveptrは最後の呼び出しの後に返される値です。変更しないでください。

一連の異なる文字列は、同時に抽出するために継続的にstrtok_rと呼ばれる場合があり、異なる呼び出しには異なるsaveptrパラメータを渡す必要があります。

strtok_rは、実際にはstrtok内に暗黙的に保存されたthisポインターであり、パラメーターの形で関数の外部と対話します。発信者が転送、保存、さらには変更します。呼び出し元が同じソース文字列を継続的に分割する場合、strパラメータをNULLに割り当てるだけでなく、最後の分割中に保存されたsaveptrも渡す必要があります。

関数プロトタイプは次のとおりです

char *strtok_r(char *str, const char *delim, char **saveptr);

ソースコード

/* Parse S into tokens separated by characters in DELIM.
   If S is NULL, the saved pointer in SAVE_PTR is used as
   the next starting point.  For example:
        char s[] = "-abc-=-def";
        char *sp;
        x = strtok_r(s, "-", &sp);      // x = "abc", sp = "=-def"
        x = strtok_r(NULL, "-=", &sp);  // x = "def", sp = NULL
        x = strtok_r(NULL, "=", &sp);   // x = NULL
                // s = "abc\0-def\0"
*/
char *strtok_r(char *s, const char *delim, char **save_ptr) {
    char *token;
   /*判断参数s是否为NULL,如果是NULL就以传递进来的save_ptr作为起始分解位置;若不是NULL,则以s开始切分*/
    if (s == NULL) s = *save_ptr;
 
    /* Scan leading delimiters.  */
    s += strspn(s, delim);
    /*判断当前待分解的位置是否为'\0',若是则返回NULL(联系到(一)中所说对返回值为NULL的解释);不是则继续。*/
    if (*s == '\0') 
  return NULL;
 
    /* Find the end of the token.  */
    token = s;
    s = strpbrk(token, delim);
    if (s == NULL)
        /* This token finishes the string.  */
        *save_ptr = strchr(token, '\0');
    else {
        /* Terminate the token and make *SAVE_PTR point past it.  */
        *s = '\0';
        *save_ptr = s + 1;
    }
 
    return token;
}

上記の例を実現する

strtok_rを呼び出すコードには、strtokを呼び出すコードよりも2つ多いポインタ、outer_ptrおよびinner_ptrがあります。outer_ptrは、各個人の抽出位置、つまり外部ループをマークするために使用され、inner_ptrは、各個人内の各情報アイテムの抽出位置、つまり内部ループをマークするために使用されます。

strtok_rは、元の内部ポインターを表示し、パラメーターsaveptrを提供します。関数の柔軟性とセキュリティが向上しました。

//https://tool.lu/coderunner/
//来源:技术让梦想更伟大
//作者:李肖遥

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
#define INFO_MAX_SZ 80
 
typedef struct person{ 
 char name[25]; 
 char sex[10]; 
 char age[4]; 
}Person;

int main()
{
 int in=0;  
 int j;
 char buffer[INFO_MAX_SZ]="Aob male 18,Bob male 19,Cob female 20";      
 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;  
  }  
  buf=NULL;  
 }  
 printf("Here we have %d strings\n",in);  
 for (j=0; j<in; j++)  
 {     
  printf(">%s<\n",p[j]);  
 } 
    return 0;
}

コンパイル結果は以下の通りです

予防

この関数は分解された文字列の整合性も破壊しますが、残りの文字列をsaveptr変数に保存して安全を確保します。

Windowsでのstrtok_s関数

解説

strtok_sは、Windowsでの分割文字列セキュリティ関数です。

プロトタイプ

char *strtok_s( char *strToken, const char *strDelimit, char **buf);
char * strtok_s(char * restrict str,rsize_t * restrict strmax,const char * restrict delim,char ** restrict ptr);

strが指すnullで終了するバイト文字列で次のトークンを検索します。区切り文字は、delimが指すヌル終了バイト文字列によって識別されます。

この関数は、同じ文字列から連続したトークンを取得するために複数回呼び出されるように設計されています。

ここで参照できますが、ここでは触れません。

https://cloud.tencent.com/developer/p/1009645

巨人の肩

https://blog.csdn.net/bobyangsmile/article/details/38420985

https://www.runoob.com/cprogramming/c-function-strtok.html

やっと

ここでは、これらの関数の基本的な使用法と、いくつかの長所と短所などについて簡単に紹介します。後で、私が踏んだピットに従ってstrtok()の隠された特性を解釈します。次の号でお会いしましょう!

推奨読書:

嵌入式编程专辑Linux 学习专辑C/C++编程专辑

关注微信公众号『技术让梦想更伟大』,后台回复“m”查看更多内容,回复“加群”加入技术交流群。
长按前往图中包含的公众号关注

おすすめ

転載: blog.csdn.net/u012846795/article/details/107903402