C代码 - 无法通过putchar插入新行。

时间:2012-06-12 02:14:43

标签: c

我在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'。

此致 马里奥

2 个答案:

答案 0 :(得分:3)

此表达式不是您想要的:(c != SPACE || c != TAB)

这总是如此。如果cSPACE,那么它不是TAB,那么第二部分就是真的。如果c是TAB,那么它不是SPACE所以第一部分是真的。

在这两种情况下,您想要的是(c != SPACE && c != TAB)仅当c不是SPACE而不是TAB时才会这样。运算符&&是布尔“和”。

另外,我建议您使用像13这样的C字符常量而不是像'\r'这样的幻数。

关于你的第二个问题,你的程序并没有写得那么糟糕。我绝对不认为你会通过加入continue来改善它,我甚至不知道它会如何起作用。 (正如您所指出的,如果else continue;位于循环的最后,您可以省略continue;实际上,您可以切断整个else,因为{ {1}}什么都不做。)

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

  • 在状态OUT_W中,需要转换到状态IN_W(这里是打印CR / LF以转到下一行的地方)
  • 在状态IN_W中,找到另一个角色,留在IN_W(这里打印角色)
  • 在状态IN_W中,找到一个标签或空格,因此需要转换到状态OUT_W

还有第四种可能性:

  • 在状态OUT_W,找到另一个标签或空格,留在OUT_W(第三种情况处理得不错)

如果你想要最有效的代码,我认为最好重新安排它,这样你只需要在一个地方检查else;SPACE

TAB

使用这个重新组织的代码版本,很明显,只要你在IN_W中打印字符,但只有在打印CR / LF的转换时。因此,您可以将此缩短为没有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); } } ,始终调用else,但在检查转换后执行此操作。我将把它作为练习留给你。

答案 1 :(得分: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;
}