1
char line[255];
char *token = NULL;
char *line2 = NULL;
char *temporaryToken = NULL;

if( scanf(" %[^\n]", line) > 0)
    token = strtok( line, ";" ); //divide the line by ;
    do
    {
        line2 = token;
        temporaryToken = strtok(line2, " ");
        do
        {
            //divide the line2 by spaces into command and args, not the question here]
            temporaryToken = strtok( NULL, " " );
        }while (temporaryToken != NULL );
        token = strtok( NULL, ";" );
    }while(token != NULL);

ちなみに、これは私のコードを逐語的にではなく、それがどのように設定されているかの単なる例です

私のプログラムでは、2回目に分割する前に「トークン」変数を出力すると、;まですべてが出力されます。キャラクター。

たとえば、stdInが「ls-la; mkdir lololol; ls -la」を取り込んだ場合、「ls-la」と出力されます。ただし、2回目の分割後、「token」を出力すると「ls」のみが出力されます。

これはなぜですか、どうすれば修正できますか?

4

2 に答える 2

4

strtok元の文字列を変更します。このような呼び出しを混在させたい場合は、コピーを作成するか、を使用する必要がありますstrtok_r

于 2012-08-11T05:11:39.453 に答える
1

には2つの問題がありますstrtok()

  1. 入力文字列を変更します。
  2. strtok()一度にアクティブにできるコールのセットは1つだけです。

あなたの問題は後者だと思います。コードにインデントの問題もあります。

if (scanf(" %[^\n]", line) > 0)
    token = strtok( line, ";" );
do
{
    line2 = token;
    temporaryToken = strtok(line2, " ");
    do
    {
        //divide the line2 by spaces into command and args, not the question here]
        temporaryToken = strtok(NULL, " ");
    } while (temporaryToken != NULL);
    token = strtok( NULL, ";" );
} while(token != NULL);

あなたはおそらくそれを読むことを意図していました:

if (scanf(" %[^\n]", line) > 0)
{
    token = strtok(line, ";");
    do
    {
        line2 = token;
        temporaryToken = strtok(line2, " ");
        do
        {
            //divide the line2 by spaces into command and args, not the question here]
            temporaryToken = strtok(NULL, " ");
        } while (temporaryToken != NULL);
        token = strtok(NULL, ";");
    } while (token != NULL);
}

これが意図したものであると仮定すると、で実行されているものがあり、次にでstrtok()実行されているものがあるという問題があります。問題は、のループがの解釈を完全に破壊することです。ネストされたループを。で使用することはできません。lineline2line2linestrtok()

のようなものを使用する必要がある場合は、POSIXまたはMicrosoftのstrtok()いずれかを探します(ただし、C11標準のAnnexKバージョンは異なることに注意してください。TR24731の「安全な」機能を使用していますか?を参照してください)。strtok_r()strtok_s()strtok_s()

if (scanf(" %[^\n]", line) > 0)
{
    char *end1;
    token = strtok_r(line, ";", &end1);
    do
    {
        char *end2;
        line2 = token;
        temporaryToken = strtok_r(line2, " ", &end2);
        do
        {
            //divide the line2 by spaces into command and args, not the question here]
            temporaryToken = strtok_r(NULL, " ", &end2);
        } while (temporaryToken != NULL);
        token = strtok_r(NULL, ";", &end1);
    } while (token != NULL);
}

コメントについて

またはその親戚の1つを使用している間strtok()、入力文字列が変更され、複数の区切り文字がある場合、どの区切り文字が存在したかを判断できなくなります。文字列のコピーを操作して、比較を行うことができます(通常は文字列の先頭からのオフセットに基づいています)。

使用の制限内で、strtok_r()上記のソリューションは「機能します」。これがデモンストレーションするテストプログラムです:

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

int main(void)
{
    char line[1024];

    if (scanf(" %[^\n]", line) > 0)
    {
        char *end1;
        char *token;
        printf("Input: <<%s>>\n", line);
        token = strtok_r(line, ";", &end1);
        do
        {
            char *end2;
            char *line2 = token;
            char *temporaryToken;
            printf("Token1: <<%s>>\n", token);
            temporaryToken = strtok_r(line2, " ", &end2);
            do
            {
                printf("Token2: <<%s>>\n", temporaryToken);
                //divide the line2 by spaces into command and args, not the question here]
                temporaryToken = strtok_r(NULL, " ", &end2);
            } while (temporaryToken != NULL);
            token = strtok_r(NULL, ";", &end1);
        } while (token != NULL);
    }

    return 0;
}

入力と出力の例:

$ ./strtok-demo
ls -la; mkdir lololol; ls -la
Input: <<ls -la; mkdir lololol; ls -la>>
Token1: <<ls -la>>
Token2: <<ls>>
Token2: <<-la>>
Token1: << mkdir lololol>>
Token2: <<mkdir>>
Token2: <<lololol>>
Token1: << ls -la>>
Token2: <<ls>>
Token2: <<-la>>
$

代替使用strcspn()strspn()

元の文字列を取り壊したくない場合は、strtok()ファミリ以外の関数を使用する必要があります。機能strcspn()strspn()は適切です。これらは標準C(C89以降のバージョン)の一部ですが、他のいくつかの機能ほどよく知られていません。しかし、彼らはこのタスクにぴったりです。

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

static char *substrdup(const char *src, size_t len);

int main(void)
{
    char line[1024];

    if (scanf(" %[^\n]", line) > 0)
    {
        char *start1 = line;
        size_t len1;
        printf("Input: <<%s>>\n", line);
        while ((len1 = strcspn(start1, ";")) != 0)
        {
            char *copy = substrdup(start1, len1);
            char *start2 = copy;
            size_t len2;
            printf("Token1: %zd <<%.*s>>\n", len1, (int)len1, start1);
            printf("Copy: <<%s>>\n", copy);
            start2 += strspn(start2, " ");      // Skip leading white space
            while ((len2 = strcspn(start2, " ")) != 0)
            {
                printf("Token2: %zd <<%.*s>>\n", len2, (int)len2, start2);
                start2 += len2;
                start2 += strspn(start2, " ");
            }
            free(copy);
            start1 += len1;
            start1 += strspn(start1, ";");
        }
        printf("Check: <<%s>>\n", line);
    }

    return 0;
}

#include <assert.h>

static char *substrdup(const char *src, size_t len)
{
    char *copy = malloc(len+1);
    assert(copy != 0);              // Apalling error handling strategy
    memmove(copy, src, len);
    copy[len] = '\0';
    return(copy);
}

入力と出力の例:

$ strcspn-demo
ls -la; mkdir lololol; ls -la
Input: <<ls -la; mkdir lololol; ls -la>>
Token1: 140734970342872 <<>>
Copy: <<ls -la>>
Token2: 2 <<ls>>
Token2: 3 <<-la>>
Copy: << mkdir lololol>>
Token2: 5 <<mkdir>>
Token2: 7 <<lololol>>
Copy: << ls -la>>
Token2: 2 <<ls>>
Token2: 3 <<-la>>
Check: <<ls -la; mkdir lololol; ls -la>>
$

whileこのコードは、ループを使用する必要がなく、より快適なループに戻りdo-whileます。これは利点です。

于 2012-08-11T07:10:22.377 に答える