7

次のプログラムでstrtok()は、大部分は期待どおりに動作しますが、1 つの発見の背後にある理由を理解できません。私はそれについて読んstrtok()だ:

トークンの開始と終了を決定するために、関数は最初に区切り文字に含まれていない最初の文字 (トークンの開始となる) の開始位置からスキャンします。次に、このトークンの先頭から、区切り記号に含まれる最初の文字をスキャンします。これがトークンの末尾になります。

ソース: http://www.cplusplus.com/reference/cstring/strtok/

ご存知のように、各トークンの末尾にstrtok()aを配置します。\0しかし、次のプログラムでは、最後の区切り文字はドット ( .) であり、その後、そのドットと引用符 ( ) の間にToad"があります。現在、ドットは私のプログラムの区切り記号ですが、Toadの後に区切り記号はなく、空白 (私のプログラムの区切り記号) もありません。この前提から生じる次の混乱を解消してください。

2 つの区切り文字の間にないのに、Toadstrtok()をトークンと見なすのはなぜですか? これは、NULL文字( )に遭遇したときに私が読んだことです:strtok()\0

strtok の呼び出しで str の終端の null 文字が検出されると、最初の引数として null ポインターを使用したこの関数への後続の呼び出しはすべて null ポインターを返します。

ソース: http://www.cplusplus.com/reference/cstring/strtok/

null 文字が検出されると、トークンの先頭へのポインターが返されるとはどこにも書かれていません (区切り文字が見つからなかったため、トークンの末尾を取得できなかったため、ここにはトークンすらありません)。トークンの先頭 (つまり、Toad の「T」) から開始されたスキャンの後、区切り文字ではなく、null 文字のみが見つかりました)。では、引数文字列の最後の区切り文字と引用符の間の部分がトークンと見なされるのはstrtok()なぜですか? これを説明してください。

コード:

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

int main ()
{
  char str[] =" Falcon,eagle-hawk..;buzzard,gull..pigeon sparrow,hen;owl.Toad";
  char * pch=strtok(str," ;,.-");

    while (pch != NULL)
  {
    printf ("%s\n",pch);
    pch = strtok (NULL, " ;,.-");
  }

  return 0;
}

出力:

ハヤブサ
ワシミミズク
ノスリ カモメ ハト スズメ
メンドリ フクロウ ヒキガエル _





4

5 に答える 5

9

strtok(7.24.5.8)の標準仕様はかなり明確です。特に、段落4(私が強調を追加)は、私が正しく理解している場合、質問に直接関連しています。

3 シーケンスの最初の呼び出しは、 がs1指す現在の区切り文字列に含まれていない最初の文字を、 が指す文字列を検索しますs2。そのような文字が見つからない場合は、 が指す文字列にトークンがなくs1strtok関数はヌル ポインターを返します。そのような文字が見つかった場合、それが最初のトークンの始まりです。

4strtok関数はそこから、現在の区切り文字列に含まれる文字を検索します。そのような文字が見つからない場合、現在のトークンは が指す文字列の末尾まで拡張されs1、その後のトークンの検索では null ポインターが返されます。そのような文字が見つかった場合は、null 文字で上書きされ、現在のトークンが終了します。このstrtok関数は、トークンの次の検索が開始される次の文字へのポインターを保存します。

通話中

char *where = strtok(string_or_NULL, delimiters);

返されるトークン (ポインタ) は、開始位置 (含む) から見つかった最初の非区切り文字から次の区切り文字 (含まない) まで (存在する場合)、または文字列の末尾までです。後の区切り文字が存在しない場合。

リンクされた説明では、標準とは対照的に、文字列の最後までトークンが拡張されている場合について明示的に言及していないため、その点では不完全です。

于 2013-05-15T19:50:59.880 に答える
4

のPOSIXの説明に行くと、説明にstrtok()は次のように書かれています:

char *strtok(char *restrict s1, const char *restrict s2);

の一連の呼び出しはstrtok()、 が指す文字列をs1一連のトークンに分割します。各トークンは、 が指す文字列から 1 バイトで区切られますs2。シーケンス内の最初の呼び出しは、s1最初の引数として を持ち、最初の引数としてヌル ポインターを持つ呼び出しが続きます。が指す区切り文字列はs2、呼び出しごとに異なる場合があります。

シーケンスの最初の呼び出しは、 がs1指す現在の区切り文字列に含まれていない最初のバイトを、 が指す文字列を検索しますs2。そのようなバイトが見つからない場合、 が指す文字列にはトークンがなくs1strtok()null ポインターが返されます。そのようなバイトが見つかった場合、それは最初のトークンの始まりです。

次に、strtok()関数はそこから、現在の区切り文字列に含まれるバイトを検索します。そのようなバイトが見つからない場合、現在のトークンは が指す文字列の末尾まで拡張されs1、その後のトークンの検索では null ポインターが返されます。そのようなバイトが見つかった場合、現在のトークンを終了する NUL 文字で上書きされます。関数は次のバイトへのstrtok()ポインタを保存し、そこからトークンの次の検索が開始されます。

3 番目の段落の 2 番目の文に注意してください。

そのようなバイトが見つからない場合、現在のトークンは が指す文字列の末尾まで拡張されs1、その後のトークンの検索では null ポインターが返されます。

Toadこれは、質問の例では、実際にトークンであることを明確に示しています。これを考える 1 つの方法は、区切り文字のリストには常に'\0'区切り文字列の末尾に NUL が含まれているということです。


それを診断した後、strtok()これは使用するのに適した関数ではないことに注意してください — これはスレッドセーフでも再入可能でもありません。strtok_s()Windows では、代わりに使用できます。Unix では、通常、 を使用できますstrtok_r()。これらは、検索を再開するポインタを内部的に格納しないため、より優れた関数です。

は再入可能ではないため、 を使用しているときに、それ自体が使用する関数内からstrtok()を使用する関数を呼び出すことはできません。また、 を使用しているライブラリ関数は、 を使用している関数から呼び出すことができないため、使用していることを明確に識別する必要があります。したがって、使用すると生活が難しくなります。strtok()strtok()strtok()strtok()strtok()strtok()

strtok()関数のファミリ (および関連する ) のもう1 つの問題はstrsep()、デリミタが上書きされることです。トークナイザーが文字列をトークン化した後、区切り文字が何であったかを知ることはできません。これは、一部のアプリケーション (シェル コマンド ラインの解析など) で問題になる可能性があります。区切り文字がパイプ、セミコロン、アンパサンド (または ...) のいずれであるかが重要ですstrtok()。 SO パーサーが使用するシェルについてstrtok()

一般に、 plain を避けるべきであり、目的に適しているstrtok()かどうかを判断するstrtok_r()のはあなた次第です。strtok_s()

于 2013-05-15T19:56:27.073 に答える
2

cplusplus.com がすべてを伝えているわけではないからです。Cppreference.comには、より適切な説明があります。

Cplusplus.comstrtokでは、スレッド セーフではないことについても言及されておらず、C++ プログラミング言語の機能のみが文書化されていstrtokますが、cppreference.com ではスレッド セーフの問題について言及されており、 CC++strtokプログラミング言語の両方の機能が文書化されています。

于 2013-05-15T17:55:14.397 に答える
0

strtok は、指定された区切り文字で区切られた一連のトークンに文字列を分割します。区切り記号はトークンを分離するだけで、必ずしも両側で終了するわけではありません。

于 2013-05-15T17:11:01.320 に答える
0

説明を読み間違えただけでしょうか?

strtok の呼び出しで str の終了ヌル文字が検出されると、最初の引数としてヌル ポインターを使用したこの関数への後続の呼び出しはすべてヌル ポインターを返します。

「後続」を考えると、これは、必ずしも現在のもの自体ではなく、検出されたもののstrtok のすべての呼び出しとしてこれを読んでいます。\0したがって、定義は動作 (および から期待されるものstrtok) と一致しています。

于 2013-05-15T17:14:12.360 に答える