I am trying to figure out how to read input multiple ways. Input can look like this.
N:{-4,2,1}
E:{1,1,9}
W:{-2,5,3}
S:{7,1}
or like this
E:{9,1,1}N:{1,2,-4}W:{3,5,-2}S:{7,1}
I added into my code checking for word END. Its supposed to be working on EOF but on my windows machine it doesnt work. So input END to signal END of input. So far I managed to get them both working separatly... But I need them to both at the same time.
char *skip_whitespace(char *str)
{
//skip any leading whitespace characters
while (*str == ' ' || *str == '\t')
str ;
return str;
}
void read_tokens(int *north, int *west, int *east, int *south, int *north_size, int *west_size, int *east_size, int *south_size)
{
//buffer for reading in input
char buffer[MAX_TOKENS];
//read in the input line by line
while (fgets(buffer, MAX_TOKENS, stdin) != NULL)
{
//remove the newline character from the end of the line
buffer[strcspn(buffer, "\n")] = 0;
//check for the "END" string to end the input
if (strcmp(buffer, "END") == 0)
break;
//split the line at the curly brace character
char *direction_token = strtok(buffer, "{");
char *tokens = strtok(NULL, "}");
//get the direction
char direction = direction_token[0];
//skip any leading or trailing whitespace characters
tokens = skip_whitespace(tokens);
//split the tokens at each comma
char *token = strtok(tokens, ",");
//determine the direction and store the tokens in the appropriate array
if (direction == 'N')
{
while (token != NULL)
{
//skip any leading or trailing whitespace characters
token = skip_whitespace(token);
//store the token in the array
north[*north_size] = atoi(token);
(*north_size) ;
//find the next token
token = strtok(NULL, ",");
}
}
else if (direction == 'W')
{
while (token != NULL)
{
//skip any leading or trailing whitespace characters
token = skip_whitespace(token);
//store the token in the array
west[*west_size] = atoi(token);
(*west_size) ;
//find the next token
token = strtok(NULL, ",");
}
}
else if (direction == 'E')
{
while (token != NULL)
{
//skip any leading or trailing whitespace characters
token = skip_whitespace(token);
//store the token in the array
east[*east_size] = atoi(token);
(*east_size) ;
//find the next token
token = strtok(NULL, ",");
}
}
else if (direction == 'S')
{
while (token != NULL)
{
//skip any leading or trailing whitespace characters
token = skip_whitespace(token);
//store the token in the array
south[*south_size] = atoi(token);
(*south_size) ;
//find the next token
token = strtok(NULL, ",");
}
}
else
{
//invalid direction = error
printf("Nespravny vstup.\n");
}
}
}
Here is the main function. For anyone intrested here is calling and printing.
int main(void)
{
//field for token values
int north[MAX_TOKENS], west[MAX_TOKENS], east[MAX_TOKENS], south[MAX_TOKENS];
//sizes of token value fields
int north_size = 0, west_size = 0, east_size = 0, south_size = 0;
printf("Input:\n");
//fetch token values from input
read_tokens(north, west, east, south, &north_size, &west_size, &east_size, &south_size);
printf("N: { ");
for (int i = 0; i < north_size; i )
printf("%d, ", north[i]);
printf("}\n");
printf("W: { ");
for (int i = 0; i < west_size; i )
printf("%d, ", west[i]);
printf("}\n");
printf("E: { ");
for (int i = 0; i < east_size; i )
printf("%d, ", east[i]);
printf("}\n");
printf("S: { ");
for (int i = 0; i < south_size; i )
printf("%d, ", south[i]);
printf("}\n");
}
CodePudding user response:
You have an input format that permits optional whitespace, including newlines, between tokens. Your two examples differ only in that one makes use of that option and the other doesn't. In the most basic terms, then, the solution is to make your parser ignore (all) whitespace between tokens, too. Such a parser handles both forms of input presented, and other variants, too.
I do think, however, that fgets()
is more a liability here than a help. Your input is not fundamentally line-oriented, and with fgets()
you need to (but do not presently) watch out for and handle cases where a long line is split over two or more reads. I suggest tokenizing the input directly from the stream instead of first reading it into an intermediate buffer.
I know that scanf()
gets a lot of hate, but it might serve your purpose pretty well here. It already knows how to skip whitespace, to recognize integers, and to match specific characters. Something along these lines, maybe:
while (1) {
char direction;
char delim;
int result;
// scan first part:
result = scanf(" %c : %c", &direction, &delim);
if (result == EOF) {
// end of file
} else if (result != 2 || !is_valid_direction(direction) || delim != '{') {
// invalid input ...
// ... or maybe (part of) an "END" keyword if you decide to
// go ahead with that
}
// ... handle direction code ...
do {
// scan number
int num;
result = scanf("%d %c", &num, &delim);
if (result != 2 || (delim != ',' && delim != '}')) {
// invalid input ...
// unless empty number lists are allowed: N:{}
}
// store number ...
} while (delim == ',');
}
If you want to avoid scanf()
, then you can do basically the same thing by reading one character at a time via getchar()
or fgetc()
. Or, yes, with fgets()
too, provided you exercise sufficient care.
That's schematic, of course, not a full implementation of the needed parser