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" );
}
}