2

Hi I'm having a really hard time reading user input in C a specific way...

I want to read a users input, in which case my program will verify the first three characters as characters and the last four digits as digits. And I don't know how to make sure that their input is 7 characters in total (3 chars, 4 ints) ex: ULI0788.

I don't want to use arrays, ex arr[12];

Currently I am at the point where I'm learning memory allocation & pointers, thus I am encouraged to use this rather than arrays if possible

for example

char itemID;
printf("Enter an ID of ITEM. (max 3 characters, 4 digits)");
scanf("%s", itemID);

I've done some googling and tried user suggestions but none do both of what I'm looking for, verifying each character/digit and setting a max of 7 total characters/digits. Or I just don't understand properly how to use the suggestions

googled
googled2
googled3
googled4
googled5
googled6
googled7
googled8
googled9
googled10

ryyker
  • 20,437
  • 3
  • 35
  • 78
  • Please [edit] your question and add links to the solutions you tried and explain why these solutions do not work for you. `char itemID` is a single character. You cannot store a string as you try to do with `scanf` format `%s`. You **need** an array to store a string, e.g. `char itemID[8]` for 7 characters and a terminating `\0`. Otherwise you could process and check the input character-by-character only. – Bodo Feb 22 '21 at 12:43
  • Read a string from the user (almost any method will do), *then* evaluate the string to see if it's the correct format. Don't try to, for example, devise a `scanf` format string that will read only strings of the desired format. You might be able to devise such a format, but (a) it will be much more work and (b) it will be much harder to arrange for a proper error message when the user types something that doesn't comply. – Steve Summit Feb 22 '21 at 12:54
  • 2
    Not sure what you mean by "not wanting to use arrays". Is this an artificial constraint imposed by your instructor? In the code fragment you posted, it looks like `itemID` is probably an array – Steve Summit Feb 22 '21 at 12:56
  • That is correct, this is a constraint I have to deal with. No arrays... Is this even possible ? If I could use arrays, I'm sure I would have come up with something since I have good understanding of them from java experience, but now I'm learning C and pointers aren't that difficult but things seem to be quite difficult using them in this situation – Quibble Coinbiter Feb 22 '21 at 13:08
  • 2
    [In `C`, the definition of a string is](https://www.tutorialspoint.com/cprogramming/c_strings.htm) _"a one-dimensional array of characters terminated by a null character '\0'."_. Saying you'ld like to use a non-array string is like asking for a glass of water, but make it not wet. – ryyker Feb 22 '21 at 13:15
  • Ok thanks for the info, there's no other choice then. – Quibble Coinbiter Feb 22 '21 at 13:18
  • @QuibbleCoinbiter *quite difficult using them in this situation* an array **is** a pointer, and a pointer **is** an array. Although you can manipulate characters by themselves without arrays, as in both answers below, you cannot dissociate the use of pointers and arrays, as those are both the same thing. – Adalcar Feb 22 '21 at 13:32
  • 1
    If you're not allowed to use an array, you could use a pointer and call `malloc`. Not sure whether that would or wouldn't violate the letter or the spirit of your artificial restriction. – Steve Summit Feb 22 '21 at 13:45
  • That wouldn't violate, as a matter of fact we are learning memory allocation and pointers at this point and that is why we are restrained from using arrays – Quibble Coinbiter Feb 22 '21 at 13:56
  • @SteveSummit I'm feeling rather dumb right now... haven't used malloc in ages so I'd completely forgotten about that – Adalcar Feb 22 '21 at 14:09
  • A pointer variable can be allocated memory to contain 7 `char`, eg `char *string = calloc(7, 1);`, or `calloc(8, 1);` if you want room for the `NULL` terminator.. Is this what you are after? – ryyker Feb 22 '21 at 14:13
  • Thank you my friend, I know the basics of calloc, malloc & unimportantly for this situation, free. I am just trying to figure out how to answer my question using memory allocation & maybe pointers ? – Quibble Coinbiter Feb 22 '21 at 14:21
  • See edit in answer below. – ryyker Feb 22 '21 at 14:43
  • 1
    @ryyker *Saying you'ld like to use a non-array string is like asking for a glass of water, but make it not wet.* That's just dehydrated water. Just add water. ;-) – Andrew Henle Feb 22 '21 at 14:46
  • 1
    @AndrewHenle - LOL, I was waiting for someone to comment on the, but thought it would be something like, _what about ice?_ – ryyker Feb 22 '21 at 14:49
  • 1
    @ryyker *Dry* ice, of course. :-D – Andrew Henle Feb 22 '21 at 14:54

2 Answers2

5

"I want to read a users input, in which case my program will verify the first three characters as characters and the last four digits as digits. And I don't know how to make sure that their input is 7 characters in total (3 chars, 4 ints)...I don't want to use arrays"

Without the ability to use C strings, the above is constrained to simply inputting a series of characters, then treating and testing them as discrete items:

bool test7char(void)
{
    int Char;

    for(int i=0;i<7;i++)
    {
        Char = getc(stdin);
        if(i<3)
        {
             if(!isalpha(Char)) return false;
        }
        else if(!isdigit(Char)) return false;
    }
    return true;
}

Usage:

int main(void)
{
    printf("Enter an ID of ITEM. (max 3 characters, 4 digits)");
    while(!test7char())
    {
        printf("Mistake - Re-enter an ID of ITEM. (max 3 characters, 4 digits)");
    }
    
    return 0;
}

EDIT - "I am just trying to figure out how to answer my question using memory allocation & maybe pointers"

Using pointer: (In memory, this pointer will point to a series of alphanumeric characters, terminated by \0, i.e. a string.)

#define STR_SIZE 7 + 1
BOOL test7char(const char *str);

int main(void)
{
    char *str = calloc(STR_SIZE, 1);
    if(str)
    {
        printf("Enter an ID of ITEM. (max 3 characters, 4 digits)");
        if(fgets(str, STR_SIZE, stdin))
        {
            while(!test7char(str))
            {
                printf("\nMistake - Re-enter an ID of ITEM. (max 3 characters, 4 digits)");
                if(!fgets(str, STR_SIZE, stdin))
                {
                    //message user of error
                    break;
                }
            }
        }
        free(str);
    }
    
    return 0;
}

bool test7char(const char *str)
{
    if(!str) return false;
    if(strlen(str) != STR_SIZE -1) return false; 

    for(int i=0;i<7;i++)
    {
        if(i<3)
        {
             if(!isalpha(str[i])) return false;
        }
        else if(!isdigit(str[i])) return false;
    }
    return true;
}

    
ryyker
  • 20,437
  • 3
  • 35
  • 78
4

I would advise you to use both fgets and sscanf:

  • fgets allows you to read a certain number of characters (which can be read from stdin).
  • sscanf allows you to read formatted input from a string (which you got from fgets).

By combining those, you can read 7 characters from standard input (8 if you add the \0) and then parse those to get the two values you're looking for.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
    // 8 chars long for string and terminating `\0`
    char *ID = calloc(8, 1);
    
    // extra char for same reason as above
    char *IDchar = calloc(4, 1);
    int IDnum, processed;
    
    // get the string and verify it's 7 characters long
    if (fgets(ID, 8, stdin) && strlen(ID) == 7) 
        sscanf(ID, "%3s%4d%n", IDchar, &IDnum, &processed);
    if (processed == 7)
        printf("success");
    else
        printf("failure");
}

The %n will collect the number of characters processed by the sscanf, ensuring you parsed the right number of characters. note that this is a VERY dangerous parameter, and you should always verify your input length before using it.

Edit:

If you do not want to use arrays at all, and only want to verify the input format without storing or reusing it, you can use getc to read the characters one at a time and verify their value:

#include <stdio.h>
#include <ctype.h>
int isEnd(int c)
{
    return c == '\n' || c == EOF;
}
void main()
{
    int tmp;
    int valid = 1;
    //check the first 3 characters
    for(int v = 0; v < 3 && valid; v++)
    {
        // read a char on standard input
        tmp = fgetc(stdin);
        // check if tmp is a letter
        valid = islower(tmp) || isupper(tmp);
    }
    //check the next 4 characters
    for(int v = 0; v < 4 && valid; v++)
    {
        // read a char on standard input
        tmp = fgetc(stdin);
        // check if tmp is a numeral
        valid = isdigit(tmp);
    }
    if (valid)
    {
        printf("format OK\n");
        // Check that the input is finished (only if format is OK)
        tmp = fgetc(stdin);
        if (isEnd(tmp))
            printf("length OK\n");
        else
            printf("length KO: %d\n", tmp);
    }
    else
    {
        printf("format KO\n");
    }
}

As I said before, this will only check the validity of the input, not store it or allow you to reuse it. But it does not use arrays.

Edit 2:

Another thing to watch out for with fgetc or getc is that, though it will manage longer inputs properly, it will get stuck waiting for the chars to be provided if there aren't enough. Thus make sure to exit the moment you read an incorrect character (if it's an end-of-line char, there won't be any more coming)

Edit 3:

Of course I'd forgotten malloc. Edited the first answer.

Adalcar
  • 1,430
  • 8
  • 24
  • 1
    Have you tried compiling your code? Also, the OP is NOT interested in using arrays. – Rohan Bari Feb 22 '21 at 12:57
  • @RohanBari sorry, missed a few typos. And yes, but pray tell me, how do you manage strings without using arrays? – Adalcar Feb 22 '21 at 13:00
  • That's the misconception of the OP about storing some string constant without a char-array. It should be mentioned in the answer. However, not introducing the subscript operator, only using the string manipulating functions and memory allocation will suffice. – Rohan Bari Feb 22 '21 at 13:03
  • @RohanBari I assumed he did not want to mess with arrays themselves (as in accessing specific chars within the array) and wanted to use functions to do that for him. Any other interpretation makes the question nonsensical – Adalcar Feb 22 '21 at 13:04
  • This is excellent and in line with what I've found searching through google, but has both solutions to my problem... However this uses arrays which I don't want Can pointers not be used instead ? – Quibble Coinbiter Feb 22 '21 at 13:04
  • @QuibbleCoinbiter As mentioned by Rohan above, if your problem is that you do not want to use arrays, you might want to use C++, where ther is an actual `string` type, instead of C, where any string treatment is done using arrays. – Adalcar Feb 22 '21 at 13:07
  • I see, unfortunately I am learning C right now, so any other language is out of the questions for this tasks. I suppose this will do, maybe my instructor was wrong. I said unfortunately, but actually fortunately I am using C, I am glad to be learning more languages, especially this one – Quibble Coinbiter Feb 22 '21 at 13:10
  • 1
    @QuibbleCoinbiter I have added a potential solution. It has no practical use but it solves your problem – Adalcar Feb 22 '21 at 13:27
  • Hi, thank you for all the help, I appreciate it. I will go through this code over the course of the current and following weeks. Input validation is difficult for me under this circumstance (no arrays) and Input validation is really important, so this will be helpful forever. – Quibble Coinbiter Feb 22 '21 at 14:33
  • 1
    @QuibbleCoinbiter Just check the last edit because the code was not working (swapped first and second inputs for sscanf). Also remember that the first one will not verify whether the first 3 characters are letters or numbers or anything, it will just read them – Adalcar Feb 22 '21 at 14:38
  • 2
    1. `fgetc()` returns `int`, not `char`. You can not reliably check for `EOF` if you cram the returned value into a `char`. 2. `isUpperCaseLetter()`? What are you recreating standard C functions such as `isupper()` and `isdigit()`? – Andrew Henle Feb 22 '21 at 14:44
  • @AndrewHenle 1: Thanks, fixed it 2: I had no idea those even existed... – Adalcar Feb 22 '21 at 14:51
  • 2
    @QuibbleCoinbiter *Input validation is really important, so this will be helpful forever.* Well, yes and no. There's nothing wrong with Adalcar's answer *as an answer to a learning exercise with an artificial constraint*. But for real programs, there are much, *much* better ways of doing input validation than any member of the `scanf` family. (In particular, in a real program, you would probably want to use a real regular-expression parser, not the limited, pseudo-regular-expression handling of `scanf` or `sscanf`.) – Steve Summit Feb 22 '21 at 14:53