3

私はCのソースコードを持っています。

#include <stdio.h>
#define IN_W 1
#define OUT_W 0
#define SPACE 32
#define TAB 9

int main() { 
    int c, state, temp;
    state = OUT_W;

    while ((c = getchar()) != EOF) {
        if ((c != SPACE || c != TAB) && (state == OUT_W)) {
            state = IN_W;
            temp = c;
            c = 13;
            putchar(c);
            c = 10;
            putchar(c);
            putchar(temp);          
        } else if (c != SPACE || c != TAB)
            putchar(c);     
        else 
            state = OUT_W;      
    }   
    return 0;
}

私が達成したいのは、いくつかの文字/単語を入力し、それらの入力をgetcharでキャッチすることです。getcharがスペースまたはタブ以外の文字を受け取ると、新しい行が印刷され、スペースまたはタブが見つかるまで(それらを破棄して)それらの文字が印刷されます。たとえば、私が入力すると

123  eat    4bananas     in themorning

プログラムは印刷されます

123
eat
4bananas
in
themorning

CRまたはLFと統合しようとしましたが、それでも「123は朝に4バナナを食べる」と印刷されます。

私の質問は次のとおりです。1。何が恋しかったですか?2.最後の「else」では、実行中のプログラムにとってどちらがより効率的ですか。

    else 
        state = OUT_W;

また

    else if ((c == SPACE || c == TAB) && state == IN_W)
        state = OUT_W;
    else
        continue;        // or can I use single ';' since we do nothing in here?

それで全部です。ご協力ありがとうございました。

注:「\n」と「\t」も試してみました。

よろしく、マリオ

4

2 に答える 2

3

この式はあなたが望むものではありません:(c != SPACE || c != TAB)

これは常に真実です。である場合cSPACEではないTABため、2 番目の部分は true になります。cが TABの場合はそうではないSPACEので、最初の部分は true になります。

どちらの場合も、必要なのは(c != SPACE && c != TAB) This is only true when cis notSPACEおよび notTABです。演算子&&はブール「and」です。

また、あなたのようなマジックナンバーの代わりに、 のような13C 文字定数を使用することをお勧めします'\r'

2 番目の質問についてですが、あなたのプログラムは書かれているほど悪くはありません。を入れることで改善するとは絶対に思いませんし、continueそれがどのように機能するかさえよくわかりません。(指摘したように、else continue;がループの最後にある場合continueは、を省略できます。実際には、何もしないelseため、全体を切り取ることができます。)else;

あなたは小さなステートマシンを書きました。興味深いケースが 3 つあります。

  • 状態 OUT_W では、状態 IN_W に遷移する必要があります (ここで、CR/LF を出力して次の行に移動します)
  • 状態IN_Wで、別の文字を見つけ、IN_Wにとどまる(ここで文字を印刷します)
  • ステート IN_W で、タブまたはスペースが見つかったため、ステート OUT_W に遷移する必要があります

4 番目の可能性があります。

  • 状態 OUT_W で、別のタブまたはスペースを見つけ、OUT_W にとどまる (3 番目のケースで問題なく処理される)

SPACE最も効率的なコードが必要な場合は、コードを並べ替えて、TAB1 か所だけをチェックするだけで済むようにするのが最善だと思います。

while ((c = getchar()) != EOF) {
    if (c == SPACE || c == TAB) {
        state = OUT_W;
    }
    else {
        /* c is not SPACE or TAB so we will print it */
        if (state == OUT_W) {
            /* transition from OUT_W to IN_W */
            state = IN_W;
            putchar('\r');
            putchar('\n');
            putchar(c);
        }
        else 
            putchar(c);
    }
}

この再構成されたバージョンのコードでは、IN_W にいるときはいつでも文字を出力しますが、トランジションでのみ CR/LF を出力することが明らかになります。したがって、これを短縮してelse, always callputchar(c);を持たないようにすることもできますが、トランジションのチェック後にそれを行います。私はあなたのための演習としてそれを残しておきます.

于 2012-06-12T02:21:04.013 に答える
1

はじめに:

(c != SPACE || c != TAB)

は常に真です。1 つの文字が同時にスペースとタブの両方になることはできないため、常に非タブまたは非スペースのいずれかでなければなりません。私はあなたが意味したと思います:

(c != SPACE && c != TAB)

これが、最初の行末シーケンスの後、2 番目のステートメントが常に true であるため、状態がに戻ることはないため、その最後のビットに到達することはありません。OUT_Wifelse

次のコードは問題なく動作します。

#include <stdio.h>
#define IN_W 1
#define OUT_W 0
#define SPACE 32
#define TAB 9

int main (void) {
    int c, state, temp;
    state = OUT_W;

    while ((c = getchar()) != EOF) {
        if ((c != SPACE && c != TAB) && (state == OUT_W)) {
            state = IN_W;
            temp = c;
            c = 13;
            putchar(c);
            c = 10;
            putchar(c);
            putchar(temp);
        } else if (c != SPACE && c != TAB)
            putchar(c);
        else
            state = OUT_W;
    }
    return 0;
}

迷惑な最初の改行がまだありますが、初期状態を に設定するだけで修正できますIN_W

また、コードには多くのマジック ナンバーがあり、かなり不必要な値の移動もあります。おそらく、より洗練されたバージョンは次のようになります。

#include <stdio.h>

#define IN_W 1
#define OUT_W 0

#define SPACE ' '
#define TAB '\t'
#define CR '\r'
#define LF '\n'

int main (void) {
    int c, state;

    state = IN_W;
    while ((c = getchar()) != EOF) {
        if ((c != SPACE) && (c != TAB) && (state == OUT_W)) {
            putchar(CR);
            putchar(LF);
            putchar(c);
            state = IN_W;
        } else if ((c != SPACE) && (c != TAB))
            putchar(c);
        else
            state = OUT_W;
    }

    return 0;
}

1 つ述べておきたいのは、実行されるアクションからステート マシン自体を分離することが望ましい場合が多いということです。そのために、文字と状態のペアではなく現在の状態に基づいて主要な選択を行い、各状態のアクションを状態マシンから分離します。

これにより、物事がより読みやすくなり、変更が容易になると思います。

#include <stdio.h>

enum tState { ST_WORD, ST_SPACE };

static enum tState doWord (int ch) {
    if ((ch == ' ') || (ch == '\t')) {
        putchar ('\r');
        putchar ('\n');
        return ST_SPACE;
    }
    putchar (ch);
    return ST_WORD;
}

static enum tState doSpace (int ch) {
    if ((ch == ' ') || (ch == '\t'))
        return ST_SPACE;
    putchar (ch);
    return ST_WORD;
}

int main (void) {
    int ch;
    enum tState state = ST_WORD;

    while ((ch = getchar()) != EOF) {
        switch (state) {
            case ST_WORD:  state = doWord  (ch); break;
            case ST_SPACE: state = doSpace (ch); break;
        }
    }

    return 0;
}
于 2012-06-12T02:18:49.840 に答える