How to check input in command line is integer in C? - c

I want the code to check input in command line is integer. i.e. 10b is not valid. I tried isdigit() but is not working? Thanks in advance.
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
int main(int argc, string argv[])
{
if (argc == 2)
{
int key = atoi(argv[1]);
if (isdigit(key))
{
printf("Success\n\%i\n", key);
exit(0);
}
}
printf("Usage: ./caesar key\n");
return 1;
}

Function isDigit checks if a single character is a digit, i.e. in the range between '0'..'9'. To check if a string is a number, I'd suggest to use function strtol.
long strtol(const char *str, char **str_end, int base ) converts a string str to an integral number and also sets the pointer str_end to the first character that took not part in the conversion any more. If you require that no characters must follow your number, then str_end must point to the string's end, i.e. to string termination character '\0':
#include <stdio.h>
#include <stdlib.h>
int isNumber(const char* str) {
if (!str || *str=='\0') { // NULL str or empty str: not a number
return 0;
}
char* endOfNum;
strtol(str,&endOfNum,10);
if (*endOfNum == '\0') { // string is at its end; everything in it was a valid part of the number
return 1;
} else {
return 0; // something that is not part of a number followed.
}
}
int main() {
const char* vals[] = {
"10b",
"-123",
"134 ",
" 345",
"",
NULL
};
for (int i=0; vals[i]; i++) {
printf("testing '%s': %d\n", vals[i], isNumber(vals[i]));
}
}
Output:
testing '10b': 0
testing '-123': 1
testing '134 ': 0
testing ' 345': 1
testing '': 0
Adapt the meaning of corner cases like empty strings or NULL-strings to your needs.

The isdigit function checks whether a single character represents a single digit. (numbers − 0 1 2 3 4 5 6 7 8 9).
In order to check if a string is an integer you could use a function like that. It will
bool isNumber(char number[])
{
int i = 0;
// only if you need to handle negative numbers
if (number[0] == '-')
i = 1;
for (; number[i] != 0; i++)
{
if (!isdigit(number[i]))
return false;
}
return true;
}

My first thought would be to use something like:
int inputvalue = 0;
if (sscanf(argv[i], "%d", &inputvalue) == 1)
{
// it's ok....
}
else
{
// not an integer!
}
or something like that. See http://www.cplusplus.com/reference/cstdio/sscanf/

Related

Are the digits in a given string strictly ascending?

I am a beginner programmer and I'm having trouble with this task.
I have to write a function that checks if the digits in the given string are strictly ascending, or better say is each digit larger than the previous one.
Function returns true or false, but it also has to be able to return -1 if there are no numbers in the string.
So for the string: "a1b2c3d" the function should return 1,
but for "a1b3c3d" the function should return 0 because they are not strictly ascending.
I cannot use arrays or create new strings.
#include <stdio.h>
#include<ctype.h>
//we have to enter our strings like this
void unesi(char niz[],int velicina)
{
char znak=getchar();
if(znak=='\n')
znak=getchar();
int i=0;
while(i<velicina-1 && znak!='\n')
{
niz[i]=znak;
i++;
znak=getchar();
}
niz[i]='\0';
}
//function checks if the string contains any digits
int cifre(const char *s)
{
int istina=1;
if(isdigit(*s++))
istina=0;
return istina;
}
int srce(const char *s)
{
int broj=1,istina=0;
if(cifre(s)==1)
return -1;
else
{
while(*s!='\0')
{
if(isdigit(*s)==1)
{
if(*s<broj)
{
broj=*s;
istina=1;
s++;
}
}
else
s++;
}
return istina;
}
}
int main() {
char a[100];
unesi(a,100);
printf("%d",srce(a));
return 0;
}
Logic is quite simple.
The function returns -1 if there is no digits
1 if they are sorted in ascending order or zero if not
int isAsc(char *str)
{
int result = -1;
int last = -1;
while(*str)
{
if(isdigit((unsigned char)*str))
{
result = 1;
if(last != -1)
{
if(*str - '0' < last)
{
result = 0;
break;
}
}
last = *str - '0';
}
str++;
}
return result;
}
int main(void)
{
printf("%d\n", isAsc("dsfgdfgdfgfgd"));
printf("%d\n", isAsc("d0sfg1dfg4dfg7fgd"));
printf("%d\n", isAsc("d0sfg1dfg4dfg7f2gd"));
}
//we have to enter our strings like this
It's unclear to me if the whole unesi function is given as part of the assignment and can't be modified or not, so I won't comment it.
Just read about fgets, in the future.
I cannot use arrays
Well, I guess that restriction is somehow relaxed:
char a[100];
That's "literally" an array of 100 chars. Hopefully, it will be null-terminated, so that we can consider it a "string".
//function checks if the string contains any digits
That comment is lying, cifre is only checking the first character, because there is no loop, nor any calling to a function that could traverse a char array inside it.
Besides, instead of checking beforehand if the string contains any digit, you could start searching for the first digit and then check if the other digits are in strictly ascending order.
In srce function, we can find
int srce(const char *s)
{
int broj=1, istina=0;
// ^^^^^^ ^^ Note that the starting value of broj is 1.
// [...]
while(*s!='\0')
{
if(isdigit(*s)==1)
{
if( *s < broj )
{ //^^^^^^^^^ Are any of '0', '1', '2'... less than 1?
// Is this supposed to be the path taken when
// the digits are not in STRICTLY ascending order?
// In that case you should check if the current digit
// is less than OR EQUAL to the previous one.
broj=*s;
istina=1; // Shouldn't it be 0 when the order is not correct?
s++; // Shouldn't the traversal of the string just end now?
}
}
else
s++;
}
return istina;
}
Another subtle issue with the previous code is that isdigit1 returns a non-zero value if the character is a numeric character, not necessarly 1.
(1) About isdigit call, see also: Why is it OK to pass mismatch argument into isdigit function?

Why I am getting an empty value in this implementation of Caesar's cipher?

I've implemented the Caesar's cipher in C, and, despite the algorithm is working, I didn't understood why (sometimes) I get an empty value if I do not subtract the first letter of the alphabet before adding the key. Here's the full code (see line 59 or search for return (letter + k) % 26):
#include <cs50.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
string caesar_cipher(string text, int k);
char replace_letter(char letter, int k);
bool is_numeric(string input);
int main(int argc, string argv[])
{
if (argc != 2 || (argc == 2 && !is_numeric(argv[1])))
{
fprintf(stderr, "You must specify a key to the cipher! Exiting...\n");
exit(EXIT_FAILURE);
}
// Convert command line argument to integer.
int k = atoi(argv[1]);
// Prompts user for the text to encrypt
string text = get_string("plaintext: ");
// Returns encrypted text
printf("ciphertext: %s\n", caesar_cipher(text, k));
exit(EXIT_SUCCESS);
}
string caesar_cipher(string text, int k)
{
int text_length = strlen(text);
string ciphered_text = text;
for (int i = 0; text[i] != '\0'; i++)
{
ciphered_text[i] = replace_letter(text[i], k);
}
return ciphered_text;
}
char replace_letter(char letter, int k)
{
// Early return when 'letter' is a non-alphabetical character
if (!isalpha(letter))
{
return letter;
}
char operation_letter = 'a';
if (isupper(letter))
{
operation_letter = 'A';
}
// return (letter + k) % 26; // Sometimes, returns an empty value
return ((letter - operation_letter + k) % 26) + operation_letter;
}
// Loop over characters to check if each one of them is numeric
bool is_numeric(string input)
{
for (int i = 0; input[i] != '\0'; i++)
{
// If character is not numeric
// returns false.
if (isdigit(input[i]) == 0)
{
return false;
}
}
return true;
}
Can anybody explain why this happens?
You need to account for the first letter of the alphabet (either a or A) in your functions because chars are internally represented as an integer number (usually only a single byte, but it depends on the encoding). In ASCII for example, doing a % 26 will result in any of the 26 first values of the ASCII table, none of which are actual letters. Hopefully I made myself clear.

Why am I getting errors?

I'm trying to create a program that accepts cmd line arguments to encipher a plaintext!
The program must accept one cmd line argument after its name when making it and this would be the key which by the plaintext (only) alphabetical characters are rotated by this key (e.g. it's number is added to the real alphabet ASCII number resulting in another alphabet to be printed!
it is supposed to print an error message when one argument is present (e.g. here:/make encipher)
instead of here:/make encipher 12 <-- 12 = key!
I am getting a segmentation fault when running the program without the key argument, why?
This is the full code. I'm posting it because I need to learn where is my fault's exact location
and why is it triggered?!
#include <cs50.h>
#include <stdio.h>
#include <stdlib.h> // To use atoi (converting a string to an int)
#include <ctype.h>
#include <string.h>
bool key_is_numb(string argv[]);
void encipher(string txt, int key);
int main(int argc, string argv[])
{
if (key_is_numb(argv) == false)
{
printf("Usage: ./caesar key\n");
return 1;
}
else
{
int key = atoi(argv[1]);
string plaintext;
if (argc == 2 && key > 0)
{
plaintext = get_string("plaintext: ");
encipher(plaintext, key); // A function that prints the ciphered text
return 0; // returns Zero as main return value which means "All good"!
}
else if (argc == 1 || argc > 2 || key <= 0)
{
printf("Usage: ./caesar key\n");
return 1;
}
} // End else.
} // End main()å func.
bool key_is_numb(string argv[])
{
int n = strlen(argv[1]);
for (int i = 0; i < n; i++) // checking element by element in the second string of the argv[] array of strings
{
if (isdigit(argv[1][i]) == 0) // if the entered string "key" contains chars other than digits.
{
return false; // break out of the if statement & the entire function key_is_numb()
// and return false as soon as a letter is encountered.
}
else
{
continue; // go up & start the next iteration for the for loop.
}
// if only digits encountered then this for loop will come to an end and exist from here.
} // End for loop
return true; // function exits and return boolean true from here.
} // End key_is_numb() func.
void encipher(string txt, int key)
{
printf("ciphertext: ");
for (int i = 0, n = strlen(txt); i <= n; i++) // strlen counts the number of elements in a string excluding '\0'
{
char c = txt[i];
if (isalpha(c))
{
if (isupper(c))
{
char m = 'A'; // This is a modifyer character equals to 'A' = 65 so that it is indexed # ZERO!
printf("%c", (c - m + key) % 26 + m );
//c = ((((int)txt[i] - 65) + key) % 26) + 65; // char c = 65 <-- 65 is an ASCII code equals 'A'
}
else if (islower(c))
{
char m = 'a'; // This is a modifying character 'a' = 97
printf("%c", (c - m + key) % 26 + m );
}
}// End if(alpha).
else
{
printf("%c", c);
}
} // End for().
printf("\n");
} // End encipher() func.
int n = strlen(argv[1]);
in key_is_numb() and
int key = atoi(argv[1]);
in main().
If you didn't enter a key argument, argv[1] as equal as argv[argc] is a null pointer as stated in C17, §5.1.2.2.1/2.
Any attempt to access its data is undefined behavior and probably caused the segmentation fault.
Well you are assuming that argv[1] is defined in key_is_numb. However, in C and C++, the second parameter of the main function contains command line arguments. Which, in your case will be the name of the binary as the first element, then any other arguments. This is why when you are running the program without arguments, it will segfault, as there are no argument to put in argv, and no default value either.
You should always check the size of argv, by using the number stored in argc, before trying to read anything in argv.
Your segmentation fault comes from this line int n = strlen(argv[1]);, but I'd highly suggest you to learn to use debugger software like valgrind, which if the program has been compiled with debug flag will tell you the exact line.
Other debugger are really useful too, so you should learn to use them, as they usually report this kind of errors.
Your code asumes there is always an argv[1]. You should check argc which tells the number of arguments. For example:
int main(int argc, string argv[])
{
if (argc < 2) {
printf("Key required\n");
exit (1);
}

Return value won't change, always returns with 0

I'm kinda new to coding, and I have this assignment: I need to write a function in C that checks whether the given string is formatted properly, and if not, it should return 0. The string should contain only letters and the delimiter " - ". So, for example: "aAa - bBb" is correct, so the return value should be 1, but for "a-1 - bB3", it should return 0.
Here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int functiontest(char* text){
char* piece = strtok(text," - "); //breaks the given string into
int returnvalue = 1; //smaller strings
while (piece != NULL) {
for (int x = 0; x < strlen(piece); x++) {
if ((isalnum(piece[x]))) {
returnvalue = 1;
} else {
returnvalue = 0;
break;
}
}
piece = strtok(NULL," - ");
}
return returnvalue;
}
int main() {
char texttest[11] = "akod - kljp";
printf("%s", &texttest);
int test = functiontest(texttest);
printf("\n%d",test);
return 0;
}
No matter how I change the texttest string, the function always returns 0. What's wrong with it?
Your break is in the wrong conditional block. If you were to have a digit as the every character, you would see a return value of 1. Basically, what you have written is going to return 0 at the first occurrence of a non-digit. Since you have non-digit characters in your input, it will always be 0. 3 or 333 would return 1, but 3- would return 0; no non-digit-containing string will ever return 1.

fetching string and converting to double

I'm trying to create a function which can determine if an input can be converted perfectly into a double and then be able to store it into an array of doubles. For example, an input of "12.3a" is invalid. From what I know, strtod can still convert it to 12.3 and the remaining will be stored to the pointer. In my function doubleable, it filters whether the input string consists only of digits. However, I'm aware that double has "." like in "12.3", and my function will return 'X' (invalid).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char doubleable (char unsure[], int length){
int i;
int flag;
for (i=0; i<length; i++ ){
if(isdigit(unsure[i]==0)){
printf("You have invalid input.\n");
flag=1;
break;
}
}
//check for '.' (?)
if(flag==1)
return 'X';
else
return 'A';
}
int main(){
char input [10];
double converted[5];
char *ptr;
int i;
for(i=0; i<5; i++){
fgets(input, 10, stdin);
//some code here to replace '\n' to '\0' in input
if(doubleable(input, strlen(input))=='X'){
break;
}
converted[i]=strtod(input, &ptr);
//printf("%lf", converted[i]);
}
return 0;
}
I'm thinking of something like checking for the occurrence of "." in input, and by how much (for inputs like 12.3.12, which can be considered invalid). Am I on the right track? or are there easier ways to get through this? I've also read about the strtok function, will it be helpful here? That function is still quite vague to me, though.
EDIT:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
double HUGE_VAL= 1000000;
void string_cleaner (char *dirty){
int i=0;
while(1){
if (dirty[i]=='\n'){
dirty[i]='\0';
break;
}
i++;
}
}
int doubleable2(const char *str)
{
char *end_ptr;
double result;
result = strtod(str, &end_ptr);
if (result == HUGE_VAL || result == 0 && end_ptr == str)
return 0; // Could not be converted
if (end_ptr < str + strlen(str))
return 0; // Other input in the string after the number
return 1; // All of the string is part of the number
}
int main(){
char input [10];
double converted[10];
char *ptr;
int i;
for(i=0; i<5; i++){
while (1){
printf("Please enter:");
fgets(input, 10, stdin);
string_cleaner(input);
if (doubleable2(input)==0)
continue;
else if (doubleable2(input)==1)
break;
}
converted[i]=strtod(input, &ptr);
printf("%lf\n", converted[i]);
}
return 0;
}
thank you! It works just fine! I have a follow up question. If I enter a string that is too long, the program breaks. If I am to limit the input to, let's say, a maximum of 9 characters in input[], how am I to do that?
from what I understand about fgets(xx, size, stdin), it only gets up to size characters (including \n, \0), and then stores it to xx. In my program, I thought if I set it to 10, anything beyond 10 will not be considered. However, if I input a string that is too long, my program breaks.
You can indeed use strtod and check the returned value and the pointer given as the second argument:
int doubleable(const char *str)
{
const char *end_ptr;
double result;
result = strtod(str, &end_ptr);
if (result == HUGE_VAL || result == 0 && end_ptr == str)
return 0; // Could not be converted
if (end_ptr < str + strlen(str))
return 0; // Other input in the string after the number
return 1; // All of the string is part of the number
}
Note that you need to remove the newline that fgets most of the time adds to the string before calling this function.
From what I know, strtod can still convert it to 12.3 and the remaining will be stored to the pointer.
That is correct – see its man page:
If endptr is not NULL, a pointer to the character after the last character used in the conversion is stored in the location referenced by endptr.
So use that information!
#include <stdio.h>
#include <stdbool.h>
#include <errno.h>
bool doable (const char *buf)
{
char *endptr;
errno = 0;
if (!buf || (strtod (buf, &endptr) == 0 && errno))
return 0;
if (*endptr)
return 0;
return 1;
}
int main (void)
{
printf ("doable: %d\n", doable ("12.3"));
printf ("doable: %d\n", doable ("12.3a"));
printf ("doable: %d\n", doable ("abc"));
printf ("doable: %d\n", doable (NULL));
return 0;
}
results in
doable: 1
doable: 0
doable: 0
doable: 0
After accept answer
Using strtod() is the right approach, but it has some challenges
#include <ctype.h>
#include <stdlib.h>
int doubleable3(const char *str) {
if (str == NULL) {
return 0; // Test against NULL if desired.
}
char *end_ptr; // const char *end_ptr here is a problem in C for strtod()
double result = strtod(str, &end_ptr);
if (str == end_ptr) {
return 0; // No conversion
}
// Add this if code should accept trailing white-space like a \n
while (isspace((unsigned char) *endptr)) {
endptr++;
}
if (*end_ptr) {
return 0; // Text after the last converted character
}
// Result overflowed or maybe underflowed
// The underflow case is not defined to set errno - implementation defined.
// So let code accept all underflow cases
if (errno) {
if (fabs(result) == HUGE_VAL) {
return 0; // value too large
}
}
return 1; // Success
}
OP's code
No value with result == 0 in result == 0 && end_ptr == str. Simplify to end_ptr == str.
Instead of if (end_ptr < str + strlen(str)), a simple if (*end_ptr) is sufficient.
if (result == HUGE_VAL ... is a problem for 2 reasons. 1) When result == HUGE_VAL happens in 2 situations: A legitimate conversion and an overflow conversion. Need to test errno to tell the difference. 2) the test should be if (fabs(result) == HUGE_VAL ... to handle negative numbers.

Resources