I have to input a string to make the second if condition false and print "Correct!". Can it be done?
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
if(argc != 2) { // argc is how long argv is, including that path-to-self
puts("You need to give me exactly one argument!");
return -1;
}
// argv[1] represents the command line input you have given to the program. Which is a string or a char array.
if(argv[1][0] != ~'t' || argv[1][1] != ~'h' || argv[1][2] != ~'e') {
puts("noooo!");
return 0;
}
else printf("Correct!\n");
return 0;
}
If an argument is passed to the program (i.e. the first argc != 2 check doesn't fail) then the expression argv[1][0] != ~'t' will always be true (as well as the other checks but those won't be made because of short-circuit evaluation).
The expression ~'t' will first be converted to an int (see this reference for why, the same thing will happen to argv[1][0]). That means you will have ~0x00000074 (if using the ASCII alphabet and 32-bit int). That will evaluate as 0xffffff8b.
It doesn't matter what argument you give your program, argv[1][0] will never be equal to 0xffffff8b and so the condition is true.
Related
Hi folks thanks in advance for any help, I'm doing the CS50 course i'm at the very beginning of programming.
I'm trying to check if the string from the main function parameter string argv[] is indeed a number, I searched multiple ways.
I found in another topic How can I check if a string has special characters in C++ effectively?, on the solution posted by the user Jerry Coffin:
char junk;
if (sscanf(str, "%*[A-Za-z0-9_]%c", &junk))
/* it has at least one "special" character
else
/* no special characters */
if seems to me it may work for what I'm trying to do, I'm not familiar with the sscanf function, I'm having a hard time, to integrate and adapt to my code, I came this far I can't understand the logic of my mistake:
#include <cs50.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
int numCheck(string[]);
int main(int argc, string argv[]) {
//Function to check for user "cooperation"
int key = numCheck(argv);
}
int numCheck(string input[]) {
int i = 0;
char junk;
bool usrCooperation = true;
//check for user "cooperation" check that key isn't a letter or special sign
while (input[i] != NULL) {
if (sscanf(*input, "%*[A-Za-z_]%c", &junk)) {
printf("test fail");
usrCooperation = false;
} else {
printf("test pass");
}
i++;
}
return 0;
}
check if the string from the main function parameter string argv[] is indeed a number
A direct way to test if the string converts to an int is to use strtol(). This nicely handles "123", "-123", "+123", "1234567890123", "x", "123x", "".
int numCheck(const char *s) {
char *endptr;
errno = 0; // Clear error indicator
long num = strtol(s, &endptr, 0);
if (s == endptr) return 0; // no conversion
if (*endptr) return 0; // Junk after the number
if (errno) return 0; // Overflow
if (num > INT_MAX || num < INT_MIN) return 0; // int Overflow
return 1; // Success
}
int main(int argc, string argv[]) {
// Call each arg[] starting with `argv[1]`
for (int a = 1; a < argc; a++) {
int success = numCheck(argv[a]);
printf("test %s\n", success ? "pass" : "fail");
}
}
sscanf(*input, "%*[A-Za-z_]%c", &junk) is the wrong approach for testing numerical conversion.
You pass argv to numcheck and test all strings in it: this is incorrect as argv[0] is the name of the running executable, so you should skip this argument. Note also that you should pass input[i] to sscanf(), not *input.
Furthermore, lets analyze the return value of sscanf(input[i], "%*[A-Za-z_]%c", &junk):
it returns EOF if the input string is empty,
it returns 0 if %*[A-Za-z_] fails,
it also returns 0 if the conversion %c fails after the %*[A-Za-z_] succeeds,
it returns 1 is both conversions succeed.
This test is insufficient to check for non digits in the string, it does not actually give useful information: the return value will be 0 for the string "1" and also for the string "a"...
sscanf() is very tricky, full of quirks and traps. Definitely not the right tool for pattern matching.
If the goal is to check that the strings contain only digits (at least one), use this instead, using the often overlooked standard function strspn():
#include <stdio.h>
#include <string.h>
int numCheck(char *input[]) {
int i;
int usrCooperation = 1;
//check for user "cooperation" check that key isn't a letter or special sign
for (i = 1; input[i] != NULL; i++) {
// count the number of matching character at the beginning of the string
int ndigits = strspn(input[i], "0123456789");
// check for at least 1 digit and no characters after the digits
if (ndigits > 0 && input[i][ndigits] == '\0') {
printf("test passes: %d digits\n", ndigits);
} else {
printf("test fails\n");
usrCooperation = 0;
}
}
return usrCooperation;
}
Let's try this again:
This is still your problem:
if (sscanf(*input, "%*[A-Za-z_]%c", &junk))
but not for the reason I originally said - *input is equal to input[0]. What you want to have there is
if ( sscanf( input[i], "%*[A-Za-z_]%c", &junk ) )
what you're doing is cycling through all your command line arguments in the while loop:
while( input[i] != NULL )
but you're only actually testing input[0].
So, quick primer on sscanf:
The first argument (input) is the string you're scanning. The type of this argument needs to be char * (pointer to char). The string typedef name is an alias for char *. CS50 tries to paper over the grosser parts of C string handling and I/O and the string typedef is part of that, but it's unique to the CS50 course and not a part of the language. Beware.
The second argument is the format string. %[ and %c are format specifiers and tell sscanf what you're looking for in the string. %[ specifies a set of characters called a scanset - %[A-Za-z_] means "match any sequence of upper- and lowercase letters and underscores". The * in %*[A-Za-z_] means don't assign the result of the scan to an argument. %c matches any character.
Remaining arguments are the input items you want to store, and their type must match up with the format specifier. %[ expects its corresponding argument to have type char * and be the address of an array into which the input will be stored. %c expects its corresponding argument (in this case junk) to also have type char *, but it's expecting the address of a single char object.
sscanf returns the number of items successfully read and assigned - in this case, you're expecting the return value to be either 0 or 1 (because only junk gets assigned to).
Putting it all together,
sscanf( input, "%*[A-Za-z_]%c", &junk )
will read and discard characters from input up until it either sees the string terminator or a character that is not part of the scanset. If it sees a character that is not part of the scanset (such as a digit), that character gets written to junk and sscanf returns 1, which in this context is treated as "true". If it doesn't see any characters outside of the scanset, then nothing gets written to junk and sscanf returns 0, which is treated as "false".
EDIT
So, chqrlie pointed out a big error of mine - this test won't work as intended.
If there are no non-letter and non-underscore characters in input[i], then nothing gets assigned to junk and sscanf returns 0 (nothing assigned). If input[i] starts with a letter or underscore but contains a non-letter or non-underscore character later on, that bad character will be converted and assigned to junk and sscanf will return 1.
So far so good, that's what you want to happen. But...
If input[i] starts with a non-letter or non-underscore character, then you have a matching failure and sscanf bails out, returning 0. So it will erroneously match a bad input.
Frankly, this is not a very good way to test for the presence of "bad" characters.
A potentially better way would be to use something like this:
while ( input[i] )
{
bool good = true;
/**
* Cycle through each character in input[i] and
* check to see if it's a letter or an underscore;
* if it isn't, we set good to false and break out of
* the loop.
*/
for ( char *c = input[i]; *c; c++ )
{
if ( !isalpha( *c ) && *c != '_' )
{
good = false;
break;
}
}
if ( !good )
{
puts( "test fails" );
usrCooperation = 0;
}
else
{
puts( "test passes" );
}
}
I followed the solution by the user "chux - Reinstate Monica". thaks everybody for helping me solve this problem. Here is my final program, maybe it can help another learner in the future. I decided to avoid using the non standard library "cs50.h".
//#include <cs50.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
void keyCheck(int);
int numCheck(char*);
int main(int argc, char* argv[])
{
//Error code == 1;
int key = 0;
keyCheck(argc); //check that two parameters where sent to main.
key = numCheck(argv[1]); //Check for user "cooperation".
return 0;
}
//check for that main received two parameters.
void keyCheck(int key)
{
if (key != 2) //check that main argc only has two parameter. if not terminate program.
{
exit(1);
}
}
//check that the key (main parameter (argv [])) is a valid number.
int numCheck(char* input)
{
char* endptr;
errno = 0;
long num = strtol(input, &endptr, 0);
if (input == endptr) //no conversion is possible.
{
printf("Error: No conversion possible");
return 1;
}
else if (errno == ERANGE) //Input out of range
{
printf("Error: Input out of range");
return 1;
}
else if (*endptr) //Junk after numeric text
{
printf("Error: data after main parameter");
return 1;
}
else //conversion succesfull
{
//verify that the long int is in the integer limits.
if (num >= INT_MIN && num <= INT_MAX)
{
return num;
}
//if the main parameter is bigger than an int, terminate program
else
{
printf("Error key out of integer limits");
exit(1);
}
}
/* else
{
printf("Success: %ld", num);
return num;
} */
}
I am writing a C program and I have to read parameters by command line.
How can I check if the argument passed to my program is a string (that is to say an array of characters) or an integer?
Is there any immediate call I can use in C?
You can call isdigit() on each character of the string, and if it's true for all characters you have an integer, otherwise it's some alphanumeric string.
You can also call strtol to parse the string as an integer. The second argument returns a pointer to the first non-numeric character in the string. If it points to the first character, it's not an integer. If it points to the end, it's an integer. If it points somewhere in the middle, it's an integer followed by a sequence of non-numeric characters.
Parameters passed by command line are always strings, if you want to check if this string can be converted to integer you can use strtol:
char *ptr = argv[1];
long num;
num = strtol(ptr, &ptr, 10);
if (*ptr == '\0')
/* arg is a number */
else
/* arg is NOT a number */
How can I check if the argument passed to my program is a string (that is to say an array of characters) or an integer?
Command line arguments are always passed to a C program as strings. It's up to you to figure out whether an argument represents a number or not.
int is_number(char const* arg)
{
// Add the logic to check whether arg is a number
// The code here can be simple or complex depending on the
// level of checking that is necessary.
// Should we return true or false if the argument is "1abc"?
// This is a very simple test.
int n;
return (sscanf(arg, "%d", &n) == 1);
}
int main(int argc, char** argv) // argv is array of strings.
{
int i = 0;
for ( i = 1; i < argc; ++i )
{
if ( is_number(argv[i]) )
{
// Use the argument.
}
}
}
You can try to check whether all string characters fall in the range of numbers from 0-9.check this program for instance :
#include <stdio.h>
int checkString(char *Str)
{
char *ptr = Str;
while( *ptr )
{
// check if string characters are within the range of numbers
if( ! (*ptr >= 0x30 && *ptr <= 0x39 ) )
{
return 0;
}
ptr++;
}
return 1;
}
int main(int argc,char *argv[])
{
// does argv[1] consist entirely of numbers ?
if( checkString(argv[1]) )
{
/* if it does , do something */
puts("success");
}
else
{
/* do something else */
}
return 0;
}
So I have this program that takes in PID input and a character.
$ ./transmit 1111 a
My question is. If it were
$ ./transmit 111a x
since PID are all numbers I'd need to catch that.
given:
char *a[] = {"./transmit", "111a", "x"};
how would I check if "111a" is numeric only? isdigit only works if it's a character. Do I have to loop through the entire input?
char *err;
unsigned long pid = strtoul(argv[1], &err, 10);
if (*err || err == argv[1])
error();
if ((pid_t)pid != pid || (pid_t)pid <= 0)
error();
When you are really pedantic, you can check for ULONG_MAX and errno == ERANGE too, but because pid_t is smaller than unsigned long, this will be catched by the second check already.
You could maybe use the strspn() function:
#include <stdio.h>
#include <string.h>
int main(int argc, char* argv[]) {
if (argc > 1) {
if (argv[1][strspn(argv[1], "0123456789")] == '\0') {
puts("Yes, the first command line argument is all numeric.");
}
else {
puts("No, the first command line argument is not all numeric.");
}
}
else {
puts("Please provide an argument on the command line");
}
return 0;
}
What is the proper way to receive a command line argument from the user and compare it to a single char? For example if the user typed "Y" for yes run some function and "N" for no would run another function.
My main error is "comparison between ptr and integer" & "too few arguments to function call, expected 3 have 1"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void calculate();
void verify();
int main (int argc, char *argv[]) {
if(argc < 2){
printf("Please enter a mode of operation.\n");
return 0;
}
else if(argc > 2){
printf("Too many arguments supplied. Please try again.\n");
return 0;
}
else if(strncmp(argv[1] == 'c') == 0)
calculate();
else if(strncmp(argv[1] == 'v') == 0)
verify();
return 0;
}
void calculate(){
}
void verify(){
}
You're not calling strncmp() correctly. It should be:
strncmp(argv[1], "c", 1)
It takes 3 arguments: two strings and a limit. 'c' is a char, not a string, string literals are put in double quotes.
You can also write simply:
if (argv[1][0] == 'c')
This just checks the first character, so the user can type y or yes and they'll match y. If you want to match the whole argument and require it be just a single character, you should use strcmp() rather than strncmp():
if (strcmp(argv[1], "c") == 0)
This
strncmp(argv[1] == 'c') == 0
just is incorrect syntax. For starters the function strncmp requires three arguments and it deals with strings while in the avove call of the function there is supplied only one argument of type int. because the wrong expression argv[1] == 'c' has type int and is equal to 0.
I can suggest the following solution
else if ( argc > 2 || strlen( argv[1] ) != 1 ){
printf("Too many arguments supplied. Please try again.\n");
return 0;
}
else if ( argv[1][0] == 'c' )
calculateCRC();
else if ( argv[1][0] == 'v' )
verifyCRC();
I am getting input as argv, argc and I want to return TRUE of FALSE according to the input. My code is :
int is_valid_regexp(const char* regexp);
int main(int argc, char *argv[])
{
if (!is_valid_regexp(argv[1])){
fprintf(stderr, "wrong regular expression format:%s", argv[1]);
return(EXIT_FAILURE):
}
return (EXIT_SUCCESS);
}
int is_valid_regexp(const char* regexp)
{
do{
if(*regexp == '\\')
return FALSE;
}while (regexp++ != '\0');
return TRUE;
}
The program executes and then stops working. I think something is wrong with if(*regexp == '\\') return FALSE - the program works well if I exclude that part. Does it have something to do with pointer problem?
}while (regexp++ != '\0');
Here, regexp is a POINTER, which is equal to address of area of memory. regexp++ just shift right on one position this pointer. As type of pointer is size_t (unsigned __int32) you go outside your string.
Use
`}while (*regexp++ != '\0');`
to solve your problem.