C语言 K&R练习1-21 -心理不理解

relj7zay  于 5个月前  发布在  其他
关注(0)|答案(9)|浏览(67)

不可能的K&R练习。
“写一个程序entab,用最少的制表符和空格来替换空格字符串,以达到相同的间距。使用相同的制表位,比如每n列。n应该是一个变量还是一个符号参数?”
我遇到的问题是,我不确定如何正确地做到这一点。我知道这不是很好的解释,但这几乎是这里的问题。我见过的大多数例子都计算了一些空白,并将这些系列替换为制表符,但这不是它所要求的,我想我理解它的要求,但目前感觉无法做到这一点。
有人可以帮忙吗:)
编辑:到目前为止我写的代码是can be found here

6jjcrrmo

6jjcrrmo1#

如果你的问题是“这是要求我做什么?”我想我可以通过解释原始问题(以不同的方式提出相同的问题)来提供帮助。
写一个程序,输入带有空格的文本,输出尽可能使用制表符的视觉等效文本。
例如,制表符每隔8个字符,空格显示为“.”,制表符显示为“-”;

input;
".foo:...bar;......#comment"
output;
".foo:-bar;-..#comment"

input;
".......-foo:.....bar;......#comment"
output;
"-foo:-.bar;-...#comment"

字符串
编写程序,使tabstop参数n可以变化,即允许n的值不为8。准备好证明你的决定,使n为常数,或者变量。

  • 编辑 * 我看了你的代码,我认为它比它需要的更复杂。我的建议是一次一个字符。没有必要缓冲一整行。在读取每个字符时保持列计数。('\n'将其重置为零,'\t'将其增加1或更多,其他字符将其递增)。当您看到空格(或制表符)时,不要立即发出任何内容,开始你的entabbing过程,发出零个或多个制表符,然后空格(在'\n'或非空格字符,以先到者为准)。

最后一个提示是,状态机可以使这种算法更容易编写、验证、测试和读取。

  • 编辑2* 为了让OP接受我的答案,我现在已经开始自己编写解决方案,基于我上面提供的提示和我在讨论中的评论。
// K&R Exercise 1-21, entab program, for Stackoverflow.com
#include <stdio.h>
#define N 4     // Tabstop value. Todo, make this a variable, allow
                //  user to modify it using command line

int main()
{
    int col=0, base_col=0, entab=0;

    // Loop replacing spaces with tabs to the maximum extent
    int c=getchar();
    while( c != EOF )
    {

        // Normal state
        if( !entab )
        {

            // If whitespace goto entab state
            if( c==' ' || c=='\t' )
            {
                entab = 1;
                base_col = col;
            }

            // Else emit character
            else
                putchar(c);
        }

        // Entab state
        else
        {

            // Trim trailing whitespace
            if( c == '\n' )
            {
                entab = 0;
                putchar( '\n' );
            }

            // If not whitespace, exit entab state
            else if( c!=' ' && c!='\t' )
            {
                entab = 0;

                // Emit tabs to get close to current column position
                //  eg base_col=1, N=4, col=10
                //  base_col + 3 = 4 (1st time thru loop)
                //  base_col + 4 = 8 (2nd time thru loop)
                while( (base_col + (N-base_col%N)) <= col )
                {
                    base_col += (N-base_col%N);
                    putchar( '\t' );
                }

                // Emit spaces to close onto current column position
                // eg base_col=1, N=4, col=10
                //  base_col -> 8, and two tabs emitted above
                //  base_col + 1 = 9 (1st time thru this loop)
                //  base_col + 1 = 10 (2nd time thru this loop)
                while( (base_col + 1) <= col )
                {
                    base_col++;
                    putchar( ' ' );
                }

                // Emit buffered character after tabs and spaces
                putchar( c );
            }
        }

        // Update current column position for either state
        if( c == '\t' )
            col += (N - col%N); // eg col=1, N=4, col+=3
        else if( c == '\n' )
            col=0;
        else
            col++;

        // End loop
        c = getchar();
    }
    return 0;
}

b5buobof

b5buobof2#

我有点晚了,但这是我自己解决这个问题的方法。这是一个不同于上面分享的方法,所以如果你有任何意见/反馈,请分享。
查看my public gist on Github的源代码,这里有代码注解,文件顶部解释了方法,但我将复制并粘贴到这里,以便从一开始就清楚逻辑。
方法:

  • 我们将跟踪遇到的空格数(在非制表符/非空格字符之间)
  • 我们将跟踪每个输入行的字符(不是制表符/空格/换行符
  • 我们将通过以下方式来评估空格产生的“间隙”:
  • 计算这些字符之间的空格数。
  • 当空格数>= TABSIZE时,间隙将“足够大”
  • 然后,对于我们的“缓冲区”中所有剩余的空格,我们将单独打印出来

最后,我们打印出读入的字符(不是制表符/空格)
以及更新空间计数和字符计数(如有必要)。
我仍然是一个新手程序员在所有意义上,所以我不知道如何将比较与其他解决方案张贴在这里,但逻辑似乎更容易遵循(至少对我来说)。
希望这对以后的人有所帮助!

sg3maiej

sg3maiej3#

我同意你的评估。用制表符替换每n个空格是不够的;例如,如果n == 4,“hi blank blank blank”不应该被替换为“hi tab”,而是“hi tab blank blank”。
听起来你需要做的是在阅读每一行时跟踪当前位置,并使用这些信息来确定你需要多少个标签。这有帮助吗?如果你需要更多细节,请告诉我!
至于“变量与符号参数”部分,两者都是可行的,但我能想到使用变量的一个显著优点:你可以在不重新编译的情况下为不同的n值运行程序。

bjp0bcyl

bjp0bcyl4#

让我提供另一个至少处理提供顶级答案的情况。它只处理一个字符串。
假设列表是至少包含2个空格的事物。
1.让我们跟踪尾随空格的数量。
1.如果最后一个符号和当前符号都是空格,则可能有一个表格。
1.当尾随空格的数量等于制表停止时:将主计数器设置为counter -tablation stop - 1(就像我们使用数组一样),然后将位置分配给\t
1.将计数器变量移位后,其他符号将照常写入

/*
Write a program that takes as input text with spaces
and produces as output visually equivalent text using tabs to the maximum extent possible.

f.e. tab=4

input:
".foo:...bar;......#comment"
output:
".foo:-bar;-..#comment"

input:
".......-foo:.....bar;......#comment"
output:
"-foo:-.bar;-...#comment"
*/

#include <stdio.h>

#define MAXLINE 24
#define TABSTOP 4

int entab(char s[]);

int main() {
    char s[MAXLINE];

    entab(s);
    printf("%s\n", s);

    return 0;
}

int entab(char s[]) {
    int i;
    int j;
    char c;

    for (i = 0, j = 1; i < MAXLINE - 1 && ((c = getchar()) != '\n'); ++i) {
        // entab state
        if (i > 0 && s[i - 1] == ' ' && c == ' ') {
            ++j;
            if (j == TABSTOP) {
                i -= TABSTOP - 1;
                j = 1;
                s[i] = '\t';
            } else
                s[i] = c;
        }
        // Normal state
        else
            s[i] = c;
    }
    if (c == '\n') {
        s[i] = c;
        ++i;
    }
    s[i] = '\0';

    return 0;
}

字符串

olhwl3o2

olhwl3o25#

我的理解是,你不必真的知道问题是什么或者如何解决它来回答这个问题。这个问题似乎是在问你是否理解何时使用变量而不是“符号参数”。我实际上不确定“符号参数”是什么意思;它似乎是过时的术语。
话虽如此,解决问题的第一部分(用制表符代替空格)是相当直接的。

tuwxkamq

tuwxkamq6#

我粗略地看了一下你的代码,没有什么明显的错误。
因此,我的建议是,要么在调试器中单步调试几个输入示例,一边调试一边检查变量值,要么添加一大堆调试打印语句。无论哪种情况,您的目标都是找到程序状态开始偏离预期或意图的点。

m3eecexj

m3eecexj7#

我正在浏览KnR,看到了这个页面:
Answers to Exercises
您的练习位于以下位置:

  • 解决方案
  • 第1章第一次见面
  • Ex No 21 Pg No 34

users.powernet.co.uk/eton/kandr2/krx121.html
希望你觉得这有用。
真诚的,Morpfh
1http://users.powernet.co.uk/eton/kandr2/index.html“C编程语言”,第2版,Kernighan和里奇-练习答案

kpbpu008

kpbpu0088#

在上面的最佳答案中,程序过于复杂。为了简化这部分答案,我附上了一段简单得多的代码,希望是以K&R的风格编写的(主要是通过++内联递增)。

包含

define TAB 4

int main()函数{

char newsentence[255],c;
int spacecount = 0, oldsentencepointer = 0, newsentencepointer = 0;

printf("Give me a sentence please:\n");

while ((c = getchar()) != '\n') {
    if ((oldsentencepointer != 0) && (oldsentencepointer % TAB == 0) && (spacecount > 0))
       {
        newsentencepointer -= spacecount;         //if at tabstop, and spaces and not
                                                    first, go back to 1st space, set tab.
        newsentence[newsentencepointer++] = '\t';
        spacecount = 0;
        }

    if (c == ' ') {
        newsentence[newsentencepointer++] = ' ';
        spacecount++;                       //keep track of spaces before tab stop
    }

    else if (c == '\t') {
        newsentence[newsentencepointer++] = '\t' ;
        oldsentencepointer = TAB;   //set old pointer to TAB (does not matter if actual,
                                      only cadence important)
        continue;                   //continue from here so as not to increment 
                                      old sentence counter.
        }

    else {
        newsentence[newsentencepointer++] = c ;   //write whatever was old into new.
        spacecount = 0;                           //reset space counter.
        }

    oldsentencepointer++;

}

newsentence[newsentencepointer] = '\0';    //cap it off.

puts(newsentence);

return 0;

字符串
}

vptzau2j

vptzau2j9#

还有一个更简洁的解决方案,尽管它没有采用可用的最佳代码实践(滥用短路评估,通过continue进行笨拙的控制流,有点奇怪的“空间”循环)。

#include <stdio.h>

#define TS 8

int main(int arg, char *argv[]) {
    int counter = 0, space_counter = 0, c;
    while ((c = getchar()) != EOF) {
        ++counter;
        if (c == ' ' && ++space_counter && (counter % TS) == 0) {
            space_counter = 0;
            c = '\t';
        } else if (c == '\t') {
            counter = space_counter = 0;
        } else if (c != ' ') {
            while (space_counter--)
                putchar(' ');
            space_counter = 0;
            if (c == '\n')
                counter = 0;
        } else {
            continue; /* don't call putchar(c) */
        }
        putchar(c);
    }
    return 0;
}

字符串
除了空格之外,读取的每一个字符都被逐字打印。空格被计数。如果程序遇到一个非空格字符,它将打印之前计数过的空格,然后重置计数器。如果遇到一个空格,它将通过第二个计数器进行检查如果光标在制表位上,则打印(从行首/最后一个制表位开始的打印字符)。如果是,则打印制表符,否则仅计算空白。
输入中的一个制表符被处理为重置空格计数器并输出该制表符,从而消除该过程中的任何多余空格。

相关问题