【问题标题】:Kernighan and Ritchie C exercise 1-22 (fold long input lines)Kernighan 和 Ritchie C 练习 1-22(折叠长输入行)
【发布时间】:2021-10-11 09:58:33
【问题描述】:

我正在尝试做 K&R C-book 中关于折线的练习,但我无法继续前进。

特别是,我不知道如何处理我必须剪切的行内的空格。练习如下:

“编写一个程序,将长输入行“折叠”成两行或更多短行在输入的第 n 列之前出现的最后一个非空白字符之后。确保您的程序用很长的行做一些智能的事情,如果在指定的列之前没有空格或制表符。"

我实现的代码如下:

#include <stdio.h>

#define CUT 6

int main()

{
  int c, i = 0;

  while ((c = getchar()) != EOF) {

    if (i == CUT) {
      printf(",");
      putchar(c);
      i = 1;
    } else {
      putchar(c);
      i++;
    }
    if (c == '\n')
      i = 0;
  }
}

代码检查输入中的字符,同时使用计数器检查它是否到达需要剪切的行。例如,对于LoremIpsumissimplydummytex 行,它将给出LoremI,psumis,simply,dummyt,ext

main 末尾的条件 if (c=='\n'{ i=0;} 用于在每个换行符处重新初始化用于 CUT 的计数器 i。

问题在于空格处理:例如,我认为对于该行 Lorem Ipsum(有很多空格),应该执行程序以便打印Lorem,Ipsum。但是我不明白如何编写必要的代码来实现这个解决方案。

抱歉帖子太长,提前致谢。

编辑:______________________________________________

为了处理多个空格,我在代码中添加了一些条件。想问一下这是否可以被认为是正确的解决方案:

#include <stdio.h>

#define CUT 6


int main() 

{
    int c,i=0;
    
    while ((c=getchar()) !=EOF){
    
        if (i==CUT){
            if (c!=' '){            
            putchar(c);
                } else {                   
                printf("\n");
                i=1;}
        } else{
        putchar(c);
        i++;
        }
    }
}

使用此代码,一旦达到阈值 CUT,如果遇到普通字符 [非空白],则只需将其添加到新行进行打印,否则打印“\n”并将计数器重新初始化为 1 .

【问题讨论】:

  • 为什么每 6 个字符后插入一个,?这不是任务描述所说的......
  • 任务描述有些不清楚。我猜他们的意思是像自动换行。这意味着您应该在出现在空白之前的最后一个非空白字符之后拆分行。假设输入行 Lorem ipsum dolor sit amet, consectetur adipisici elitn=25 我希望输出的行不超过 25 个字符:Lorem ipsum dolor sitamet, consecteturadipisici elit。见stackoverflow.com/q/14797734/10622916
  • 在问题、格式和 MCVE 方面做得很好。

标签: c kernighan-and-ritchie


【解决方案1】:

如果您将长行拆分为多行,如任务描述中所述,则在遇到剪切阈值时必须打印'\n' 而不是','

在您知道该字符应该打印在当前行还是下一行之前,您不应该在输出流上打印该字符。因此,您必须存储所有字符,直到您知道它必须打印在哪一行。

您可能希望使用函数isblank 来确定字符是空格还是制表符。

按照community guidelines on homework questions,我暂时不会提供完整的问题解决方案。但是,如果需要,我会提供进一步的提示。


更新 1:

您最近一次编辑中的代码不正确。根据任务描述,您应该在阈值之前的最后一个空白处拆分行,而不是在阈值之后的第一个空白处。

大多数时候当你遇到一个新字符时,你无法知道它是属于当前行还是新行。因此,正如我在回答中已经说过的,您必须记住所有字符,直到您知道它们属于哪一行。在你知道它属于当前行还是下一行之前,你不应该打印一个字符。

如果您使用fgets 而不是getchar,您可能会发现更容易解决此问题。不过这个问题也可以用getchar解决。


更新 2:

由于您似乎已经为这个问题苦苦挣扎了好几天,我现在提供解决问题的方法:

这是我使用getchar的解决方案:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdbool.h>
#include <assert.h>

#define CUT 10

int main( void ) 
{
    //this memory buffer will hold the remembered
    //line contents
    char line[CUT+1];

    //current index
    int i = 0;

    //whether a blank character has been encountered in
    //the current line
    bool found_blank = false;

    //index of the last encountered blank character
    int blank_index;

    //this will store the current character
    int c;

    //this loop will read one character per loop iteration
    while ( (c=getchar()) != EOF )
    {
        //if we encounter the end of the line, flush the
        //buffer and start a new line
        if ( c == '\n' )
        {
            line[i] = '\0';
            puts( line );
            i = 0;
            found_blank = false;
            continue;
        }

        //check if new character is blank and, if it is, then
        //remember the index
        if ( isblank(c) )
        {
            blank_index = i;
            found_blank = true;
        }

        //write new character into array
        line[i] = c;

        if ( i == CUT - 1 )
        {
            if ( !found_blank )
            {
                //flush buffer
                line[CUT] = '\0';
                puts( line );

                //reset line
                i = 0;
                found_blank = false;
                continue;
            }
            else // found_blank == true
            {
                //remember character that is going to be overwritten
                char temp = line[blank_index+1];

                //output everything up to and including the last blank
                line[blank_index+1] = '\0';
                puts( line );

                //move unwritten characters to start of next line
                i = CUT - 1 - blank_index;
                line[0] = temp;
                memmove( line + 1, line+blank_index+2, i );

                found_blank = false;
                continue;
            }
        }

        i++;
    }

    //check stream for error
    if ( ferror(stdin) )
    {
        fprintf( stderr, "error reading input!\n" );
        exit( EXIT_FAILURE );
    }

    //if we reach this, then we must have encountered
    //end-of-file
    assert( feof( stdin ) );

    //flush buffer
    line[i] = '\0';
    puts( line );
}

这是我使用fgets的解决方案:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdbool.h>
#include <assert.h>

#define CUT 10

int main( void )
{
    char line[CUT+2];
    size_t leftovers = 0;

    //in every iteration of the loop, we write one line of output
    for (;;)
    {
        size_t blank_index;
        bool found_blank = false;

        if ( fgets( line + leftovers, sizeof line - leftovers, stdin ) == NULL )
        {
            //print all leftovers
            line[leftovers] = '\0';
            printf( "%s\n", line );
            break;
        }

        size_t len = strlen( line );

        //this check is probably not necessary
        assert( len != 0 );

        if ( line[len-1] == '\n' )
        {
            //we read a complete line, so print it
            printf( "%s", line );
            leftovers = 0;
            continue;
        }

        //if we reach this, then input line was not read in
        //completely, or we are dealing with the last line before
        //end-of-file or input failure

        //find last blank
        for ( size_t i = 0; line[i] != '\0'; i++ )
        {
            if ( isblank( (unsigned char)line[i] ) )
            {
                found_blank = true;
                blank_index = i;
            }
        }

        if ( !found_blank )
        {
            if ( len <= CUT )
            {
                if ( ferror(stdin) )
                {
                    fprintf( stderr, "error reading input!\n" );
                    exit( EXIT_FAILURE );
                }

                //we should only reach this if end-of-file was
                //encountered in the middle of a line
                assert( feof(stdin) );

                printf( "%s\n", line );
                break;
            }

            //remember last character
            char temp = line[len-1];

            line[len-1] = '\n';

            printf( "%s", line );

            line[0] = temp;
            leftovers = 1;
        }
        else
        {
            //remember character after last blank
            char temp = line[blank_index+1];

            //replace character after last blank with terminator
            line[blank_index+1] = '\0';

            //print line up to and including last blank
            printf( "%s\n", line );

            if ( temp != '\0' )
            {
                //move unprinted characters to start of next line
                line[0] = temp;
                leftovers = len - blank_index - 1;
                memmove( line + 1, line + blank_index + 2, leftovers - 1);
            }
            else
            {
                leftovers = 0;
            }
        }
    }
}

看来我最初使用fgets 而不是getchar 的建议不是很好,因为fgets 解决方案比getchar 解决方案稍微复杂一些。

这是两个程序的一些示例输入和输出(两个程序的行为方式相同):

示例输入:

This is a line with several words.
Thisisalinewithonelongword.

示例输出:

This is a 
line with 
several 
words.
Thisisalin
ewithonelo
ngword.

【讨论】:

  • 嗨,我在之前的代码中添加了一些内容。我想问你这是否可以成为解决问题的更好的实现方式。
  • @PwNzDust:我已经编辑了我的答案以评论您更新的代码。
  • @PwNzDust:如果在尝试了一段时间之后,您想看到答案,那么我愿意发布它。但是,如果您首先尝试自己,您将了解更多信息。
  • 谢谢,我明天会努力寻找解决方案,并在此处发布以检查是否可以视为有效
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多