K&R练习1-20 编程语言第2版

3
这个程序该怎么做?我不理解。
问题是:编写一个程序detab,将输入中的制表符替换为适当数量的空格以跳转到下一个制表符停止位。假设有一组固定的制表符停止位,例如每n列。n应该是变量还是符号参数?
我首先用空格(' ')替换了制表符('\t')。但我想这是错误的方法。
请提供建议?
顺便问一下,n应该是变量还是符号参数?
到目前为止的代码:
#include<stdio.h>
#define TAB 5
int main() {
    int i, c;
    while((c = getchar()) != EOF) {
        if(c == '\t') {
            for(i = 0; i < TAB; ++i)
                putchar(' ');
        } else
            putchar(c);
    }
    return 0;
}

在这个练习中发布的所有问题中,我都无法理解意思。

这是我的最终代码,请告诉我它是否有任何问题/错误。我认为它正在按照应该的方式工作。
感谢@Nit,@Chrono Kitsune,@dasblinkenlight和所有其他帮助过我的人们。
#include<stdio.h>
#define TAB 8
int main() {
int c, count = 0, space = 0;
while((c = getchar()) != EOF) {

    if(c == '\t') {
        space = (TAB - (count % TAB));
        while(space > 0){
            putchar(' ');
            count++;
            space--;
        }
    }
    else{
        putchar(c);
        ++count;
    }

    if(c == '\n')
        count = 0;
}
return 0;
}

1
提示:计算字符数。 - Weather Vane
5
顺便说一句,我觉得很有意思的是,在这个网站上,尽管有很多的踩,但只要你使用 K&R(指书籍"The C Programming Language"作者Kernighan和Ritchie的缩写),不需要做任何研究,不解释问题,也不提供输入输出示例,就可以得到赞同票。真是个笑话。 - ChiefTwoPencils
1
你的错误在于假设只有当你在制表位时才会得到一个制表符\t。制表符并不意味着恰好是x个空格,而是1到x个空格,因此我们现在处于下一个制表位,这些制表位位于每行开头的n*x位置。 - Deduplicator
4
把终端/控制台看作是由行和列组成的。假设你有80列(0-79),每8列(0、8、16、24、32、40、48、56、64、72)有一个特殊位置叫做“制表符停止位”。如果你遇到了\t,它应该移到下一个制表符停止位。基本上,你要通过输出移动到下一个制表符停止位所需的空格数来复制相同的效果。 - user539810
2
“hi \tcrazy\t world” 应该打印“hi”,将您放在第三列。 然后打印5个空格作为制表符(TAB - column%TAB),以移动到第8列,这是下一个制表符停止位置。 然后打印“crazy”,使您处于第13列。 另一个制表符将您移动到第16列,因此“8-13%8 = 8-5 = 3个空格”。 最后打印“world”。 如果您使用Linux或OS X,则可以在命令行中使用“printf”实用程序来了解它:“printf'hi \ tcrazy \ t world \ n hi crazy world \n”的输出。 (我的空格数量可能有点不正确,但您可以像我说的那样自己尝试)。 - user539810
显示剩余8条评论
5个回答

12
你现在做的不是练习要求你做的事情:你应该根据已经在该行中打印出的字符数插入不同数量的空格,而不是为每个制表符插入固定数量的空格。
无论你如何获取每个制表符的空格数量 - 将其设置为预处理器常量的方式是完全可以的。然而,你的程序需要计算出已经打印的“普通”字符数,并且在看到 '\t' 时计算需要多少个空格。
创建一个变量count来记录已经打印的字符数。将它初始化为零,并在每次看到'\n' 字符时将其重置为零。当调用putchar时,也使count++
现在,当你看到一个制表符'\t'时,计算一下距离下一个制表位还有多远。此表达式为:
TAB - (count % TAB)

这是需要打印的空格数目。

这应该足够让你回去修复你的程序了 - 我认为你只需要写五行额外的代码(不包括需要插入的花括号行)就能完成这个练习。


什么是制表位? - Abhishek
1
再次强调,只需要花几秒钟在谷歌上搜索一下就可以知道什么是制表位。 - Weather Vane
5
这与早期计算机和打字机上 TAB 键的工作方式有关。当您按下 TAB 键时,光标会移动到下一个所谓的制表位位置,这可以像这个练习中一样设置为等间隔,也可以在特定的位置设置。假设制表位设置为每 5 个空格间隔相等。当您按下 TAB 键时,光标应该移动到下一个可被 5 整除的位置。如果您处于位置 0,则光标将移到位置 5。如果您在位置 7,则光标将移到位置 10,以此类推。 - Sergey Kalinichenko
我需要做的是将空格和制表符替换为固定长度为5个字符的空格?例如,将_____ <tab> <space> <space> _____ <space> <tab> 替换为 <space - 5> ____ <space - 5> _____,其中_是字符。 - Abhishek
1
@Abhishek 不,用固定的5个字符空格替换是你现在程序所做的。相反,它应该计算到下一个可被五整除的位置缺少多少个空格(查看答案中的公式),并打印出这么多的空格。 - Sergey Kalinichenko
2
我认为制表键仍然是这样工作的。例如,使用我使用的 MS Word 版本 14.0,在 Word 诞生 21 年后,按制表键会将书写标记移动到一系列均匀间隔位置中最近的位置。制表符曾经(可能现在仍然)用于制表数据或表格,因此得名,用于打字机。许多人喜欢使用它缩进源代码,因为与使用空格相比,它可以节省几个按键。随着更智能的编辑器为您缩进,这种选择现在几乎已经不重要了。哦,对了,“真正”的制表符大小是 8。 - Peter - Reinstate Monica

4
第一步是理解问题。一开始,我反复阅读了几遍,但仍然不清楚它要我做什么。这是因为有些概念对我来说不太清晰或陌生,所以在搜索更多关于tab的信息之前,需要先了解一下什么是tab以及tab stop。Tab在许多上下文中用转义序列\t表示的字符。就像其他字符(如字母或数字)一样,它也是一个字符,但具有特殊的用法,因此它不是看起来像一个宽空格或4个或8个空格。看起来像一个宽空格或4个或8个空格只是它的设计目的,它将每个以制表符分隔的文本组在多行上对齐,使区域看起来像一个表格,但在软件层面下方,它只是一个字符。 Tab stop是当按下Tab键时光标所到达的线上位置。这些位置根据Tab字符显示的宽度或字符数(或列,所有这些都指同一个概念)固定在该线上。例如,在Windows记事本中,Tab的默认宽度为8个字符,当您输入Tab键时,光标会移动到第8、16、24...个字符后面。您可以在第一行输入0以清楚地看到其效果:
00000000000000000000000000000000
    Ivan    Hello   World
This    is  a   table
delimited   by  tab

现在重新阅读这个问题,对我来说很清楚,它是关于替换Tab字符为空格,同时保持原始表格的外观。然后你可以开始编写代码来计算每个Tab所需的空格数。以下是我完成这个练习的完整代码:

#include <stdio.h>

#define MAX_LENGTH 1000
#define LINE_NUM 100
#define TAB_WIDTH 8

int readLine(char line[], int maxLength);
void copy(char from[], char to[]);
void detab(char line[], char result[]);

main() {
    printf("Input: \n");
    char lines[LINE_NUM][MAX_LENGTH];
    char line[MAX_LENGTH];
    char result[MAX_LENGTH];
    int lineId = 0, length = 0;
    while ((length = readLine(line, MAX_LENGTH)) != 0) {
        detab(line, result);
        copy(result, lines[lineId]);
        lineId++;
    }
    
    printf("Output: \n");
    for (int i = 0; i <= lineId; i++) {
        printf("%s\n", lines[i]);
    }
}

int readLine(char line[], int maxLength) {
    char ch;
    int length = 0;
    while ((ch = getchar()) != EOF && ch != '\n' && length < maxLength) {
        line[length] = ch;
        length++;
    }
    if (ch == '\n') {
        line[length] = '\0';
    }
    return length;
}

void copy(char from[], char to[]) {
    int i = 0;
    while (from[i] != '\0') {
        to[i] = from[i];
        i++;
    }
    to[i] = '\0';
}

void detab(char line[], char result[]) {
    int i = 0;
    char ch;
    int column = 0;
    int spaces;
    int nextTabStop;
    while ((ch = line[i++]) != '\0') {
        if (ch == '\t') {
            spaces = TAB_WIDTH - column % TAB_WIDTH;
            nextTabStop = column + spaces;
            for (; column < nextTabStop; column++) {
                result[column] = ' ';
            }
        } else {
            result[column] = ch;
            column++;
        }
    }
    result[column] = '\0';
}

3

首先,尝试熟悉'\t'(制表符),并查看在打印时会发生什么。

'\t' + ','
'.' + '\t' + ','
'..' + '\t' + ','

等等。您会发现,在初始的一定数量的“.”中,“\t”后面的“,”字符处于相同的位置,这意味着“\t”的长度不固定,因此,如果您尝试用固定数量的“ ”字符(空格)替换它,则输出将与输入不同。

了解这一点后,您的任务是创建一个程序,将所有“\t”字符替换为空格,因此您必须计算每个读取的“\t”字符所需的空格数。这是我到目前为止所做的。

#include <stdio.h>
#define WSPT 8 // white spaces per tab

main () {
    int c, counter;
    counter = 0; // distance from the previous tab stop
    while((c = getchar()) != EOF) {
        if(c == '\t') {
            for(int i = 0; i < WSPT - counter; ++i) 
                putchar(' '); // print white spaces until reach the next tab stop
            counter = 0; // you are again at the start of a tab stop
        } else {
            putchar(c); 
            if(c != '\n')
                counter = (counter + 1) % WSPT; // move 1 step
            else
                counter = 0; // you are again at the start of a tab stop
        }
    }
}

-1

好的,实际上这是一个非常简单的问题,我不确定为什么大家把它弄得比实际复杂。我们只需要用必要数量的空格替换制表符,这样输出结果看起来与有制表符的情况没有任何区别。

没有必要写上百行代码...几行就足够了...

#include <stdio.h>
/* A program *detab* that replaces tabs in the input with the proper number of blanks to space to the next tab stop. Assuming a fixed set of tabstops, say every n columns */
#define TAB_WIDTH 4         // tab width on particular machine

int main()
{
int c;
int i = 0;
while((c = getchar()) != EOF) {
  if(c != '\t')
    putchar(c);
  else
    while(i < TAB_WIDTH - 1){
      putchar(' ');
      i++;
    }
  }
}


2
如果制表符在行的开头,这个方法是有效的。但是,如果它们跟随一些普通字符,那么您还需要考虑在上一个制表符停止位置之后打印的字符。 (简单示例:在像“ab<tab>”这样的行中,制表符应替换为TAB_WIDTH-2个空格,而不是TAB_WIDTH个空格。) - tzp

-1

为什么要这么复杂的东西..只需要这样做

#include <stdio.h>

int main()
{
        int c, tab=6, count=0;
        while((c=getchar())!=EOF)
        {
                count++;
                if (c=='\t')
                {
                        for (int i=count; i%(tab+1)!=0; i++) putchar(' ');
                }
                else putchar(c);
                if(c=='\n') count=0;
        }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接