strtok()
との間にどのような違いがあるのか誰かに説明してもらえますstrsep()
か?それらの長所と短所は何ですか?そして、なぜ私は他のものの上に1つを選ぶのでしょうか。
3 に答える
strtok()
との大きな違いの1つstrsep()
は、strtok()
標準化されている(C標準によって、したがってPOSIXによっても)が、strsep()
標準化されていない(CまたはPOSIXによって、GNU Cライブラリで利用可能であり、BSDで作成されている)ことです。したがって、ポータブルコードは。strtok()
よりも使用する可能性が高くなりますstrsep()
。
もう1つの違いは、strsep()
異なる文字列での関数の呼び出しはインターリーブできるのに対し、では実行できないことですstrtok()
(ただし、では実行できますstrtok_r()
)。したがって、ライブラリで使用strsep()
しても他のコードが誤って破損することはありませんが、ライブラリ関数で使用する場合は、同時にstrtok()
使用する他のコードではライブラリ関数を呼び出せないため、文書化する必要があります。strtok()
kernel.orgのマニュアルページにstrsep()
は次のように書かれています。
strtok(3)は空のフィールドを処理できないため、strsep()関数はstrtok(3)の代わりとして導入されました。
したがって、他の大きな違いは、GeorgeGaálが彼の答えで強調したものです。strtok()
単一のトークン間に複数の区切り文字を許可しますが、トークン間にstrsep()
単一の区切り文字を想定し、隣接する区切り文字を空のトークンとして解釈します。
両方とも入力文字列strsep()
をstrtok()
変更しますが、どちらもトークンの終わりをマークした区切り文字を識別できません(どちらもトークン'\0'
の終わりの後にセパレーターの上にNULを書き込むため)。
いつ使用しますか?
strsep()
トークン間に複数の区切り文字を許可するのではなく、空のトークンが必要な場合、および移植性を気にしない場合に使用します。strtok_r()
トークン間に複数の区切り文字を許可し、空のトークンが必要ない場合に使用します(POSIXは十分に移植可能です)。strtok()
あなたがそうしなければ、誰かがあなたの人生を脅かすときだけあなたは使うでしょう。そして、あなたはそれを生命を脅かす状況から抜け出すのに十分な時間だけ使用するでしょう。その後、もう一度それのすべての使用を放棄します。それは有毒です。使用しないでください。を使用するよりもstrtok_r()
、独自に作成する方がよいでしょう。strsep()
strtok()
なぜstrtok()
有毒なのですか?
ライブラリ関数で使用すると、このstrtok()
関数は有毒です。ライブラリ関数がを使用する場合はstrtok()
、明確に文書化する必要があります。
それは理由です:
- 呼び出し元の関数が使用し
strtok()
ていて、も使用している関数を呼び出す場合はstrtok()
、呼び出し元の関数を中断します。 - 関数がを呼び出す関数を呼び出すと
strtok()
、関数での。の使用が中断されますstrtok()
。 strtok()
プログラムがマルチスレッドの場合、一連のstrtok()
呼び出し全体で、常に最大1つのスレッドを使用できます。
この問題の根本は、呼び出しの間に保存された状態でstrtok()
あり、中断したところから続行できます。「使用しない」以外に問題を解決する賢明な方法はありませんstrtok()
。
strsep()
利用可能な場合は使用できます。- POSIX
strtok_r()
が利用可能な場合は、それを使用できます。 strtok_s()
利用可能な場合は、Microsoftを使用できます。- 名目上、ISO / IEC 9899:2011 Annex K.3.7.3.1関数を使用できますが、そのインターフェイスはMicrosoftの
strtok_s()
インターフェイスとは異なります。strtok_r()
strtok_s()
BSD strsep()
:
char *strsep(char **stringp, const char *delim);
POSIX strtok_r()
:
char *strtok_r(char *restrict s, const char *restrict sep, char **restrict state);
マイクロソフトstrtok_s()
:
char *strtok_s(char *strToken, const char *strDelimit, char **context);
附属書K strtok_s()
:
char *strtok_s(char * restrict s1, rsize_t * restrict s1max,
const char * restrict s2, char ** restrict ptr);
これには4つの引数があり、。の他の2つのバリアントのように3つではないことに注意してくださいstrtok()
。
GNU Cライブラリのマニュアルから-文字列内のトークンの検索:
strsep
との違いの1つstrtok_r
は、入力文字列に区切り文字からの文字が1行に複数含まれている場合、区切り文字strsep
からの文字のペアごとに空の文字列が返されることです。これは、プログラムが通常、strsep
処理する前に空の文字列を返すかどうかをテストする必要があることを意味します。
との最初の違いはstrtok()
、strsep()
入力文字列内の連続する区切り文字を処理する方法です。
strtok()
:による連続した区切り文字の処理
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void) {
const char* teststr = "aaa-bbb --ccc-ddd"; //Contiguous delimiters between bbb and ccc sub-string
const char* delims = " -"; // delimiters - space and hyphen character
char* token;
char* ptr = strdup(teststr);
if (ptr == NULL) {
fprintf(stderr, "strdup failed");
exit(EXIT_FAILURE);
}
printf ("Original String: %s\n", ptr);
token = strtok (ptr, delims);
while (token != NULL) {
printf("%s\n", token);
token = strtok (NULL, delims);
}
printf ("Original String: %s\n", ptr);
free (ptr);
return 0;
}
出力:
# ./example1_strtok
Original String: aaa-bbb --ccc-ddd
aaa
bbb
ccc
ddd
Original String: aaa
出力では、トークンが次々"bbb"
と表示されます。連続する区切り文字の出現を示すものではありません。また、入力文字列を変更します。"ccc"
strtok()
strtok()
strsep()
:による連続した区切り文字の処理
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void) {
const char* teststr = "aaa-bbb --ccc-ddd"; //Contiguous delimiters between bbb and ccc sub-string
const char* delims = " -"; // delimiters - space and hyphen character
char* token;
char* ptr1;
char* ptr = strdup(teststr);
if (ptr == NULL) {
fprintf(stderr, "strdup failed");
exit(EXIT_FAILURE);
}
ptr1 = ptr;
printf ("Original String: %s\n", ptr);
while ((token = strsep(&ptr1, delims)) != NULL) {
if (*token == '\0') {
token = "<empty>";
}
printf("%s\n", token);
}
if (ptr1 == NULL) // This is just to show that the strsep() modifies the pointer passed to it
printf ("ptr1 is NULL\n");
printf ("Original String: %s\n", ptr);
free (ptr);
return 0;
}
出力:
# ./example1_strsep
Original String: aaa-bbb --ccc-ddd
aaa
bbb
<empty> <==============
<empty> <==============
ccc
ddd
ptr1 is NULL
Original String: aaa
出力では、との間の2つの空の文字列(を介して示されます<empty>
)を確認できます。これらの2つの空の文字列は、との間用です。の後に区切り文字が見つかると、区切り文字を文字に置き換えて、を返しました。この後、別の区切り文字を見つけました。次に、区切り文字を文字に置き換えて、空の文字列を返しました。次の区切り文字についても同じです。bbb
ccc
"--"
"bbb"
"ccc"
strsep()
' '
"bbb"
'\0'
"bbb"
strsep()
'-'
'\0'
連続する区切り文字はstrsep()
、ヌル文字(つまり、値を持つ文字)へのポインターを返す'\0'
ときに示されます。
入力文字列と、アドレスが最初の引数としてに渡されstrsep()
たポインタを変更しますstrsep()
。
2番目の違いは、strtok()
文字列内の現在の解析位置を追跡するために静的変数に依存していることです。この実装では、2番目の文字列を開始する前に1つの文字列を完全に解析する必要があります。しかし、これは。の場合には当てはまりませんstrsep()
。
strtok()
別のが終了していないときに呼び出すstrtok()
:
#include <stdio.h>
#include <string.h>
void another_function_callng_strtok(void)
{
char str[] ="ttt -vvvv";
char* delims = " -";
char* token;
printf ("Original String: %s\n", str);
token = strtok (str, delims);
while (token != NULL) {
printf ("%s\n", token);
token = strtok (NULL, delims);
}
printf ("another_function_callng_strtok: I am done.\n");
}
void function_callng_strtok ()
{
char str[] ="aaa --bbb-ccc";
char* delims = " -";
char* token;
printf ("Original String: %s\n", str);
token = strtok (str, delims);
while (token != NULL)
{
printf ("%s\n",token);
another_function_callng_strtok();
token = strtok (NULL, delims);
}
}
int main(void) {
function_callng_strtok();
return 0;
}
出力:
# ./example2_strtok
Original String: aaa --bbb-ccc
aaa
Original String: ttt -vvvv
ttt
vvvv
another_function_callng_strtok: I am done.
この関数function_callng_strtok()
はトークンのみ"aaa"
を出力し、入力文字列の残りのトークンは出力しません。これは、この関数が呼び出しanother_function_callng_strtok()
て、すべてのトークンの抽出が終了したときにstrtok()
の静的ポインターをに設定するためstrtok()
です。NULL
コントロールはfunction_callng_strtok()
while
ループにstrtok()
戻りNULL
、静的ポインターNULL
がループ条件を作成してループをfalse
終了するために戻ります。
strsep()
別のが終了していないときに呼び出すstrsep()
:
#include <stdio.h>
#include <string.h>
void another_function_callng_strsep(void)
{
char str[] ="ttt -vvvv";
const char* delims = " -";
char* token;
char* ptr = str;
printf ("Original String: %s\n", str);
while ((token = strsep(&ptr, delims)) != NULL) {
if (*token == '\0') {
token = "<empty>";
}
printf("%s\n", token);
}
printf ("another_function_callng_strsep: I am done.\n");
}
void function_callng_strsep ()
{
char str[] ="aaa --bbb-ccc";
const char* delims = " -";
char* token;
char* ptr = str;
printf ("Original String: %s\n", str);
while ((token = strsep(&ptr, delims)) != NULL) {
if (*token == '\0') {
token = "<empty>";
}
printf("%s\n", token);
another_function_callng_strsep();
}
}
int main(void) {
function_callng_strsep();
return 0;
}
出力:
# ./example2_strsep
Original String: aaa --bbb-ccc
aaa
Original String: ttt -vvvv
ttt
<empty>
vvvv
another_function_callng_strsep: I am done.
<empty>
Original String: ttt -vvvv
ttt
<empty>
vvvv
another_function_callng_strsep: I am done.
<empty>
Original String: ttt -vvvv
ttt
<empty>
vvvv
another_function_callng_strsep: I am done.
bbb
Original String: ttt -vvvv
ttt
<empty>
vvvv
another_function_callng_strsep: I am done.
ccc
Original String: ttt -vvvv
ttt
<empty>
vvvv
another_function_callng_strsep: I am done.
ここでは、strsep()
1つの文字列を完全に解析する前に呼び出しても、違いはありません。
したがって、との欠点はstrtok()
、strsep()
両方とも入力文字列を変更することですが、上記のようstrsep()
にいくつかの利点がありstrtok()
ます。
strsepから:
strsep()関数は、strtok()関数の代わりとして使用することを目的としています。strtok()関数は移植性の理由から優先されるべきですが(ISO / IEC 9899:1990( `` ISO C90'')に準拠)、空のフィールドを処理できません。つまり、2つの隣接する区切り文字で区切られたフィールドを検出できません。または、一度に複数の文字列に使用されます。strsep()関数は4.4BSDで最初に登場しました。
参考のために: