1

私はかなり些細なことに困惑しています...したがって、基本的には、最初のものと最後のものの間の「単語」をデータに移動し、最後のものをキーに移動したいと考えています。

C-POSIX のみです。

strtok_r は行くべき道ですか、それとも私はこれをやめていますか? 他の何か?

char *key = NULL, *data=NULL, *save=NULL;
char comando[1024];
fgets(comando, 512, stdin);

strtok_r(comando, " ",&save);

while(strcmp(save,"\n")){
    strcat(data,strtok_r(NULL," ",&save));
}

key = strtok_r(NULL, "\n",&save);

PS: メモリは問題ではなく、申し訳ありませんが安全であるため、comando は 1024 です。fgets は 512 を読み取ります。これは、標準の UNIX 端末の char 行の制限であるためです。

4

3 に答える 3

1

ループを次のコードに置き換えることをお勧めします (printf() はテストのためだけに使用されます)。

strtok_r(comando, " ", &save);
char *res = NULL;
while (NULL != (res = strtok_r(NULL, " ", &save))) {
  if (key != NULL) {
    //strcat(data, key); // FIXME
    printf("data = %s\n", key);
  }
  key = res;
}
printf("key = %s\n", key);

また、strcat() は NULL 引数で使用しないでください - クラッシュにつながります。したがって、データポインターは何らかの配列を指している必要があります。コードの実行結果:

┌─(16:08:22)─(michael@lorry)─(~/tmp/strtok)
└─► gcc -o main main.c; echo "one two three four five" | ./main
data=two
data=three
data=four
key = five
于 2013-10-31T12:09:15.090 に答える
1

コードは次の行でクラッシュします。

strcat(data,strtok_r(NULL," ",&save));

のスペースを予約したことがないからですdata。メモリアドレスstrcatへの書き込みを試みます。NULL

saveもう 1 つ注意すべき点は、行末のチェックに頼るべきではないということです。のマンページによるとstrtok

saveptr 引数は、同じ文字列を解析する連続する呼び出し間のコンテキストを維持するために、strtok_r() によって内部的に使用される char * 変数へのポインターです。

saveptroutside ofの値に依存して抽象化レイヤーを破るので、 がどのようにを使用strtok_rするかについて何も想定しないでください。それは悪い習慣です。strtoksaveptr

少し良い方法は、 によって返された前のトークンstrtokへのポインターと、現在のトークンへのポインターを保持することです。が NULL を返す場合strtok、これ以上トークンがないことを意味prevし、最後のトークンを指します。これはあなたのkey. ここにいくつかのコードがあります:

char *key = NULL, *save=NULL;
char *prev, *curr;
char comando[1024];
char data[1024];

data[0] = '\0';
fgets(comando, 512, stdin);
prev = curr = strtok_r(comando, " ",&save);

while (curr != NULL) {
    prev = curr;
    curr = strtok_r(NULL, " ", &save);
    if (curr != NULL)
        strcat(data, prev);
}

key = prev;

dataポインターではなく配列として宣言することで、スペースを割り当てたことに注意してください。命令

data[0] = '\0';

strcatが最初の呼び出しでヌル終了バイトを見つけることを確認するためにあります。

prevの使用を に直接置き換えることができますkey。コードを読みやすくするためにそのままにしておきます。

アドバイス:strtokはその引数を破壊的に変更する (区切りバイトの ID を失う) こと、および定数文字列で呼び出すことはできないことを常に覚えておいてください。

注: data連結されたすべての単語が含まれます。スペースを失います。これがあなたの望むものかどうかわかりません。strcatそうでない場合は、 (あまり効率的ではありませんが)よりも優れたものを使用することをお勧めします。たとえば、先頭にスペースを付けsprintfて にトークンをdata出力し、 の次の空き位置へのポインターを保持するためにコードを使用しますdata

于 2013-10-31T12:04:26.737 に答える
1

あなたのコードには多くの間違いがあります

char *key = NULL, *data=NULL, *save=NULL;

後で、 にstrcat文字列を追加するために を使用してdataいますが、 にストレージを割り当てていませんdata。これにより、セグメンテーション違反が発生します。

fgets(comando, 512, stdin);

fgets渡された数値より最大で 1 つ少ない値を読み取ります。そのため、ユーザーが 512 文字を入力すると、文字列には終端がありません\n。また、エラーまたはファイルの終わりを検出する唯一の方法は、 の戻り結果を確認することですfgets。NULL の場合は、ファイルの終わりに達した (ユーザーが ctrl-d を押した) か、エラーがあります。いずれの場合も、バッファの内容は不確定です。

while(strcmp(save,"\n"))

saveポインターが未使用の文字列の残りの部分を指しているという前提に頼ることは許されていないと思います。

strtok_r(comando, " ",&save);

strtok_rNULLポインターを返すことによって、データの最後に到達したことを通知します。返された結果を見ずに捨てることはできません。\nまた、これは最後のトークンの一部として末尾を消費します。

strcat(data,strtok_r(NULL," ",&save));

前に言ったようにdata、null ポインターです。また、strtok_r返品可能NULL

私はもっ​​と好きなことをします:

char* currentTok = strtok_r(commando, " \n", &save); // separator is space or \n
char* previousTok = NULL;
while (currentTok != NULL)
{
    if (previousTok != NULL)
    {
        // save previousTok in data unless its the first token
    }
    previousTok = currentTok;
    currentTok = strtok_r(NULL, " \n", &save);
}
char* key = previousTok;
于 2013-10-31T12:20:25.013 に答える