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 eat 4bananas in themorning”。

我的问题是: 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)

这总是正确的。If cis SPACEthen it is not TAB,所以第二部分是真的。如果c是 TAB 则不是SPACE,所以第一部分是正确的。

在这两种情况下,您想要的是(c != SPACE && c != TAB) This is only true when cis notSPACE和 not TAB。运算符&&是布尔“和”。

另外,我建议不要使用像13你这样的幻数,而应该使用 C 字符常量,比如'\r'.

至于你的第二个问题,你的程序写得还不错。我绝对不认为你会通过添加 a 来改进它,continue我什至不太明白它会如何工作。(正如您所指出的,如果else continue;是在循环的最后,您可以省略continue; 实际上,您可以直接砍掉整个else,因为else;什么都不做。)

你写了一个小状态机。你有三个有趣的案例:

  • 在状态 OUT_W 中,需要转换到状态 IN_W(这里是打印 CR/LF 以转到下一行的位置)
  • 在状态 IN_W,找到另一个字符,停留在 IN_W(这里打印字符)
  • 在状态 IN_W 中,找到了一个制表符或空格,因此需要转换到状态 OUT_W

还有第四种可能:

  • 在 OUT_W 状态下,找到另一个选项卡或空格,留在 OUT_W 中(第三种情况处理得很好)

如果您想要最有效的代码,我认为最好重新排列它,以便您只需要在一个地方检查SPACE和:TAB

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 call putchar(c);,但在检查过渡后执行此操作。我会把它留给你作为练习。

于 2012-06-12T02:21:04.013 回答
1

作为一个开始:

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

总是正确的。一个字符不能同时是空格和制表符,因此它必须始终是非制表符或非空格。我怀疑你的意思是:

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

这就是为什么状态永远不会回到的原因OUT_W,因为在第一行结束序列之后,第二个if语句总是正确的,所以它永远不会回到最后else一位。

以下代码可以正常工作:

#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;
}

我要提到的一件事是,通常最好将状态机本身与执行的操作分开。为此,我会根据当前状态而不是字符/状态对做出主要选择,并将每个状态的动作与状态机分开。

我认为这使事情更具可读性,并且更容易修改:

#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 回答