简体   繁体   中英

How to test that an input string in C is of the correct 'format'?

I've written a simple calculator app in C and it works pretty well so far but I'm missing one thing: how to ensure the user has entered an input in the correct form eg "6 + 7". Is there any way I can test for that?

Here is my code so far:

/*A simple calculator that can add, subtract and multiple. TODO: Division
and add error handling i.e. if not an expected input!*/

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

int main() {
  char input[10];
  int result;
  printf("Welcome to the calculator! The available operations are +, - and *.\n");
  while (1) {
    printf("What would you like to calculate?\n");
    fgets(input, 10, stdin); /*Getting user input in form e.g. 4 + 7*/

    /*Ideally test for input should go here*/

    /*Separating user input into the numbers and operators, using strtok and
    space as a delimiter*/
    char *firstNumber = strtok(input, " ");
    char *operator = strtok(NULL, " ");
    char *secondNumber = strtok(NULL, " ");

    /*Converting the separated numbers to integers*/
    int firstInt = atoi(firstNumber);
    int secondInt = atoi(secondNumber);

    if (strcmp(operator, "+") == 0) {
      result = firstInt + secondInt;
    } else if (strcmp(operator, "-") == 0) {
      result = firstInt - secondInt;
    } else if (strcmp(operator, "*") == 0) {
      result = firstInt * secondInt;
    } else {
      printf("That ain't a valid operator sonny jim. Try again:\n");
      continue;
    }

    printf("Your result is %d.\n", result);

    int flag = 0;

    while (flag == 0) {
      printf("Would you like to do another calculation? (yes or no)\n");
      fgets(input, 10, stdin);
      if (strcmp(input, "yes\n") == 0) {
        flag = 1;
      } else if (strcmp(input, "no\n") == 0) {
        flag = 2;
      } else {
        printf("That isn't a valid response. Please respond yes or no.\n");
      }
    }
    if (flag == 2) {
      break;
    }
  }

  return 0;

}

Not necessarily the best way, but I would use sscanf this way:

    int firstInt;
    int secondInt;
    char operator;
    if (3 != sscanf(input, "%d %c %d %1[^\n]", &firstInt, &operator, &secondInt, &temp)) {
      printf("Invalid input Billy!\n");
      continue;
    }

sscanf should return 3 if it successfully read in the values from the input string. If it would return 4 that would mean it read some trailing non whitespace characters which is invalid. The added benefit of this approach is that you don't need to parse the operands elsewhere with atoi .

Whole code:

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

int main() {
  char input[10];
  char temp[2];
  int result;
  printf("Welcome to the calculator! The available operations are +, - and *.\n");
  while (1) {
    printf("What would you like to calculate?\n");
    fgets(input, 10, stdin); /*Getting user input in form e.g. 4 + 7*/

    /*Ideally test for input should go here*/
    int firstInt;
    int secondInt;
    char operator;
    if (3 != sscanf(input, "%d %c %d %1[^\n]", &firstInt, &operator, &secondInt, temp)) {
      printf("Invalid input Billy!\n");
      continue;
    }

    if ('+'== operator) {
      result = firstInt + secondInt;
    } else if ('-' == operator) {
      result = firstInt - secondInt;
    } else if ('*' == operator) {
      result = firstInt * secondInt;
    } else {
      printf("That ain't a valid operator sonny jim. Try again:\n");
      continue;
    }

    printf("Your result is %d.\n", result);

    int flag = 0;

    while (flag == 0) {
      printf("Would you like to do another calculation? (yes or no)\n");
      fgets(input, 10, stdin);
      if (strcmp(input, "yes\n") == 0) {
        flag = 1;
      } else if (strcmp(input, "no\n") == 0) {
        flag = 2;
      } else {
        printf("That isn't a valid response. Please respond yes or no.\n");
      }
    }
    if (flag == 2) {
      break;
    }
  }
  return 0;
}

The first test you should do is for length. You only allow input of ten characters. That's one for the operator, two for spaces, one for the \\n , and one for the NUL terminator. That leaves only 5 characters to be split amongst the two operands. So, if the user inputs 543 * 65 , you've already truncated the \\n . An input of 5432 * 65 , and you start losing important data. The way I would implement a length check is to search for the \\n :
if (input[0] && input[strlen(input) - 1] != '\\n') , you know the input has been truncated. Next, you need to check for the validity of the characters. If you keep your strtok() method, you can do input checking when you convert the string to an integer if you use the preferred function strtol() †† with far better error checking. As for the operator, you already have checks on that. For formatting of input: check for NULL pointers . If strtok() doesn't find a space delimiter, it will return a NULL pointer, which you would then try to read from later in your program.

†: I personally would make my input character limit larger: at least 25
††: man strtol for more information

string in C is of the correct 'format'?

A simpler approach uses " %n" at the end to record the offset of the scan - if it made it that far. Akin to @Chris Dodd comment.

int firstNumber;
char operator[2];
int secondNumber;

int n = 0;
sscanf(input, "%d %1[+-] %d %n", &firstNumber, operator, &secondNumber, &n);
//  v---v--------------------  Scan complete?
if (n > 0 && input[n] == '\0') Success();
//           ^--------------^  No extra junk at the end?
else Fail();

Detecting if a space exists is tricky. This answer and "%d %c %d" would pass `"5-7". If spaces are required about the operator, use

"%d%*[ ]%1[+-]*[ ]%d %n"

Notice the " " in " %n" allows scanning to be tolerant of a trailing '\\n' . Use as desired.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM