6

“不可能”的 K&R 练习。

“编写一个程序entab,用最少数量的制表符和空格替换空格字符串以实现相同的间距。使用相同的制表位,例如每n列。n应该是变量还是符号参数?”

我遇到的问题是,我不确定如何正确地做到这一点。我知道这不是很容易解释,但这几乎是这里的问题。我见过的大多数示例都计算了一些空白,并用选项卡替换了这些系列,但这不是它的要求,我想我理解它的要求,但目前感觉无法做到这一点。

谁能帮忙:)

编辑:到目前为止我写的代码可以在这里找到

4

8 回答 8

15

如果您的问题是“这是要我做什么?” 我想我可以通过解释原始问题来提供帮助(以不同的方式提出相同的问题)。

编写一个程序,将带空格的文本作为输入文本,并尽可能使用制表符生成视觉上等效的文本作为输出。

例如,每 8 个字符使用制表位,并将空格显示为 '.' 和标签为'-';

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

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

编写程序,使制表符参数 n 可以变化,即允许 n 的值不为 8。准备好证明您决定将 na 设为常量或变量的合理性。

编辑我看了你的代码,我认为它比它需要的更复杂。我的建议是一次做一个角色。无需缓冲整行。在读取每个字符时保持列计数('\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;
}
于 2010-06-11T04:40:49.870 回答
2

我有点晚了,但这是我自己解决的方法。这是一种与上面分享的方法不同的方法,所以如果您有任何意见/反馈,请分享。

查看我在 Github上的公开 gist 以获取源代码。代码上有注释,方法在文件顶部进行了说明,但我将在此处复制并粘贴它,以便从一开始就清楚逻辑。

方法:

  • 我们将跟踪遇到的空格数(在非制表符/非空格字符之间)

  • 我们将跟踪每个输入行的字符(不是制表符/空格/换行符)

  • 我们将通过以下方式评估空间产生的“间隙”:

    • 评估这些字符之间是否有空格数。

    • 当空格数 >= TABSIZE 时,间隙将“足够大”

    • 然后,对于“缓冲区”中的所有剩余空间,我们将分别打印出来

最后,我们打印出读入的字符(不是制表符/空白)

以及必要时更新空间计数和字符计数。

我仍然是一个新手程序员,所以我不确定它与这里发布的其他解决方案相比如何,但逻辑似乎更容易理解(至少对我而言)。

希望这对以后的人有所帮助!

于 2018-02-14T22:37:22.873 回答
1

我同意你的评价。用制表符替换每 n 个空格是不够的;例如,如果 n == 4,则“hi blank blank blank blank”不应替换为“hi tab”,而应替换为“hi tab blank blank”。

听起来您需要做的是在每行阅读时跟踪当前位置,并使用此信息来确定您需要多少个选项卡。这有帮助吗?如果您需要更多详细信息,请告诉我!

至于“变量与符号参数”部分,两者都肯定是可行的,但我可以想到使用变量的一个显着优势:您可以针对不同的 n 值运行程序而无需重新编译。

于 2010-06-11T04:35:21.183 回答
0

我的理解是,为了回答这个问题,您实际上不必知道问题是什么或如何解决它。这个问题似乎在问您是否了解何时使用变量而不是“符号参数”。我实际上不确定“符号参数”是什么意思;它似乎是过时的命名法。

话虽如此,解决问题的第一部分(用制表符替换空格)相当简单。想想除法和余数。

于 2010-06-11T03:46:42.953 回答
0

我非常粗略地查看了您的代码,没有什么是公然错误的。

所以我的建议是要么在调试器中单步执行一些输入示例,一边检查变量值,要么添加一大堆调试打印语句。无论哪种情况,您的目标都是找到程序状态开始偏离您的预期或意图的点。

于 2010-06-11T04:26:41.377 回答
0

我目前正在耕种 KnR 并遇到了这个页面:

练习答案

您的练习位于:

希望你觉得这很有用。

真诚的, Morpfh

1 : http://users.powernet.co.uk/eton/kandr2/index.html “The C Programming Language”,第 2 版,Kernighan 和 Ritchie - 练习答案

于 2010-09-04T22:20:50.467 回答
0

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

包括

定义选项卡 4

诠释主要(){

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;

}

于 2014-11-11T13:42:51.583 回答
0

还有一个更简洁的解决方案,尽管它没有采用可用的最佳代码实践(滥用短路评估,通过 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;
}

除空格外,读取的每个字符都是逐字打印的。而是计算空白。如果程序遇到非空白字符,它会打印与之前计算的一样多的空白,然后重置该计数器。如果遇到空白,它会通过第二个计数器(从行首/最后一个制表位开始打印的字符)检查光标是否位于制表位上。如果是,则打印一个制表符,否则只计算空白。

输入中的制表符处理重置空格计数器和输出制表符,消除过程中任何多余的空白。

于 2016-11-13T03:29:56.430 回答