无限循环
您报告的无限循环可能是由于在发布的代码中对EOF 的处理不正确造成的。普通的char 可能是signed 或unsigned,因此可能无法保存EOF 的值,通常是-1。
skip() 函数也无法解释可能遇到EOF 的可能性。在这里,如果在空格之前遇到EOF,例如在文件的最后一个单词中,将导致无限循环。
头文件
发布的代码缺少头文件。至少需要#include <stdio.h> 和#include <stdbool.h>。出于显而易见的原因,需要stdio.h,但也需要stdbool.h,因为这是定义true 和false 宏的地方。 _Bool 类型仍然可以使用,但最好使用stdbool.h 中定义的typedef bool。
函数声明
函数原型中的空括号表示未指定数量的参数。 void 必须用于指定要采用 no 参数。这种在函数声明器中使用空括号是该语言的一个过时特性,无论如何都不应该使用。
ANSI 转义码
在 C 中打印颜色的能力取决于平台和终端仿真器。一些教程使用\e 作为转义字符的替代,但是要在C 中生成这个字符,您可能应该使用\x1b(ESC 字符的ASCII 十六进制值;您也可以使用\033,即ESC 的 ASCII 八进制值)。我对标准的解读是 \e 不是有效的字符常量,使用它会产生编译器警告:
警告:非 ISO 标准转义序列,'\e'
但是,这在 Linux 上的 GCC 中似乎对我有用,所以我怀疑这是一个扩展。
逻辑问题
skip() 函数无法检查EOF,但遇到空格时也无法打印。此外,换行符应该能够在数字之前,因为这表示换行符。不正确处理会导致无法突出显示输入第一行之后的行上的初始数字。这些问题可以通过在循环条件中测试EOF 和\n 来解决,并通过打印导致循环终止的字符如果它是空格或换行符。
void skip(void) // skip whole words and still print them
{
putchar(ch);
while ((ch = getchar()) != ' ' && ch != '\n' && ch != EOF) {
printf("%c", ch);
}
if (ch == ' ' || ch == '\n') {
putchar(ch);
}
}
main() 也有类似的问题。当在主循环中遇到EOF 时,最终的printf() 仍然被执行,在不应该打印的时候打印一个字符。一种解决方案是将EOF 测试之后的语句放在else 块中,尽管有更好的解决方案。
while (finished)
{
ch = getchar();
if (ch == EOF) { // use EOF instead of -1
finished = false;
} else { // need this to avoid printing a character for EOF
if ( (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') ) {
skip();
} else if (ch >= '0' && ch <= '9') {
color();
}
else {
printf("%c", ch);
}
}
}
这是目前为止的程序。据我从您对预期行为的描述中可以看出,这可以按预期工作:
#include <stdio.h>
#include <stdbool.h>
int ch;
bool finished = true;
void color(void);
void skip(void);
int main(void)
{
while (finished)
{
ch = getchar();
if (ch == EOF) {
finished = false;
} else {
if ( (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') ) {
skip();
} else if (ch >= '0' && ch <= '9') {
color();
}
else {
printf("%c", ch);
}
}
}
return 0;
}
void color(void) // color characters that are numbers
{
printf("\x1b[31m%c\x1b[0m", ch);
}
void skip(void) // skip whole words and still print them
{
putchar(ch);
while ((ch = getchar()) != ' ' && ch != '\n' && ch != EOF) {
printf("%c", ch);
}
if (ch == ' ' || ch == '\n') {
putchar(ch);
}
}
改进
可以对此代码进行许多改进。
根本不需要bool。当遇到EOF 时,break 语句可以简单地终止主循环。在这种情况下,主循环需要无限期地执行,使用while (1) {},或者更好的是for (;;) {}。更好的是,使用while ((ch = getchar()) != EOF) {} 在循环条件下测试ch。
删除全局变量finished后,我们还可以将ch局部化为main()。这可能需要将ch 的值传递给color() 和skip(),在这种情况下需要更改函数签名。但是请注意,没有理由将字符传递给skip(),因为在调用skip() 之前,可以简单地在main() 中打印该字符。此外,不需要color() 函数,因为这个单行函数可以简单地手动内联。
没有必要在putchar() 的地方使用printf()。
最好#define 几个转义码宏。这更容易阅读,也更容易修改。
最后,最好使用ctype.h 中的函数来检查字符是数字、字母字符还是空白字符。与发布代码中的直接比较相比,这更便携,更不容易出错。通过在skip() 函数中使用isspace(),会自动检查\n 字符,避免之前忘记测试行尾的问题。这也处理其他空白字符,例如\t。请注意,ctype.h 中的函数需要可以表示为 unsigned char 值的参数,因此这里需要进行转换。
这是改进后的代码:
#include <stdio.h>
#include <ctype.h>
#define RED "\x1b[31m"
#define RESET "\x1b[0m"
void skip(void);
int main(void)
{
int ch;
while ((ch = getchar()) != EOF) {
if (isalpha((unsigned char) ch)) {
putchar(ch);
skip();
} else if (isdigit((unsigned char) ch)) {
printf(RED "%c" RESET, ch);
} else {
putchar(ch);
}
}
return 0;
}
void skip(void) // skip whole words and still print them
{
int c = getchar();
while (!isspace((unsigned char) c) && c != EOF) {
putchar(c);
c = getchar();
}
if (isspace((unsigned char) c)) {
putchar(c);
}
}
这是一个示例程序输出。颜色没有显示在这里,所以我在终端上以红色突出显示的数字加上括号:
(1) this is a test testing123 (456) test
(2) second line test (3) (4) (5)
(3)rd line test
(4) (5) (6) (789)