Reputation: 1034
I have a problem where I need to get input from the user (command line), and it will either be in the format [char char char] or [char string], ie 3 chars or a char and a string.
I need the chars and string on their own with no white space etc, and it all has to be input on one line.
My current solution only works for 3 chars, but I am unsure of how to get it to work for both 3 chars or a char and a string.
This is my current code:
char move[3];
while(1){
int i = scanf(" %c %c %c", &move[0], &move[1], &move[2]);
if(i == 3){
break;
}
}
If someone knows of how I can achieve what I want, I would be very grateful.
Upvotes: 0
Views: 245
Reputation: 123468
You could use a state machine to analyze and parse your input. You define some number of states you can be in at any point while reading the input, and the transitions between states based on the input. For example:
I womped up somewhat quick-n-dirty implementation, which handles various inputs as follows:
[fbgo448@n9dvap997]~/prototypes/state: ./state
a
..^
Bad input found at position 3
a b
....^
Bad input found at position 5
a b c
Read moves: a b c
a b c d
......^
Bad input found at position 7
a string
Read move: a string
a string c
.........^
Bad input found at position 10
a stringthatstoolongforourstringbuffer
......................^
Bad input found at position 23
some string
.^
Bad input found at position 2
a string followed by some junk
.........^
Bad input found at position 10
For your purposes, this is probably the definition of overkill and more involved than you need; consider a badly-written example of how you might want to handle stuff like this in the future:
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
/**
* State Input New State
* ----- ----- ---------
* exp_mv1 char + whitespace exp_mv2_or_str
* exp_mv2_or_str char + whitespace exp_mv3
* exp_mv2_or_str char seq + whitespace done
* done whitespace done
*
* Inputs that do not match the above lead to the error state.
*
*/
int getInput( const char *input, // input string
size_t inputSize, // length of input string
char *moves, // array to store moves
size_t movesLen, // length of moves array
char *str, // array to store string element
size_t strLen, // max length of string element array
size_t *movesRead, // number of moves read so far
int *count) // number of characters processed
{
enum state { exp_mv1, // expect a single char + whitespace
exp_mv2_or_str, // expect char + whitespace or string
exp_mv3, // expect char + whitespace
done, // expect only whitespace until newline
error } // found something unexpected
curState = exp_mv1; // start in exp_mv1
*count = 0;
while ( *count < inputSize && // process input while we are not
curState != error && // at the end of the string and
input[*count] && // we aren't in the error state
input[*count] != '\n' )
{
switch( curState )
{
case exp_mv1:
if ( !isspace( input[*count] ) ) // non-whitespace
{
if ( isspace( input[++(*count)] ) ) // followed by whitespace
{
moves[0] = input[((*count)++)-1]; // save the move
(*movesRead)++; // update moves counter
curState = exp_mv2_or_str; // go to new state
}
else
{
curState = error; // bad input
}
}
else
(*count)++; // read whitespace, move
break; // to next character
case exp_mv2_or_str:
if ( !isspace( input[*count] ) ) // non-whitespace character
{
if ( isspace( input[++(*count)] ) ) // followed by whitespace
{
moves[1] = input[((*count)++)-1]; // save the move
(*movesRead)++; // update moves counter
curState = exp_mv3; // go to new state
}
else
{
size_t i = 0; // non-whitespace
--(*count); // back up one character
while ( i < strLen && // read characters until we
!isspace( input[*count] ) ) // run out of space or hit
{ // a whitespace character
str[i++] = input[(*count)++]; // and save to str
}
str[i] = 0; // 0-terminate str
if ( isspace( input[*count] ) ) // if current character is
curState = done; // not whitespace, then the
else // string was longer than
{ // what we can store
(*count)--; // previous character was first bad character
curState = error; // go to error state
}
}
}
else
(*count)++; // skip over whitespace character
break;
case exp_mv3:
if ( !isspace( input[*count] ) ) // non-whitespace
{
if ( isspace( input[++(*count)] ) ) // followed by whitespace
{
moves[2] = input[((*count)++)-1]; // save the move
(*movesRead)++; // update the moves counter
curState = done; // go do done state
}
else
{
--count; // non-whitespace, previous char is bad character
curState = error; // go to error state
}
}
else
(*count)++; // skip whitespace character
break;
case done:
if ( !isspace( input[*count] ) ) // non-whitespace character
{
curState = error; // go to error state
}
else
(*count)++; // skip whitespace character
break;
case error: // no processing,
break;
}
}
return curState == done;
}
int main( void )
{
char input[81];
char moves[3];
char str[21];
size_t movesRead;
int charsRead;
while( fgets( input, sizeof input, stdin ) )
{
movesRead = 0;
if ( getInput( input, strlen( input ), moves, sizeof moves, str, sizeof str, &movesRead, &charsRead ) )
{
if ( movesRead == 3 )
printf( "Read moves: %c %c %c\n", moves[0], moves[1], moves[2] );
else
printf( "Read move: %c %s\n", moves[0], str );
}
else
{
const char *leader = ".....................................................................";
fprintf( stderr, "%*.*s^\n", charsRead, charsRead, leader );
fprintf( stderr, "Bad input found at position %d\n", charsRead+1 );
}
}
return 0;
}
I apologize for posting a novel - you caught me at a weird time.
Upvotes: -1
Reputation: 9708
A common way is to use something like this:
char move[80];
char* tok = NULL;
if (fgets(move, 80, stdin) != NULL) {
printf("Now you need to parse: %s\n", move);
/* then split into tokens using strtok */
tok = strtok(move, " ");
while (tok != NULL)
{
printf("Element: %s\n", tok);
tok = strtok(NULL, " ");
}
}
And then parse move using the C string handling functions. You can use strtok to get your 2+ tokens. If each element has strlen 1 then that is your 3 characters case and if first character is strlen 1 and second is 1+ length that is your second case.
Upvotes: 2