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
"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;
}
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.
Related
I have this code and I need help converting the comments to c code
// if the input of scanf() is "q"
{
break;
}
else
{
// convert to int
}
Firstly, how do I check if an input is a certain character. Secondly, how do I turn a string into an integer. Example: "123" -> 123
Things I've tried, that didn't work: (it is possible that I implemented these solutions incorrectly)
how does scanf() check if the input is an integer or character?
Convert char to int in C and C++
I am not using any standard libraries except for stdio.h to print some logging information on the window
you have to know also that any string is terminated by null character which is '\0' to indicate the termination of the string , also you have to check is the user entered characters not numbers and so on (that's not implemented in this code).
I also handled if negative numbers are entered.
but you have to handle if the user entered decimals numbers , to sum up . there are so many cases to handle.
and here the edited code :
#include <stdio.h>
int main(){
char inputString[100];
printf("enter the input:\n");
scanf("%s", &inputString);
if(inputString[0] == 'q' && inputString[1] == '\0' )
{
printf("quiting\n");
//break;
}
else {
int i = 0;
int isNegative = 0;
int number = 0;
// check if the number is negative
if (inputString[0] == '-') {
isNegative = 1;
i = 1;
}
// convert to int
for ( ;inputString[i] != '\0' ; i++) {
number *= 10;
number += (inputString[i] - '0');
}
if(isNegative == 1)
number *= -1;
printf("you entered %d\n", number);
}
return 0;
}
The fundamental question here is, Do you want to use scanf?
scanf is everyone's favorite library function for easily reading in values. scanf has an input specifier, %d, for reading in integers.
And it has a different input specifier, %s, for reading in arbitrary strings.
But scanf does not have any single input specifier that means, "Read in an integer as an integer if the user types a valid integer, but if the user types something like "q", have a way so I can get my hands on that string instead."
Unless you want to move mountains and implement your own general-purpose input library from scratch, I think you have basically three options:
Use scanf with %d to read integers as integers, but check scanf's return value, and if scanf fails to read an integer, use that failure to terminate input.
Use scanf with %s to read the user's input as a string, so you can then explicitly test if it's a "q" or not. If not, convert it to an integer by hand. (More on this below.)
Don't use scanf at all. Use fgets to read the user's input as a whole line of text. Then see if it's a "q" or not. If not, convert it to an integer by hand.
Number 1 looks something like this:
while(some loop condition) {
printf("enter next integer, or 'q' to quit:\n");
if(scanf("%d", &i) != 1) {
/* end of input detected */
break;
}
do something with i value just read;
}
The only problem with this solution is that it won't just stop if the user types "q", as your original problem statement stipulated. It will also stop if the user types "x", or "hello", or control-D, or anything else that's not a valid integer. But that's also a good thing, in that your loop won't get confused if the user types something unexpected, that's neither "q" nor a valid integer.
My point is that explicitly checking scanf's return value like this is an excellent idea, in any program that uses scanf. You should always check to see that scanf succeeded, and do something different if it fails.
Number 2 would look something like this:
char tmpstr[20];
while(some loop condition) {
printf("enter next integer, or 'q' to quit:\n");
if(scanf("%19s", tmpstr) != 1) {
printf("input error\n");
exit(1);
}
if(strcmp(tmpstr, "q") == 0) {
/* end of input detected */
break;
}
i = atoi(tmpstr); /* convert string to integer */
do something with i value just read;
}
This will work well enough, although since it uses atoi it will have certain problems if the user types something other than "q" or a valid integer. (More on this below.)
Number 3 might look like this:
char tmpstr[20];
while(some loop condition) {
printf("enter next integer, or 'q' to quit:\n");
if(fgets(tmpstr, 20, stdin) == NULL) {
printf("input error\n");
exit(1);
}
if(strcmp(tmpstr, "q\n") == 0) {
/* end of input detected */
break;
}
i = atoi(tmpstr); /* convert string to integer */
do something with i value just read;
}
One thing to note here is that fgets includes the newline that the user typed in the string it returns, so if the user types "q" followed by the Enter key, you'll get a string back of "q\n", not just "q". You can take care of that either by explicitly looking for the string "q\n", which is kind of lame (although it's what I've done here), or by stripping the newline back off.
Finally, for both #2 and #3, there's the question of, what's the right way to convert the user's string to an integer, and what if it wasn't a valid integer? The easiest way to make the conversion is to call atoi, as my examples so far have shown, but it has the problem that its behavior on invalid input is undefined. In practice, it will usually (a) ignore trailing nonnumeric input and (b) if there's no numeric input at all, return 0. (That is, it will read "123x" as 123, and "xyz" as 0.) But this behavior is not guaranteed, so these days, most experts recommend not using atoi.
The recommended alternative is strtol, which looks like this:
char *endp;
i = strtol(tmpstr, &endp, 10); /* convert string to integer */
Unlike atoi, strtol has guaranteed behavior on invalid input. Among other things, after it returns, it leaves your auxiliary pointer endp pointing at the first character in the string it didn't use, which is one way you can determine whether the input was fully valid or not. Unfortunately, properly dealing with all of the ways the input might be invalid (including trailing garbage, leading garbage, and numbers too big to convert) is a surprisingly complicated challenge, which I am not going to belabor this answer with.
Here are some guidelines:
scanf("%s", &var) is incorrect: you should pass the maximum number of characters to store into the array var and pass the array without the & as it will automatically convert to a pointer to its first element when passed as an argument:
char var[100];
if (scanf("%99s", var) != 1) {
printf("premature end of file\n");
return 1;
}
to compare the string read to "q", you can use strcmp() declared in <string.h>:
if (strcmp(var, "q") == 0) {
printf("quitting\n");
return 0;
}
to convert the string to the number it represents, use strtol() declared in <stdlib.h>:
char *p;
long value = strtol(var, &p, 0);
testing for a proper conversion is tricky: strtol() updated p to point to the character after the number and set errno in case of range error:
errno = 0;
char *p;
long value = strtol(var, &p, 0);
if (p == var) {
printf("not a number: %s\n", p);
return 1;
}
if (*p != '\0') {
printf("extra characters: %s\n", p);
return 1;
}
if (errno) {
printf("conversion error: %s\n", strerror(errno));
return 1;
}
printf("the number entered is: %ld\n", value);
return 0;
Here is a complete program:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char var[100];
char *p;
long value;
printf("Enter a number: ");
if (scanf("%99s", var) != 1) {
printf("premature end of file\n");
return 1;
}
if (strcmp(var, "q") == 0) {
printf("quitting\n");
return 0;
}
errno = 0;
value = strtol(var, &p, 0);
if (p == var) {
printf("not a number: %s\n", p);
return 1;
}
if (*p != '\0') {
printf("extra characters: %s\n", p);
return 1;
}
if (errno) {
printf("conversion error: %s\n", strerror(errno));
return 1;
}
printf("the number entered is: %ld\n", value);
return 0;
}
You can try this: (Assuming only positive integers needs to convert)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
// Write C code here
char var[100];
int numb_flag=1, i=0,number=0;
scanf("%s",var);
while(var[i]!='\0') { // Checking if the input is number
if(var[i]>=48 && var[i]<=57)
i++;
else {
numb_flag = 0;
break;
}
}
if(numb_flag==1) {
number = atoi(var);
printf("\nNumber: %d",number);
} else {
printf("\nNot A Number");
}
return 0;
}
//Mind that in order to be more precise you could also use atof().
//The function works the same way as atoi() but is able to convert float
// (returned value is 'double') the string s
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXILEN 100 /*change the value based on your needs*/
int main(){
int n, lim = MAXILEN;
char s[MAXILEN], *p = s, c;
/*taking the string from the input -- I do not use scanf*/
while(--lim > 0 && (c = getchar()) != EOF && c != '\n')
*p++ = c;
//here you can also do the check (if you want the '\n' char):
//if(c == '\n')
// *s++ = c;
*p = '\0';
if(s[0] == 'q' && s[1] == '\0')
exit(EXIT_SUCCESS); /*change the argument based on your needs*/
else
n = atoi(s);
printf("[DEBUG]: %d\n", n);
}
I m trying to do this little programm with defensive programming but its more than difficult for me to handle this avoiding the Loop-Goto as i know that as BAD programming. I had try with while and do...while loop but in one case i dont have problem. Problem begins when i m going to make another do...while for the second case ("Not insert space or click enter button"). I tried and nested do...while but here the results was more complicated.
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int i;
int length;
char giventext [25];
Loop:
printf("String must have 25 chars lenght:\n");
gets(giventext);
length = strlen(giventext);
if (length > 25) {
printf("\nString has over %d chars.\nMust give a shorter string\n", length);
goto Loop;
}
/* Here i trying to not give space or nothing*/
if (length < 1) {
printf("You dont give anything as a string.\n");
goto Loop;
} else {
printf("Your string has %d\n",length);
printf("Letter in lower case are: \n");
for (i = 0; i < length; i++) {
if (islower(giventext[i])) {
printf("%c",giventext[i]);
}
}
}
return 0;
}
Note that your code is not defensive at all. You have no way to avoid a buffer overflow because,
you check for the length of the string after it has been input to your program so after the buffer overflow has already occurred and
you used gets() which doesn't check input length and thus is very prone to buffer overflow.
Use fgets() instead and just discard extra characters.
I think you need to understand that strlen() doesn't count the number of characters of input but instead the number of characters in a string.
If you want to ensure that there are less than N characters inserted then
int
readinput(char *const buffer, int maxlen)
{
int count;
int next;
fputc('>', stdout);
fputc(' ', stdout);
count = 0;
while ((next = fgetc(stdin)) && (next != EOF) && (next != '\n')) {
// We need space for the terminating '\0';
if (count == maxlen - 1) {
// Discard extra characters before returning
// read until EOF or '\n' is found
while ((next = fgetc(stdin)) && (next != EOF) && (next != '\n'))
;
return -1;
}
buffer[count++] = next;
}
buffer[count] = '\0';
return count;
}
int
main(void)
{
char string[8];
int result;
while ((result = readinput(string, (int) sizeof(string))) == -1) {
fprintf(stderr, "you cannot input more than `%d' characters\n",
(int) sizeof(string) - 1);
}
fprintf(stdout, "accepted `%s' (%d)\n", string, result);
}
Note that by using a function, the flow control of this program is clear and simple. That's precisely why goto is discouraged, not because it's an evil thing but instead because it can be misused like you did.
Try using functions that label logical steps that your program needs to execute:
char * user_input() - returns an input from the user as a pointer to a char (using something other than get()! For example, look at scanf)
bool validate_input(char * str_input) - takes the user input from the above function and performs checks, such as validate the length is between 1 and 25 characters.
str_to_lower(char * str_input) - if validate_input() returns true you can then call this function and pass it the user input. The body of this function can then print the user input back to console in lower case. You could use the standard library function tolower() here to lower case each character.
The body of your main function will then be much simpler and perform a logical series of steps that tackle your problem. This is the essence of defensive programming - modularising your problem into separate steps that are self contained and easily testable.
A possible structure for the main function could be:
char * user_input();
bool validate_input(char *);
void str_to_lower(char *);
int main()
{
char * str_input = user_input();
//continue to get input from the user until it satisfies the requirements of 'validate_input()'
while(!validate_input(str_input)) {
str_input = user_input();
}
//user input now satisfied 'validate_input' so lower case and print it
str_to_lower(str_input);
return 0;
}
(Sorry for my bad english !)
I wrote a program that asks you to type a password no longer than a certain number, eight characters in this case. The characters that pass the limit will be cut out from the array:
#include <stdio.h>
#define MAXCHAR 8
main()
{
char password[MAXCHAR];
int i;
char c;
printf("Insert password: MAX 8 CHARS!\n\n");
for(i = 0; i <= MAXCHAR; i++){
c = getchar();
if(i == MAXCHAR){
break;
}
else{
password[i] = c;
}
}
printf("%s\n", password);
}
So the program works BUT there is a "strange" problem. If the limit IS EIGHT and I type a password longer than eight characters
(Example: P455w0rds98)
the output will be like this:
P455w0rd☺
So it puts a smiley at the end and I don't know why. It happens only if a the limit is established at eight.
You must specify the length to print or terminate the string. Otherwise, you will invoke undefined behavior. Try this, in which the latter method is implemented.
#include <stdio.h>
#define MAXCHAR 8
int main(void)
{
char password[MAXCHAR + 1]; /* allocate one more element for terminating null-character */
int i;
char c;
printf("Insert password: MAX 8 CHARS!\n\n");
for(i = 0; i <= MAXCHAR; i++){
c = getchar();
if(i == MAXCHAR){
break;
}
else{
password[i] = c;
}
}
password[MAXCHAR] = '\0'; /* terminate the string */
printf("%s\n", password);
}
Some people say that the if(i == MAXCHAR){ break; } part doesn't look good, so here is another code example:
#include <stdio.h>
#define MAXCHAR 8
int main(void)
{
char password[MAXCHAR + 1]; /* allocate one more element for terminating null-character */
int i;
printf("Insert password: MAX 8 CHARS!\n\n");
/* read exactly 8 characters. To improve, breaking on seeing newline or EOF may be good */
for(i = 0; i < MAXCHAR; i++){
password[i] = getchar();
}
password[MAXCHAR] = '\0'; /* terminate the string */
getchar(); /* to match number of call of getchar() to the original: maybe for consuming newline character after 8-digit password */
printf("%s\n", password);
}
All C-style strings have a terminal \0 character (value 0). This is unique from any other character value, so it can be used to signal the end of the string. The smiley face you observe is just a part of some neighboring memory block that happens to have a null character after the first byte (hence there being only one extra character). The printf function reads bytes from the string given to it, until it sees the \0. To solve your problem, you can either write
password[MAXCHAR] = '\0';
(You will need to reserve one additional byte in your array, for the \0).
Or you can zero-out your array from the get-go:
char password[MAXCHAR + 1] = { };
Or using memset:
memset(password, '\0', sizeof password);
Apart from the answer you already received from MikeCAT, an alternate approach would be to make use of fgets() to read the user input.
In that case , you don't need to keep a count on each character input, you can specify the max size and get done with it. Something like
fgets(password, MAXCHAR, stdin);
can get the job done for you, minus the looping and assignment for each element.
One thing to remember, however, for shorter inputs than the given length, fgets() reads and stores the trailing newline also, you may need to get rid of that manually. Read the linked man page for more ideas.
That said, main() is a very bad and almost non-standard for hosted environments. You should use int main(void), at least to conform to the standards.
I'm trying to make a function to validate mobile entry, the mobile number MUST starts with 0 and is 11 numbers (01281220427 for example.)
I want to make sure that the program gets the right entry.
This is my attempt:
#include <stdio.h>
#include <strings.h>
void integerValidation(char x[15]);
int main(int argc, char **argv)
{
char mobile[15];
integerValidation(mobile);
printf("%s\n\n\n", mobile);
return 0;
}
void integerValidation(char x[15]){
char input[15];
long int num = -1;
char *cp, ch;
int n;
printf("Please enter a valid mobile number:");
while(num<0){
cp = fgets(input, sizeof(input), stdin);
if (cp == input) {
n = sscanf(input, "%ld %c", &num, &ch);
if (n!=1) {printf("ERROR! Please enter a valid mobile number:");
num = -1;
}
else if (num<0)
printf("ERROR! Please enter a valid mobile number:");
else if ((strlen(input)-1)>11 || (strlen(input)-1)<11 || strncmp(&input[0], "0", 1) != 0){
printf("ERROR! Please enter a valid mobile number:");
num = -1;
}
}
}
long int i;
i = strlen(input);
//Because when I try to print it out it prints a line after number.
strcpy(&input[i-1], "");
strcpy(x, input);
}
Now, if I don't use
strcpy(&input[i-1], "");
the array prints a new line after the number, what would be a good fix other than mine? and how can I make this function optimized and shorter?
Thanks in advance!
Edit:
My question is: 1. Why does the input array prints a new line in the end?
2. How can I make this code shorter?
End of edit.
If you insist on using sscanf(), you should change the format this way:
int integerValidation(char x[15]) {
char input[15], c;
printf("Please enter a valid mobile number:");
while (fgets(input, sizeof(input), stdin)) {
if (sscanf(input, "%11[0123456789]%c", x, &c) == 2
&& x[0] == '0' && strlen(x) == 11 && c == '\n') {
// number stored in `x` is correct
return 1;
}
printf("ERROR! Please enter a valid mobile number:");
}
x[0] = '\0'; // no number was input, end of file reached
return 0;
}
%12[0123456789] parses at most 11 characters that must be digits.
%c reads the following character, which should be the trailing '\n'.
I verify that both formats have been matched, and the number starts with 0 (x[0] == '0') and it has exactly 11 digits.
You're seeing the newline, since fgets() reads until an EOF or a newline is received. The newline is stored in the buffer, and after that the string is terminated with '\0'.
An alternative would be to directly overwrite the newline with another null-byte: input[i-1] = '\0' (which basically does the same thing as your solution, but saves a function call).
The same goes for the check with strncmp with length 1, you can directly check input[0] == '0'. Note that you have to compare against '0' (char) here, not "0" (string).
A few other things I'm seeing:
You can also spare the %c in the format string for sscanf (you're never evaluating it anyway, since you're checking for 1 as return value), which also eliminates the need for char ch.
Also, you're passing char x[15] as argument to your function. This is a bit misleading, because what actually gets passed is a pointer to a char array (try using sizeof(x), your compiler will most likely issue a warning about the size of char * being returned by sizeof()).
What you could do is to ditch the char array input, which you're using as temporary buffer, and use the buffer which was handed over as argument. For this to be save, you should use a second funcion parameter to specify the size of the buffer which was handed to the function, which would result in a function header like as follows:
void integerValidation(char *input, size_t len);
With this, you'd have to use len instead of sizeof(input). The following question provides more detail why: C: differences between char pointer and array
Since you're not using a temporary buffer anymore, you can remove the final call to strcpy().
There are also a lot of checks for the number length/format. You can save a few:
If you use %lu instead of %ld no signed numbers are being converted, which saves you the check for num < 0.
You're checking whether the length of the read number is <11 or >11 - why not just check for !=11?
You're calling strlen() three times on the input-buffer (or still twice with the reworked check for lengh 11) - it makes sense to call it once, save the length in a variable and use that variable from then on, since you're not altering the string between the calls.
There is already an accepted answer, but for what it's worth, here is another.
I made several changes to your code, firstly avoiding "magic numbers" by defining the phone number length and an arbitrarily greater string length. Then there is no point passing an array x[15] to a function since it pays no regard to its length, might as well use the simpler *x pointer. Next, I return all reasons for failure back to the caller, that's simpler. And instead of trying to treat the phone number as a numeric entry (note: letters, spaces, hyphens, commas and # can sometimes be a part of phone number too) I stick to a character string. Another reason is that the required leading zero will vanish if you convert the entry to an int of some size. I remove the trailing newline that fgets() reads with the input line, and the result is this.
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAXLEN 11
#define STRLEN (MAXLEN+10)
int integerValidation(char *x);
int main(int argc, char **argv)
{
char mobile[STRLEN];
while (!integerValidation(mobile)) // keep trying
printf("Invalid phone number\n");
printf("%s\n\n\n", mobile); // result
return 0;
}
int integerValidation(char *x)
{
int i, len;
printf("Please enter a valid mobile number:");
if(fgets(x, STRLEN, stdin) == NULL) // check bad entry
return 0;
x [ strcspn(x, "\r\n") ] = 0; // remove trailing newline etc
if((len = strlen(x)) != MAXLEN) // check length
return 0;
if(x[0] != '0') // check leading 0
return 0;
for(i=1; i<len; i++) // check all other chars are numbers
if(!isdigit(x[i]))
return 0;
return 1; // success
}
I'm a beginner programmer trying to learn C. Currently I'm taking a class and had a project assigned which I managed to finish pretty quickly, at least the main part of it. I had some trouble coding around the main() if functions though, because I started using some new functions (that is, fgets and strncmp). Now, my code works in my compiler, but not in any of the online compilers. So I'm wondering if I did something wrong with it, or if there is any way I can improve it.
Any help or contribution is appreciated, thanks!
Below is the code, the encrypt and decrypt functions are the first two functions before the main, where I believe most of the messy shortcut-code might be.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * Encrypt(char sentence[])
{
int primes[12] = {1,2,3,5,7,11,13,17,19,23,29,31};
int x = 0;
int counter = 0;
int ispositive = 1;
while(sentence[x] != 0)
{
if (counter == 0)
{
ispositive = 1;
}
else if(counter == 11)
{
ispositive = 0;
}
if (ispositive == 1)
{
sentence[x] = sentence[x] + primes[counter];
counter++;
}
else if (ispositive == 0)
{
sentence[x] = sentence[x] + primes[counter];
counter--;
}
x++;
}
return sentence;
}
char * Decrypt(char sentence[])
{
int primes[12] = {1,2,3,5,7,11,13,17,19,23,29,31};
int x = 0;
int counter = 0;
int ispositive = 1;
while(sentence[x] != 0)
{
if (counter == 0)
{
ispositive = 1;
}
else if(counter == 11)
{
ispositive = 0;
}
if (ispositive == 1)
{
sentence[x] = sentence[x] - primes[counter];
counter++;
}
else if (ispositive == 0)
{
sentence[x] = sentence[x] - primes[counter];
counter--;
}
x++;
}
return sentence;
}
int main()
{
char message[100];
char input[7];
char *p;
int c;
int condition = 1;
while(condition == 1)
{
printf("Would you like to Encrypt or Decrypt a message? (Type TurnOff to end the program) \n \n");
fgets(input,7, stdin);
fflush(stdin);
if (!strncmp(input,"Encrypt",strlen(input)))
{
printf("\n \n Enter the message you want to Encrypt below: \n \n");
fgets(message, 100, stdin);
Encrypt(message);
printf("\n Your encrypted message is: ");
printf("%s", message);
fflush(stdin);
printf("\n \n");
}
else if (!strncmp(input,"Decrypt",strlen(input)))
{
printf("\n \n Enter the message you want to Decrypt below: \n \n");
fgets(message, 100, stdin);
Decrypt(message);
printf("\n Your Decrypted message is: ");
printf("%s", message);
fflush(stdin);
printf("\n \n");
}
else if (!strncmp(input,"TurnOff",strlen(input)))
{
printf("\n \n Thank you for using the program! \n \n");
condition = 0;
}
else
{
printf("That's not a valid input \n \n");
}
}
}
After the printf you doing fflush(stdin) instead of you have to do fflush(stdout). Because you are printing the output. The output is printed in stdout. So, you have to flush the stdout buffer not stdin buffer.
You can use the strcmp instead of strncmp. Because in here you are comparing the hole character in the input array. So, the strcmp is enough.
strcmp(input, "Encrypt").
The strcmp or strncmp function get the input in array upto a null or the size of the string you are declared.
The size for the input array is too few.
lets take the input is like below.
Encrypt\n
sureshkumar\n
In here you first fgets in main function reads the upto "Encrypt" it does not skip the '\n'.
The '\n' is readed form another fgets. So, it does not get the encrypt message "sureshkumar".
So, you have to modify you code. You will increase the size for the input array.
And check the condition like below.
if(strcmp(input, "Encrypt\n") == 0)
{
/*
You will do what you want
*/
}
You can use the above way or you can read the input and overwrite the '\n' to '\0' in the input array and compare as it is you before done. But you have to use the strcmp. Because the array size is incremented.
This is the right way for using the fgets. Use of fgets is to read upto new line.
You have to use the null character for the character array. Because this is necessary for the character arrays.
Your initiative towards using strcmp() and fgets() is good, though, it requires following understanding:
1. fgets() writes atmost size-1 characters into buffer and then terminates with '\0'. In your case,
fgets(input,7, stdin);
You gave input "Encrypt"/"Decrypt"/"TurnOff"
but
'input' buffer got data as "Encryp"/"Decryp"/"TurnOf"
because of size=7 (only (7-1)=6 characters being read, last position reserved for '\0' character by fgets()).
Your strncmp() calls will work correctly with your current code, since for strncmp(), length to compare
n = strlen(input) = 6;
6 characters are matching fine in all three cases of "Encrypt"/"Decrypt"/"TurnOff".
Summary is that your current code will work fine, But your actual intention is violated. You actually wanted to read and compare full length of option string.
EDIT DONE : Modifications suggested:
#define SIZE 9 <-- EDIT : Change done here, instead of 7, size = 9 is used
to allow reading '\n' so that it does not affect
fgets() read in successive iteration
char input[SIZE];
fgets(input, SIZE, stdin); // read str is e.g. "Encrypt\n"
input[SIZE-2] = '\0'; // To replace '\n' with '\0'
Similarly, you need to be careful when reading into 'message' array using fgets().