K&R习题1-21:entab程序空格转制表符输出长度异常求助
K&R《C程序设计语言》习题1-21:Entab程序问题排查与修正
需求说明
编写entab程序,用最少数量的制表符(Tab)和空格替换连续空格,实现相同间距:
- 制表位(Tab Stop)设为8
- 当制表符或单个空格都能到达下一个制表位时,优先使用制表符
第一版代码及问题
代码实现
#include <stdio.h> #define TAB 8 void entab(int space); int main() { int c, i; i = 0; while ((c = getchar()) != EOF) { if (c == ' ') ++i; else if (c == '\t') i = i + TAB; else { entab(i); i = 0; putchar(c); } } return 0; } void entab(int space) { int i; for (i = 1; space >= TAB; ++i) if (i == TAB) { putchar('\t'); i = 0; space = space - TAB; } for (i = 0; i < space; ++i) putchar(' '); }
问题现象
输入内容为3个空格+1个制表符+5个空格+j时,输出的字符长度比输入更长(原截图显示:第一行是输入,第二行是输出)。
问题原因
- 制表符处理逻辑错误:直接给空格计数加
TAB(8),但制表符的作用是跳到下一个制表位,而非固定占8个空格。比如当前处于第3个字符位置,一个制表符应占8 - 3%8 = 5个空格位置,而非直接加8。 entab函数循环冗余:用i从1计数到TAB才输出制表符,逻辑完全没必要,直接计算可替换的制表符数量即可。
第二版代码及新问题
代码实现
#include <stdio.h> #define TAB 8 void entab(int space); int main() { int c, i, l; i = l = 0; while ((c = getchar()) != EOF) { ++l; if (c == ' ') ++i; else if (c == '\t') i = i + TAB - (l%8); else { entab(i); i = 0; putchar(c); } if (c == '\n') l = 0; } return 0; } void entab(int space) { int i; for (i = 0; space >= TAB; ++i) if (i == TAB) { putchar('\t'); i = -1; space = space - TAB; } for (i = 0; i < space; ++i) putchar(' '); }
问题现象
输出总是比输入少一个字符(原截图显示:输入输出长度存在明显差异)。
问题原因
- 位置计数时机错误:
++l在处理字符前执行,导致制表位计算偏移1位,比如第一个字符未处理时l就变为1,l%8的结果出错。 entab函数循环逻辑仍错误:用i计数到TAB才输出制表符的逻辑完全多余,应直接计算空格数中包含的完整制表符数量。- 未处理结尾剩余空格:输入遇到EOF时,若还有未处理的累计空格,没有调用
entab输出。
修正后的代码
#include <stdio.h> #define TAB 8 void entab(int current_pos, int space_count); int main() { int c; int current_pos = 0; // 当前光标位置,从0开始计数 int space_count = 0; // 累计连续空格数 while ((c = getchar()) != EOF) { if (c == ' ') { space_count++; } else if (c == '\t') { // 计算制表符等效的空格数:到下一个制表位的距离 int needed = TAB - (current_pos % TAB); space_count += needed; // 立即处理累计空格 entab(current_pos, space_count); current_pos += needed; space_count = 0; } else { // 先处理累计的空格 if (space_count > 0) { entab(current_pos, space_count); current_pos += space_count; space_count = 0; } // 输出当前字符并更新位置 putchar(c); current_pos = (c == '\n') ? 0 : current_pos + 1; } } // 处理输入结束后剩余的空格 if (space_count > 0) { entab(current_pos, space_count); } return 0; } void entab(int current_pos, int space_count) { // 计算当前位置到下一个制表位的空格数 int space_to_next_tab = TAB - (current_pos % TAB); // 优先使用制表符,确保用最少字符 while (space_count >= space_to_next_tab) { putchar('\t'); space_count -= space_to_next_tab; space_to_next_tab = TAB; // 后续制表位间隔为完整的TAB } // 剩余空格用单个空格填充 for (int i = 0; i < space_count; i++) { putchar(' '); } }
修正要点
- 用
current_pos准确跟踪光标位置,从0开始计数,避免偏移。 - 正确计算制表符的等效空格数,即到下一个制表位的距离。
entab函数逻辑简化:优先输出制表符,剩余空格用单个空格填充,确保字符数量最少。- 调整位置计数时机,先处理字符再更新位置。
- 补充处理输入结束后剩余的空格,避免遗漏。
内容的提问来源于stack exchange,提问作者hansoko
相关产品推荐
相关产品推荐

