我在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'。
此致 马里奥
答案 0 :(得分:3)
此表达式不是您想要的:(c != SPACE || c != TAB)
这总是如此。如果c
为SPACE
,那么它不是TAB
,那么第二部分就是真的。如果c
是TAB,那么它不是SPACE
所以第一部分是真的。
在这两种情况下,您想要的是(c != SPACE && c != TAB)
仅当c
不是SPACE
而不是TAB
时才会这样。运算符&&
是布尔“和”。
另外,我建议您使用像13
这样的C字符常量而不是像'\r'
这样的幻数。
关于你的第二个问题,你的程序并没有写得那么糟糕。我绝对不认为你会通过加入continue
来改善它,我甚至不知道它会如何起作用。 (正如您所指出的,如果else continue;
位于循环的最后,您可以省略continue
;实际上,您可以切断整个else
,因为{ {1}}什么都不做。)
你写了一个小型的状态机。你有三个有趣的案例:
还有第四种可能性:
如果你想要最有效的代码,我认为最好重新安排它,这样你只需要在一个地方检查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;
}