3

I am trying a simple exercise from K&R to append string2 at the end of string1 using pointers. In case of overflow i.e. buffer of string1 can't contain all of string2 I want to prompt the user to re-enter string2 or exit.

I have written the following code:

#include<stdio.h>
#include<string.h>
#define MAXLINE 1000
int get_input(char *s);
int str_cat(char *s, char *t);

void main()
{
    char input1[MAXLINE], input2[MAXLINE], c; 
    get_input(input1);
    check:
    get_input(input2);
    if((strlen(input1) + strlen(input2) + 2) <= MAXLINE)
    {
        str_cat(input1, input2);
        printf("%s\n", input1);
    
    }
    else
    {
        input2[0] = '\0';
        printf("String overflow\n Press: \n 1: Re-enter string. \n 2: Exit.\n");
        scanf(" %d", &c);
        if(c == 1){
            input2[0] = '\0';
            get_input(input2);
            goto check;
        }

    }

}

int get_input(char *arr)
{
    int c;
    printf("Enter the string: \n");
    while(fgets(arr, MAXLINE, stdin))
    {
        break;
    }
}
int str_cat(char *s, char *t)
{
    while(*s != '\0')
    {
        s++;
    }
    while((*s++ = *t++) != '\0')
    {
        ;
    }
    *s = '\0';
}

Initially, I was using the standard getchar() function mentioned in the book to read the input in get_input() which looked like this:

int get_input(char *arr)
{
    int c;
    printf("Enter the string: \n");
    while((c = getchar()) != '\n' && c != EOF)
    {
        *arr++ = c;
    }
    *arr = '\0';
}
  • I am new and I read this and understood my mistake. I understand that one isn't supposed to use different input functions to read stdin and the '\n' is left in the input stream which is picked by the getchar() causing my condition to fail.

  • So, I decided to use fgets() to read the input and modified the scanf("%d", &c) as mentioned in the thread with scanf(" %d", c). This does work (kinda) but gives rise to behaviors that I do not want.

So, I have a few questions:

  1. What's a better way to fgets() from reading the input on encountering '\n' than the one I have used?

     while(fgets(arr, MAXLINE, stdin))
     {
         break;
     }
    
  2. fgets() stops reading the line and stores it as an input once it either encounters a '\n' or EOF. But, it ends up storing the '\n' at the end of the string. Is there a way to prevent this or do I have to over-write the '\n' manually?

  3. Even though I used the modified version of scanf(" %d", &c), my output looks like this: (https://i.stack.imgur.com/M5aX4.jpg). Despite that I get Enter the string: twice when prompted to re-enter the second string in case of an overflow situation. Is the modified scanf() messing with my input? And how do I correct it?

limbo1927
  • 55
  • 4
  • You don't need to add the space in `scanf(" %d", &c)` because the specifiers `%d` and `%f` and `%s` (and their variants) filter leading whitespace automatically. You *do* need the space with `%c` and `%[...]` and `%n`, because otherwise they read every character. Understanding how different formats handle whitespace is key to avoiding nasty kludges. – Weather Vane Jun 02 '22 at 14:34
  • As for the newline retained by `fgets` (if room in the buffer) please see [Removing trailing newline character from fgets() input](https://stackoverflow.com/questions/2693776/removing-trailing-newline-character-from-fgets-input/28462221#28462221) for a bullet proof one-liner. – Weather Vane Jun 02 '22 at 14:36
  • 2
    Welcome to StackOverflow. I noticed that you have put a lot of effort into making your first question a well-written one, and I just wanted to thank you for that. – Heinzi Jun 02 '22 at 14:37
  • If you might need to reject input (or there is a chance the user will type garbage) don't use `scanf`, get input into a string with `fgets` and parse it with `sscanf` or other. If you need to reject the input it is far, far simpler than with `scanf` possibly having its buffer blocked by garbage: you just input another string. – Weather Vane Jun 02 '22 at 14:41

1 Answers1

1

In general, do not mix fgets with scanf. Although it may be a bit bloaty, you will avoid many problems by being consistent with reading input with fgets and then parse it with sscanf. (Note the extra s)

A good way to remove the newline is buffer[strcspn(buffer, "\n")] = 0

Example:

// Read line and handle error if it occurs
if(!fgets(buffer, buffersize, stdin)) {
    // Handle error
}

// Remove newline (if you want, not necessarily something you need)
buffer[strcspn(buffer, "\n")] = 0;

// Parse and handle error
int val;
if(sscanf(buffer, "%d", &val) != 1) {
    // Handle error
}

// Now you can use the variable val

There is one thing here that might be dangerous in certain situations, and that is if buffer is not big enough to hold a complete line. fgets will not read more than buffersize characters. If the line is longer, the remaining part will be left in stdin.

klutt
  • 30,332
  • 17
  • 55
  • 95