Kernighan and Ritchie C exercise 1-22 (fold long input lines)

Viewed 181

I am trying to do the exercise about the line folding in the K&R C-book, but I can't go much forward.

In particular, I do not knwo how to handle blank spaces inside the line i have to cut. The exercise is the following:

"Write a program to ''fold'' long input lines into two or more shorter lines after the last non-blank character that occurs before the n-th column of input. Make sure your program does something intelligent with very long lines, and if there are no blanks or tabs before the specified column."

The code i have implemented is the following:

#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;
  }
}

The code check the character in input while using a counter to check once it arrives to the line it needs to cut. For instance, for the line LoremIpsumissimplydummytex it will give LoremI,psumis,simply,dummyt,ext.

The condition if (c=='\n'{ i=0;} at the end of the main is used to reinitialize the counter i, used for the CUT, at every newline.

The problem is with the blank space handling: for instance i think that for the line Lorem Ipsum (with many blank spaces), the progam should be implemented in order to print Lorem,Ipsum. But I do not understand how to write the necessary code to implement this solution.

Sorry for the length of the post and thanks in advance.

EDIT:______________________________________________________

I added some conditions to the code in order to handle the multiple blank spaces. Wanted to ask if this could be considered the right solution:

#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++;
        }
    }
}

With this code, once reached the treshold CUT, if a normal character [non blank] is encounterd it is simply added to the new line to print, otherwise "\n" is printed and the counter is re-initialized to 1.

1 Answers

If you to split long lines into several lines, as stated in the task description, then you must print '\n' instead of ',' when encountering the cut treshold.

You should not print the character on the output stream until you know whether it should be printed on the current line or the next line. Therefore, you must store all characters until you know which line it must be printed on.

You may want to use the function isblank to determine whether a character is a space or a tab character.

In accordance with the community guidelines on homework questions, I will not provide a full solution to the problem at this time. However, if required, I will provide further hints.


UPDATE 1:

The code in your most recend edit is not correct. According to the task description, you are supposed to split the line at the last blank before the treshold, not at the first blank after the threshold.

Most of the time when you encounter a new character, you cannot know whether it belongs in the current line or in a new line. Therefore, as already stated in my answer, you must remember all characters until you know which line they belong on. You should not print a character before you know whether it belongs on the current line or the next line.

You may find it easier to solve this problem if you use fgets instead of getchar. However, the problem can also be solved with getchar.


UPDATE 2:

Since you appear to be struggling with the problem for several days, I am now providing my solutions to the problem:

Here is my solution which uses 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 );
}

Here is my solution which uses 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;
            }
        }
    }
}

It appears that my initial advice of using fgets instead of getchar was not very good, as the fgets solution turned out to be slightly more complicated than the getchar solution.

Here is some sample input and output of both programs (both programs behave the same way):

Sample input:

This is a line with several words.
Thisisalinewithonelongword.

Sample output:

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