I'm trying to write a function that checks input. I've seen some code like this but they didn't get the input until it got int. My function should get the input and check it. It should be used like "check(input)";
Here is something I tried and failed;
char n[10];
fgets(n, sizeof(n), stdin);
strtol(n, NULL, 10);
int i;
for (i = 0; i < strlen(n); i++) {
while(! isdigit(n[i])) {
if (n[i] != '.') {
printf("Wrong! Enter again:");
fgets(n, sizeof(n), stdin);
}
}
}
return buf;
If you simply want to make sure that the input is a valid floating point number, strtod is your friend:
#include <stdlib.h>
#include <errno.h>
double check(const char input[])
{
char* p;
errno = 0;
double n = strtod(input, &p);
if (errno == ERANGE)
;//range error, the input doesn't fit on the type
if (p == input)
;//fail, not a number
return n;
}
There are some cases which pass through this test, but you may not want to, such as nan, or hexadecimal notation, so you need to take care of that.
Thank you so much!
I added a little bit to your solution. Please let me know if there is anything unnecessary or useless.
double check(const char input[]) {
char *p;
errno = 0;
double n = strtod(input, &p);
if (errno == ERANGE) {
//range error, the input doesn't fit on the type
return 0;
} else if (p == input) {
//fail, not a number
return 0;
} else if (*p && *p != '\n' && *p != '.' && *p != ',') {
// *endptr is neither end of string nor newline,
// so we didn't convert the *whole* input
return 0;
}
return n;
}
Related
Is there more elegant way to do this task?
Program asks user for integer and repeats if non-digital characters are entered.
To exit loop two conditions expected:
a) all entered characters are digits
b) last character is '\n'
Short solutions like scanf don’t work properly, other approaches require lots of variables loops and if else conditions. User input is common task and I would like to have proper reusable template.
Subjective opinions are appreciated. Way to simplify this function or advice on another solution. Improve formatting. Reading for more systematic understanding.
#include <stdio.h>
int getIntOnly();
int main() {
int x = 0;
x = getIntOnly();
printf("\nvalue entered is: %d \n", x);
}
int getIntOnly() {
int ch, num, quit, abc;
do {
num = 0;
ch = 0;
quit = 0;
abc = 0;
printf("Enter the input: ");
do {
ch = getchar();
if (ch >= 48 && ch <= 57) {
num = num * 10 + (ch - 48);
}
else if (ch == '\n') {
quit = 1;
}
else {
abc = 1;
}
}
while (quit == 0);
}
while (quit == 0 || abc == 1);
return (num);
}
Using fgets() means you'll get the full text at once.
You can then examine it (and convert it too) to suit your needs.
int getIntOnly( void ) {
int value = 0, i = 0;
char buf[ 64 ];
do {
printf( "Enter integer value: " );
fgets( buf, sizeof( buf ), stdin );
value = 0;
for( i = 0; '0' <= buf[i] && buf[i] <= '9'; i++ )
value = value * 10 + buf[i] - '0';
} while( buf[i] != '\n' ); // good! reached end of line
return value;
}
May be better? Add some validity checks for the result of fgets() and strtol() according to your original code.
#include <stdio.h>
#include <limits.h>
#include <stdbool.h>
#include <stdlib.h>
int getIntOnly();
int main() {
int x = 0;
x = getIntOnly();
printf("\nvalue entered is: %d \n", x);
}
bool isDigit(char ch) {
return (ch >= '0' && ch <= '9')? true : false;
}
bool isAllDigit(char *buf) {
int i;
for (i = 0; buf[i] != '\n'; i++) {
if (isDigit(buf[i]) == false) {
return false;
}
}
return true;
}
bool isVaildInt(long int number) {
return (number >= INT_MIN && number <= INT_MAX)? true : false;
}
int getIntOnly() {
char buf[100];
long int num;
bool done = false;
do {
/* read line-by-line */
fgets(buf, 100, stdin);
if (isAllDigit(buf) == false)
continue;
num = strtol(buf, NULL, 10);
/* strtol() returns long int */
if (isVaildInt(num) == false)
continue;
done = true;
} while (done == false);
return num;
}
As stated above, I would like to make a function that checks if all the characters in a string contains any prohibited input. The condition is that I only want to accept alphabets, hyphens and apostrophes. Below is my code which does not work the way I intended it to. If it is not an alphabet AND not an apostrophe or a hyphen, I want to change result to 0. However, when I enter a valid input like 'a-a; which is either an alphabet or hyphen, the if function still gets executed which prints "IT IS NOT ACCEPTED".
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int validateInput(char word[]);
int main(void) {
char word[33] = "a-a";
printf("%d",validateInput(word));
}
int validateInput(char word[]) {
int result = 1;
int i;
int length = strlen(word);
for (i = 0; i <length; i++) {
if ((isalpha(word[i]) == 0) && ((word[i] != '-') || (word[i] != '\''))) {
printf("IT IS NOT ACCEPTED\n");
result = 0;
}
else {
printf("ACCEPTED\n");
}
}
return result;
}
There are multiple problems in your code:
you issue the diagnostic at each iteration instead of at the end of the loop
the test (word[i] != '-') || (word[i] != '\'') is always true.
isalpha() should not be passed a char value that could be negative. You should cast the argument as (unsigned char) to avoid potential undefined behavior.
Here is a modified version:
#include <ctype.h>
int validateInput(const char *word) {
int result = 1;
for (size_t i = 0; word[i] != '\0'; i++) {
if (!isalpha((unsigned char)word[i]) && word[i] != '-' && word[i] != '\'') {
result = 0;
break;
}
}
if (result) {
printf("ACCEPTED\n");
} else {
printf("IT IS NOT ACCEPTED\n");
}
return result;
}
Note however that the above function will accept an empty string, which might not be the intended behavior.
Here is a simpler version using sscanf() that works for ASCII:
#include <stdio.h>
int validateInput(const char *word) {
int pos = 0;
sscanf(word, "%*[-a-zA-Z']%n", &pos);
if (pos > 0 && word[pos] == '\0') {
printf("ACCEPTED\n");
return 1;
} else {
printf("IT IS NOT ACCEPTED\n");
return 0;
}
}
And this is a more verbose version using strspn() that works for all encodings:
#include <string.h>
int validateInput(const char *word) {
size_t len = strspn(word, "'-ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
if (len > 0 && word[len] == '\0') {
printf("ACCEPTED\n");
return 1;
} else {
printf("IT IS NOT ACCEPTED\n");
return 0;
}
}
Try:
if( !( (isalpha((unsigned char)word[i])) || (word[i] == '-') || (word[i] == '\'')) )
I want to write a program that takes numbers as inputs over multiple lines that are identified/separated by let's say ; character and print out their sum(s). Example:
1 2 3; 4 5 6; 7 8 9;(enter)
10 11 12;(enter)
exit(enter)
And I want the expected output to be exactly like:
List 1: 6 (sum of 1 2 3)
List 2: 15 (sum of 4 5 6)
List 3: 24 (sum of 7 8 9)
List 4: 33 (sum of 10 11 12)
sum of a b c, printing out this is not necessary, but their result as number is (enter), i.e. I'm pressing enter/getting to new line.
I am terminating when user types exit. But I am getting segmentation fault error in my code. Plus in this code the sum is also getting wrong values (I tried it separately).
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main() {
char *b;
int sum = 0;
int rc;
int i = 1;
while (strcasecmp(b, "exit") != 0) {
char buff[50];
rc = read(0, buff, 50);
if (rc == -1) {
perror("");
exit(0);
}
char *a = buff;
b = strtok(a, "\n");
char *c = strtok(b, ";");
while (c != NULL) {
char *d = strtok(c, " ");
while (d != NULL) {
int a = atoi(d);
sum += a;
d = strtok(NULL, " ");
printf("List %d: %d", i, sum);
i++;
}
c = strtok(NULL, ";");
}
}
}
You can use getchar and parse the integers on the go as below, without strtok.
int main() {
int sum = 0; int rc; int i = 0, j = 0;
char buff[50] = "";
while(1) {
if (i>= sizeof buff) break; //not enough memory
if (read(STDIN_FILENO, &buff[i], 1) < 1) {break;} //read error
if (strcasecmp(buff, "exit") == 0) break;
else if (buff[i] == ';'){
buff[i] = '\0';
int a = atoi(buff);
sum += a;
printf("sum = %d\n", sum);
sum = 0;
i = 0;
memset(buff, 0 , sizeof buff);
}
else if (buff[i] == ' '){
buff[i] = '\0';
int a = atoi(buff);
sum += a;
i = 0;
}
else if (buff[i] != '\n'){
i++;
}
}
}
There are multiple problems in your code:
b is an uninitialized pointer, reading and writing through it have undefined behavior, most likely the cause of the segmentation fault.
you should not use the POSIX low level functions to read input, it is non portable and the input might not be read in line chunks and will not be null terminated... Furthermore, a -1 return value is not always an error.
Use fgets() or other standard stream functions.
Here is a simple solution if you can assume that lists do not span multiple lines and are always terminated by ;:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int sumlist(int n, char *str) {
char *p, *q;
int sum = 0, term;
for (p = str;; p = q) {
p += strspn(p, " \t\n"); // skip blanks
if (*p == '\0')
break;
term = strtol(p, &q, 10);
if (q == p) {
printf("invalid input: %s\n", str);
return -1;
}
sum += term;
}
printf("List %d: %d (sum of %s)\n", n, sum, str);
return 0;
}
int main() {
char buf[200];
int n = 1;
char *p, *q;
while (fgets(buf, sizeof buf, stdin) {
for (p = str;;) {
p += strspn(p, " \t\n"); // skip initial blanks
if (*p == '\0')
break;
q = strchr(p, ';');
if (q != NULL)
*q = '\0';
if (p == q) {
p = q + 1; // skip empty lists
continue;
}
if (!strcmp(p, "exit"))
break;
sumlist(n++, p);
if (q == NULL)
break;
p = q + 1;
}
}
return 0;
}
If you cannot use fgets() or any standard stream functions, re-write your own version, reading one byte at a time from the OS handle with read() and carefully test for potential signal interrupts:
#include <errno.h>
#include <unistd.h>
char *my_gets(int hd, char *buf, size_t size) {
size_t i;
for (i = 0; i + 1 < size;) {
ssize_t n = read(hd, &buf[i], 1);
if (n != 1) {
if (n == -1 && errno == EINTR)
continue;
break;
}
if (buf[i++] == '\n')
break;
}
if (i == 0)
return NULL;
buf[i] = '\0';
return buf;
}
int main() {
char buf[200];
int n = 1;
char *p, *q;
while (my_gets(0, buf, sizeof buf) {
for (p = str;;) {
p += strspn(p, " \t\n"); // skip initial blanks
if (*p == '\0')
break;
q = strchr(p, ';');
if (q != NULL)
*q = '\0';
if (p == q) {
p = q + 1; // skip empty lists
continue;
}
if (!strcmp(p, "exit"))
break;
sumlist(n++, p);
if (q == NULL)
break;
p = q + 1;
}
}
return 0;
}
There are already working solutions here, but I'd like to suggest another one that might be helpful to understand some concepts.
Although you cannot use getc and ungetc, I would still address your problem in a way that uses the concept of a get_buf. My solution reads a character at a time and tries to turn it into a valid token that the main loop can switch on. In my opinion, that's a nice way to handle the parsing of simple 'languages' like the one you want to interpret. Also, it is pretty extensible & it's easy to add additional tokens (e.g. math operations like + - / *).
As a quick description what's happening: In get_char, a single byte is read from STDIN whenever the internal buffer is empty. If it is not, the character that's on the buffer is returned. This functionality is used by get_valid_token which returns either your delimiter ; or a (potentially multi-digit) number. Being able to 'unget' a character is required here. In main, we continuously get tokens and perform the appropriate action, nicely separating getting and interpretation of tokens. Obviously, this a quick and dirty program, but it might work for you.
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define BUF_SIZ 2 /* get_buf never buffers more than one char by design */
static char get_buf[BUF_SIZ];
static char *get_buf_ptr = get_buf;
char get_char(int fd)
{
char c;
/* check buffer first */
if (!(get_buf == get_buf_ptr))
return *get_buf_ptr--;
/* if buffer is empty, read from STDIN */
if ((read(fd, &c, 1)) == -1) {
perror("read");
exit(1);
}
return c;
}
void unget_char(char c)
{
*(++get_buf_ptr) = c;
}
void flush(int fd)
{
char c;
do {
read(fd, &c, 1);
} while (c != '\n');
}
char is_exit()
{
if ((get_char(STDIN_FILENO)) != 'x') return 0;
if ((get_char(STDIN_FILENO)) != 'i') return 0;
if ((get_char(STDIN_FILENO)) != 't') return 0;
flush(STDIN_FILENO); /* remove already buffered input */
return 1;
}
char *get_valid_token(void)
{
char c;
char *out;
char *out_ptr;
out_ptr = out = (char *)malloc(sizeof(char)*BUFSIZ);
while (1) {
c = get_char(STDIN_FILENO);
if (c == ';') {
*out = ';';
break;
} else if (isdigit(c)) {
*out = c;
out_ptr++;
/* get the rest of the digit */
while (1) {
c = get_char(STDIN_FILENO);
if (isdigit(c)) {
*out_ptr++ = c;
} else {
unget_char(c);
break;
}
}
*out_ptr = '\0';
break;
} else if (c == 'e') {
if (is_exit())
exit(0);
}
};
return out;
}
int main(void)
{
char *t;
int sum;
sum = 0;
while ((t = get_valid_token())) {
switch (*t) {
case ';':
fprintf(stderr, "sum: %d\n", sum);
sum = 0;
break;
default:
sum += atoi(t);
break;
}
free(t);
}
return 0;
}
So i need to get number from user and i want to check if the number is int:
char temp;
int num;
int res = scanf("%d%c", &num, &temp);
if (res == 2)
{
}
else
{
}
The problem is that even if the input is double for example 2.2 the if executed.
I also try this:
int n = atoi(&temp);
scanf is difficult to use if you want a very strict validation of the input typed by the user.
Instead you could you use getc to read the input char-by-char and do the validation and value calculation yourself.
Another approach could be fgets combined with sscanf. That could be something like:
#include <stdio.h>
#include <string.h>
#define MAX_LEN 8
int main(void) {
char str[MAX_LEN];
int d, n;
int flag = 0; // Assume it isn't an int
if (fgets(str, MAX_LEN, stdin)) // Read into str
{
size_t l = strlen(str);
if (l > 0) // Check that we did read something
{
if (str[l-1] == '\n') // Last char must be new line
{
if (sscanf(str,"%d%n", &d, &n) == 1) // Try to scan the str into an int
{
if (n == l-1) // Check that all chars was used
{
flag = 1; // It is an int so set the flag
}
}
}
}
}
if (flag)
{
printf("%d\n", d);
}
else
{
printf("Not an int\n");
}
return 0;
}
I am building a C program which gets a .txt file as an argument. Inside the .txt there are rows of integer numbers, with a space between them, like this:
1 2 3 4
5 6 7 8
...
I am supposed to find out, if a non-integer-character shows up inside the .txt file, like this:
1 2 a 4
...
Since there is no instanceof operator in C, I use an array which contains the numbers from 0-9 and check each character of the .txt-file, if it is either a space or an integer. If it is neither, the program is supposed to exit.
If there are no problems in the file, the program calculates a median and prints the line to stdout.
This is my code:
#include <stdio.h>
#include <ctype.h>
int arrayContains(char value);
int main(int argc, char **argv) {
const int LINESIZE = 255;
if (argc != 2) {
printf("wrong args!");
return -1;
}
char *command1 = argv[1];
FILE *handle = fopen(command1, "r");
if (!handle) {
printf("file not found!");
return -1;
}
int count = 0;
int sum = 0;
int median;
char string[LINESIZE];
while (fgets(string, LINESIZE, handle) != NULL) {
for (int i = 0; i <= sizeof(string) / sizeof(string[0]) - 1; i++) {
printf("%c", string[i]);
if (string[i] == ' ') {
i++;
}
else if (arrayContains(string[i]) == 0) {
count++;
sum += (int)string[i];
}
else {
printf("non-integer-character found!\n");
return -1;
}
}
median = sum / count;
printf("%s\n", string);
printf("%d\n", median);
}
}
int arrayContains(char value) {
const char numbers[10] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
for (int i = 0; i <= 9; i++) {
if (numbers[i] == value) {
return 0;
}
}
return -1;
}
Now the output of the program is always the first number of the txt-file and immediately after that: "non-integer-character found!".
Which tells me that the comparison between the element of the string from gets() and an element from the constant "numbers" array inside the arrayContains() function, will always result in a return of -1 from the arrayContains() function.
What am I doing wrong?
You should really check out isdigit C function!
#include <ctype.h>
int hasDigit(const char *s)
{
while (*s) {
if (isdigit(*s++) == 0) return 0;
}
return 1;
}
Code above returns 1 on empty string. Watch out! You can use my function instead of your arrayContains. If you want to use your routine, please check out my "version":
int arrayContains(char value) {
return isdigit(value)?0:-1;
}
There are several problems with your code.
This bit matches a space, but will result in you skipping the following character because your loop will also do i++.
if (string[i] == ' ') {
i++;
}
This is not the right way to turn a digit into a number. What you're getting here is the ASCII value of the character rather than the value of the digit. So for example if you have a '1' you're adding 49 to sum rather than 1.
sum += (int)string[i];
As discussed elsewhere, you're better off using isdigit() to identify if you've got a digit character. You can also use isspace() to test to see if you have a space or '\n' character (it covers all whitespace). Which would make your loop statement a lot less complicated as you can process the whole string and easily handle lines that are longer than the size of your buffer.
This code corrects the problems you have
while (fgets(string, LINESIZE, handle) != NULL) {
for (char *pos=string; *pos!='\0'; pos++) {
printf("%c", *pos);
if (isdigit(*pos)) {
count++;
sum += *pos-'0';
} elseif(!isspace(*pos)) {
printf("non-integer-character found!\n");
return -1;
}
}
median = sum / count;
printf("%s\n", string);
printf("%d\n", median);
}
I seem to have solved it:
#include <stdio.h>
#include <ctype.h>
int arrayContains(char value);
int main(int argc, char **argv) {
const int LINESIZE = 255;
if (argc != 2) {
printf("wrong args!");
return -1;
}
char *command1 = argv[1];
FILE *handle = fopen(command1, "r");
if (!handle) {
printf("file not found!");
return -1;
}
int count = 0;
int sum = 0;
int median;
char string[LINESIZE];
while (fgets(string, LINESIZE, handle) != NULL) {
for (int i = 0; i <= sizeof(string) / sizeof(string[0]) - 1; i++) {
printf("%c\n", string[i]);
if (isspace(string[i]) == 0) {
i++;
}
else if (isdigit(string[i]) == 0) {
count++;
sum += (int)string[i];
}
else {
printf("non-integer-character found!\n");
return -1;
}
}
fgets(string, LINESIZE, handle);
}
median = sum / count;
printf("%s\n", string);
printf("%d\n", median);
}
it now kinda does the job as expected.
You use the operator sizeof() which won't return the length of the String but the memory size of the pointer (a size_t so 8 bytes).
I suggest you to use this for your for loop:
for (int i = 0; string[i] != '\0' && string[i] != '\n'; i++) {
...
}
String in C are just a part of memory, it's just like an array and the only way to get the length is to find the end ('\0')
I also suggest you to directly search into the ASCII table. Characters are just number between 0 and 127. Digits are between 48 and 57, a simple condition does the stuff !
if (string[i] <= 48 || string[i] >= 57) {
...
}