Home > front end >  How to accept string input only if it of certain length in C else ask user to input the string again
How to accept string input only if it of certain length in C else ask user to input the string again

Time:09-26

How to accept set of strings as input in C and prompt the user again to re-enter the string if it exceeds certain length. I tried as below

#include<stdio.h>
int main()
{
    char arr[10][25]; //maximum 10 strings can be taken as input of max length 25
    for(int i=0;i<10;i=i 1)
    {
        printf("Enter string %d:",i 1);
        fgets(arr[i],25,stdin);
    }
}

But here fgets accepts the strings greater than that length too. If the user hits return, the second string must be taken as input. I'm new to C

CodePudding user response:

How to accept string input only if it of certain length

Form a helper function to handle the various edge cases. Use fgets(), then drop the potential '\n' (which fgets() retains) and detect long inputs.

Some untested code to give OP an idea:

#include <assert.h>
#include <stdio.h>

// Pass in the max string _size_.
// Return NULL on end-of-file without input.
// Return NULL on input error.
// Otherwise return the buffer pointer.
char* getsizedline(size_t sz, char *buf, const char *reprompt) {
  assert(sz > 0 && sz <= INT_MAX && buf != NULL); // #1
  while (fgets(buf, (int) sz, stdin)) {
    size_t len = strlen(buf);
    // Lop off potential \n
    if (len > 0 && buf[--len] == '\n') {   // #2
      buf[len] = '\0';
      return buf;
    }
    // OK if next ends the line
    int ch = fgetc(stdin);
    if (ch == '\n' || feof(stdin)) {       // #3 
      return buf;
    }

    // Consume rest of line;
    while (ch != '\n' && ch != EOF) {      // #4
      ch = fgetc(stdin);
    }
    if (ch == EOF) {                       // #5
      return NULL;
    }

    if (reprompt) {
      fputs(reprompt, stdout);
    }
  }
  return NULL;
}

Uncommon: reading null characters remains a TBD issue.

Details for OP who is a learner.

  1. Some tests for sane input parameters. A size of zero does not allow for any input saved as a null character terminated string. Buffers could be larger than INT_MAX, but fgets() cannot directly handle that. Code could be amended to handle 0 and huge buffers, yet leave that for another day.

  2. fgets() does not always read a '\n'. The buffer might get full first or the last line before end-of-file might lack a '\n'. Uncommonly a null character might be read - even the first character hence the len > 0 test, rendering strlen() insufficient to determine length of characters read. Code would need significant changes to accommodate determining the size if null character input needs detailed support.

  3. If the prior fgets() filled its buffer and the next read character attempt resulted in an end-of-file or '\n', this test is true and is OK, so return success.

  4. If the prior fgetc() resulted in an input error, this loops exits immediately. Otherwise, we need to consume the rest of the line looking for a '\n' or EOF (which might be due to an end-of-file or input error.)

  5. If EOF returned (due to an end-of-file or input error), no reason to continue. Return NULL.

Usage

// fgets(arr[i],25,stdin);
if (getsizedline(arr[i], sizeof(arr[i]), "Too long, try again.\n") == NULL) {
  break;
}

CodePudding user response:

This code uses a buffer slightly larger than the required max length. If a text line and the newline can't be read into the buffer, it reads the rest of the line and discards it. If it can, it again discards if too long (or too short).

#include <stdio.h>
#include <string.h>
#include <stdbool.h>

#define INPUTS  10
#define STRMAX  25

int main(void) {
    char arr[INPUTS][STRMAX 1];
    char buf[STRMAX 4];
    for(int i = 0; i < INPUTS; i  ) {
        bool success = false;
        while(!success) {
            printf("Enter string %d: ", i   1);
            if(fgets(buf, sizeof buf, stdin) == NULL) {
                exit(1);                    // or sth better
            }

            size_t index = strcspn(buf, "\n");
            if(buf[index] == '\0') {        // no newline found
                // keep reading until end of line
                while(fgets(buf, sizeof buf, stdin) != NULL) {
                    if(strchr(buf, '\n') != NULL) {
                        break;
                    }
                }
                if(feof(stdin)) {
                    exit(1);                // or sth better
                }
                continue;
            }

            if(index < 1 || index > STRMAX) {
                continue;                   // string is empty or too long
            }

            buf[index] = '\0';              // truncate newline
            strcpy(arr[i], buf);            // keep this OK string
            success = true;
        }
    }
    
    printf("Results:\n");
    for(int i = 0; i < INPUTS; i  ) {
        printf("%s\n", arr[i]);
    }
    return 0;
}

CodePudding user response:

The nice thing about fgets() is that it will place the line-terminating newline character ('\n') in the input buffer. All you have to do is look for it. If it is there, you got an entire line of input. If not, there is more to read.

The strategy then, is:

fgets( s, size_of_s, stdin );
char * p = strpbrk( s, "\r\n" );
if (p)
{
  // end of line was found.
  *p = '\0';
  return s; (the complete line of input)
}

If p is NULL, then there is more work to do. Since you wish to simply ignore lines that are too long, that is the same as throwing away input. Do so with a simple loop:

int c;
do c = getchar(); while ((c != EOF) && (c != '\n'));

Streams are typically buffered behind the scenes, either by the C Library or by the OS (or both), but even if they aren’t this is not that much of an overhead. (Use a profiler before playing “I’m an optimizing compiler”. Don’t assume bad things about the C Library.)

Once you have tossed everything you didn’t want (to EOL), make sure your input isn’t at EOF and loop to ask the user to try again.

Putting it all together

char * prompt( const char * message, char * s, size_t n )
{
  while (!feof( stdin ))
  {
    // Ask for input
    printf( "%s", message );
    fflush( stdout );  // This line _may_ be necessary.

    // Attempt to get an entire line of input
    if (!fgets( s, n, stdin )) break;
    char * p = strpbrk( s, "\r\n" );

    // Success: return that line (sans newline character(s)) to the user
    if (p)
    {
      *p = '\0';
      return s;
    }

    // Failure: discard the remainder of the line before trying again
    int c;
    do c = getchar(); while ((c != EOF) && (c != '\n'));
  }

  // If we get this far it is because we have 
  // reached EOF or some other input error occurred.
  return NULL;
}

Now you can use this utility function easily enough:

char user_name[20];  // artificially small

if (!prompt( "What is your name (maximum 19 characters)? ", user_name, sizeof(user_name) )) 
{
  complain_and_quit(); 
  // ...because input is dead in a way you likely cannot fix.
  // Feel free to check ferror(stdin) and feof(stdin) for more info.
}

This little prompt function is just an example of the kinds of helper utility functions you can write. You can do things like have an additional prompt for when the user does not obey you:

What is your name? John Jacob Jingleheimer Schmidt
Alas, I am limited to 19 characters. Please try again:
What is your name? John Schmidt
Hello John Schmidt.

  • Related