将空格转换为制表符

时间:2010-12-21 23:20:46

标签: c

我最近决定学习C,所以我开始学习K& R,但是我在第1章中遇到了问题21.你应该编写一个程序,它给出一个没有标签的字符串和一定的tabwidth,转换使用制表符和空格将所有空白区域转换为等效间距。

到目前为止,我已经有了这个:

void entab (char from[], char to[], int length, int tabwidth)
{
    int i, j, tabpos, flag, count;

    j = tabpos = flag = count = 0;
    for (i = 0; from[i] != '\0' && j < length - count - 2; i++) {
        if (from[i] == ' ') {
            // If you see a space, set flag to true and increment the
            // whitespace counter. Don't add any characters until you reach the
            // next tabstop.
            count++;
            tabpos = (tabpos + 1) % tabwidth;
            flag = 1;
            if (count >= tabwidth - tabpos) {
                to[j] = '\t';
                j++;
                count = count - tabwidth + tabpos;
                tabpos = 0;
            }
        } else {
            if (flag == 1) {
                // if you see something other than a space and flag is true,
                // there weren't enough spaces to reach a tabstop. Add count
                // spaces to the string.
                flag = 0;
                tabpos = (tabpos + count + 1) % tabwidth;
                while (count > 0) {
                    to[j] = ' ';
                    j++;
                    count--;
                }
            } else {
                tabpos = (tabpos + 1) % tabwidth;
            }
            count = 0;
            to[j] = from[i];
            j++;
        }
    }
    to[j] = '\0';
    return;
}
不幸的是,它似乎产生了比预期更大的间距。关于我搞砸的地方的任何想法?

PS我已经在线查看了其他解决方案,我知道有更好的解决方法,但我真的想修复我的错误。

编辑:设置tabwidth = 4并使用:

    foobar
        foo  bar      foo bar
    foo     bar

作为输入,我得到:

/t/tfoobar
/t/t/t/tfoo  bar/t/t/t foo bar
/t/tfoo/t/t bar

作为输出,而正确的输出将是:

/tfoobar
/t/tfoo/t bar/t/t foo bar
/tfoo/t/tbar

3 个答案:

答案 0 :(得分:4)

这个代码在单个函数中并且具有很多嵌套(四个级别深)很难正确,甚至更难维护。

我建议你首先重构你有更多可管理的部分。例如:

  • 创建一个名为findTab的函数,它接受要测试的字符串,制表符宽度(空格数)并返回第一次出现的制表符的索引。
  • 然后创建一个名为replaceChars的函数,它接受args的权限并执行替换

这些只是一些想法。您最终会得到的是一些更短,更易于管理的功能,并且很可能会发现您的错误!

顺便说一下,我试着快速搜索一些C重构文章,但是空手而归。目前,重构都是关于OOP语言,如Java,C#和C ++。不过,一些经验法则对你来说还有很长的路要走:

  • 最小化代码块的嵌套:if语句和for&amp; while循环
  • 最小化每个功能的行数(在合理范围内)保持少于60个顶部
  • (从OOP类中借用)确保每个函数都能很好地完成一个非常有限的事情
  • 更详细地命名你的变量和方法 - 当你这样做时看起来很痛苦,但它将来会像现在一样带来好处。

祝你好运,欢迎来到SO!

答案 1 :(得分:2)

if (count >= tabwidth - tabpos)

这会很快开始输出标签。考虑输入字符串:

"aa              " 

以8作为tabwidth。当tabpos变为5时,你得到角色5(我将是4,计数将是3),因此触发条件。您不希望在i变量​​达到tabwidth之前输出制表符。

我也不会修复你的代码。但是,我可以指出一个明显的错误,而不是像“重构代码”这样的一般性陈述,这只是21世纪的“重写代码”的方式。您不需要tabpos变量。只需使用(i%tabwidth)。从这里开始,事情应该开始落实到位。

答案 2 :(得分:0)

对您的代码非常怀疑的一件事是增加tabpos的方式。你为每个空间增加它(mod tabwidth两次。在递增count之后,再次递增flag == 1

在调试时,尤其是具有影响循环未来迭代的条件的代码时,最好考虑不变量。这些语句在执行特定代码行时应始终为true。

另一件事:给你的变量更好的名字。 flagcount特别糟糕,但tabpos也有点令人困惑。我的第一个想法是“哪个标签的位置?”但看起来你真的希望它是自上一个制表符停止后的字符单元格数(或者等效地,你在制表符列中的当前位置)。

无论如何,他们应该有更清晰的名字。如果这个概念太难以命名,那么这可能是您需要重新考虑算法的标志,或者您至少应该对变量进行评论。通常也很好地找出变量的不变量,比如“tabpos是自上一个制表符停止后的字符单元格数”(您的代码违反了== bug)。

最后,您可以尝试在调试器中单步执行代码,或者甚至使用printf调试来查看它在某些输入上行为异常的原因。