“不可能”的 K&R 练习。
“编写一个程序entab,用最少数量的制表符和空格替换空格字符串以实现相同的间距。使用相同的制表位,例如每n列。n应该是变量还是符号参数?”
我遇到的问题是,我不确定如何正确地做到这一点。我知道这不是很容易解释,但这几乎是这里的问题。我见过的大多数示例都计算了一些空白,并用选项卡替换了这些系列,但这不是它的要求,我想我理解它的要求,但目前感觉无法做到这一点。
谁能帮忙:)
编辑:到目前为止我写的代码可以在这里找到。
如果您的问题是“这是要我做什么?” 我想我可以通过解释原始问题来提供帮助(以不同的方式提出相同的问题)。
编写一个程序,将带空格的文本作为输入文本,并尽可能使用制表符生成视觉上等效的文本作为输出。
例如,每 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;
}
我有点晚了,但这是我自己解决的方法。这是一种与上面分享的方法不同的方法,所以如果您有任何意见/反馈,请分享。
查看我在 Github上的公开 gist 以获取源代码。代码上有注释,方法在文件顶部进行了说明,但我将在此处复制并粘贴它,以便从一开始就清楚逻辑。
方法:
我们将跟踪遇到的空格数(在非制表符/非空格字符之间)
我们将跟踪每个输入行的字符(不是制表符/空格/换行符)
我们将通过以下方式评估空间产生的“间隙”:
评估这些字符之间是否有空格数。
当空格数 >= TABSIZE 时,间隙将“足够大”
然后,对于“缓冲区”中的所有剩余空间,我们将分别打印出来
最后,我们打印出读入的字符(不是制表符/空白)
以及必要时更新空间计数和字符计数。
我仍然是一个新手程序员,所以我不确定它与这里发布的其他解决方案相比如何,但逻辑似乎更容易理解(至少对我而言)。
希望这对以后的人有所帮助!
我同意你的评价。用制表符替换每 n 个空格是不够的;例如,如果 n == 4,则“hi blank blank blank blank”不应替换为“hi tab”,而应替换为“hi tab blank blank”。
听起来您需要做的是在每行阅读时跟踪当前位置,并使用此信息来确定您需要多少个选项卡。这有帮助吗?如果您需要更多详细信息,请告诉我!
至于“变量与符号参数”部分,两者都肯定是可行的,但我可以想到使用变量的一个显着优势:您可以针对不同的 n 值运行程序而无需重新编译。
我的理解是,为了回答这个问题,您实际上不必知道问题是什么或如何解决它。这个问题似乎在问您是否了解何时使用变量而不是“符号参数”。我实际上不确定“符号参数”是什么意思;它似乎是过时的命名法。
话虽如此,解决问题的第一部分(用制表符替换空格)相当简单。想想除法和余数。
我非常粗略地查看了您的代码,没有什么是公然错误的。
所以我的建议是要么在调试器中单步执行一些输入示例,一边检查变量值,要么添加一大堆调试打印语句。无论哪种情况,您的目标都是找到程序状态开始偏离您的预期或意图的点。
我目前正在耕种 KnR 并遇到了这个页面:
您的练习位于:
解决方案
第 1 章 - 教程介绍
防爆 21 号 PG 34 号
希望你觉得这很有用。
真诚的, Morpfh
1 : http://users.powernet.co.uk/eton/kandr2/index.html “The C Programming Language”,第 2 版,Kernighan 和 Ritchie - 练习答案
在上面评分最高的答案中,该程序过于复杂。为了简化这部分答案,我附上了一个更简单的代码,希望以 K&R 的风格编写(主要是通过使用 ++ 递增内联)。
诠释主要(){
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;
}
还有一个更简洁的解决方案,尽管它没有采用可用的最佳代码实践(滥用短路评估,通过 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;
}
除空格外,读取的每个字符都是逐字打印的。而是计算空白。如果程序遇到非空白字符,它会打印与之前计算的一样多的空白,然后重置该计数器。如果遇到空白,它会通过第二个计数器(从行首/最后一个制表位开始打印的字符)检查光标是否位于制表位上。如果是,则打印一个制表符,否则只计算空白。
输入中的制表符处理重置空格计数器和输出制表符,消除过程中任何多余的空白。