2011-01-14 24 views
4

strtokを使用して文字列madddyを分割する次のスニペットを考えてみましょう。strtokを理解しようとする

char* str = (char*) malloc(sizeof("Madddy")); 
strcpy(str,"Madddy"); 

char* tmp = strtok(str,"d"); 
std::cout<<tmp; 

do 
{ 
    std::cout<<tmp; 
    tmp=strtok(NULL, "dddy"); 
}while(tmp!=NULL); 

出力はMaです。しかし、strtokを次のように変更することで、出力はMaddになります。だからstrtokはどういう仕組みになっていますか?私はこの問題を抱えています。なぜなら、strtokはデリミタ文字列に含まれるすべての文字をデリミタとして扱うことを期待していたからです。しかし、特定のケースではそうしていますが、ほとんどの場合予期しない結果が出ています。誰も私がこれを理解するのを助けることができますか?

+6

私は正直これを行うための正しい方法は、完全に 'strtok'の使用を停止することだと思います。これは、スレッドセーフな保証がない、使いにくく、デバッグが難しい機能です。おそらく 'string :: find'と' string :: substr'の組み合わせを使って解析するのがよいでしょう。 – templatetypedef

+0

または 'boost :: token_iterator' –

+0

私はC言語ではなくC++を使用しているので、特に重要で強調するためにこれを繰り返していきたいと思います。また、boost :: tokenizeを調べることもできます。 –

答えて

1

デリミタ「d」でstrtokを初めて呼び出すと(ループ外)忘れてしまったようです。

strtokは正常に動作しています。参照番号hereが必要です。

第二の例(strtok("ay"))の場合:

まず、あなたは(STR、 "D")はstrtokを呼び出します。それは最初の "d"を探し、あなたの文字列を分離します。具体的には、tmp = "Ma"、str = "ddy"(最初の "d"を削除)を設定します。

次に、strtok(str、 "ay")を呼び出します。 strの "a"が検索されますが、文字列は "ddy"だけなので、一致しません。それから、 "y"が検索されます。だからstr = "dd"とtmp = ""。

あなたが見たように "Madd"が印刷されます。

+0

@ Karthick:最初の例はうまくいきますが、あなたが思うように動作しないかもしれません。私はcout << tmpの代わりに、実際に何が起こるかを見るためにcout << tmp << " - "を使用することを推奨します。空の文字列がたくさんあることがわかります。 –

10

"strtokを理解しようとしています"幸運を祈る!とにかく

、我々は適切に2011年Tokeniseにいる:

std::string str("abc:def"); 
char split_char = ':'; 
std::istringstream split(str); 
std::vector<std::string> token; 

for (std::string each; std::getline(split, each, split_char); token.push_back(each)); 

:D

+3

はい、私はこれが厳密に質問に答えるものではないことを認識しています。しかし、IMOは価値ある優れた選択肢なので、まだ答えがあると思います。 –

+0

私はこの問題を完全に認識しています。しかし、私は標準仕様について興味がありました! –

3

フレッド・フリントストーンは、おそらくstrtok()を使用しました。これは、マルチスレッド環境に先行し、ソース文字列を打ち(変更)ます。

最初のパラメータにNULLと呼ばれると、最後の文字列の解析が続けられます。この機能は便利でしたが、その日でも珍しいです。あなたのコードが間違っている

+0

+1フレッドフリントストーン参照。 –

2

実は、あなたが予期しない結果を得るのは不思議:

char* str = (char*) malloc(sizeof("Madddy")); 

char* str = (char*) malloc(strlen("Madddy") + 1); 
+1

最初の例では、おそらくポインターのサイズである4バイト(32ビット環境で)を割り当てます。 '' abcdefghijkm ''のような文字列定数の型はポインタです(具体的には、' 'char *' 'または' 'const char *' ')。 – wallyk

+1

@wallyk:実際、[それは](http://codepad.org/H7zJkjCN)です。文字列リテラルの型はchar配列です。 @アンダー:コードは変わっていますが、あなたの作品は同じです。* –

+0

@Fred Nurk:そうです。それはパターンを破るように見えますが、おそらくもっと役に立つでしょう。それは長い間、私が避ける構成物でした。 (上記の2つの誤った情報のためにお詫び申し上げます。) – wallyk

0

べきではない私は、functions causing security problems/bad practise functions and the c standard libraryについて別の質問からインスピレーションを得た質問をしました。

がそこから私に与えられた答えを引用すると:

それ が実際に区切り 文字とを置き換えながらstrtok() 機能と共通の落とし穴は、解析され 文字列が変更されないと仮定することです'\0'

また、 文字列全体がトークン化されるまで、 以降の呼び出しによってstrtok()が使用されます。いくつかの ライブラリの実装strtok()が 同時に複数のスレッドから呼び出さ であれば、いくつかの 厄介なサプライズを誘導することができる グローバル変数、で店舗 strtok()の内部状態。

あなたの質問にC++をタグ付けしたので、別のものを使用してください。 Cを使いたいのであれば、安全な方法で動作する独自のトークナイザを実装することをお勧めします。

0

タグをCではなくCに変更したので、printfを使用して何が起こっているかを見ることができるように関数を書き直しました。ホアンは正しいです。あなたは正しい出力を見ていますが、私は同じ行にすべてを印刷していると思いますので、出力が混乱します。 Hoangの答えを見て、彼は何が正しく起こっているか説明します。また、他の人が指摘しているように、strtokは入力文字列を破壊するので、注意する必要があります。スレッドセーフではありません。しかし、あなたがすばやく汚れたトークナイザを必要とするならば、それは動作します。また、Andersが正しく指摘しているように、sizeofではなくstrlenを正しく使用するようにコードを変更しました。ここで

はあなたのコードはよりC-ようなことのように変更されます。

char* str = (char*) malloc(strlen("Madddy") + 1); 
strcpy(str,"Madddy"); 

char* tmp = strtok(str,"d"); 
printf ("first token: %s\n", tmp); 

do 
{ 
    tmp=strtok(NULL, "ay"); 
    if (tmp != NULL) { 
     printf ("next token: %s\n", tmp); 
    } 
} while(tmp != NULL); 
関連する問題