Самое простое решение - использовать инструмент типа flex , чтобы сгенерировать ваш лексер и позволить ему разбить входные данные на токены (хотя flex ожидает, что его входные данные поступят изфайловый поток, а не массив символов).
strtok()
не является хорошим решением по нескольким причинам:
- Перезаписывает ввод, который вы можете сохранить дляиспользовать позже;
- Это инструмент грубой силы, который плохо обрабатывает плохо сформированный ввод;
- Если вы используете свои арифметические операторы в качестве разделителей токенов, то сами операторы будут помечены.
Обычное решение - написать конечный автомат (что в основном делает для вас flex).Вот пример очень quick-n-dirty (акцент на грязном):
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
/**
* Read from a string specified by source, updating the pointer as we go.
* We're assuming that token points to a buffer large enough to hold
* our largest token; ideally, you would want to pass the length of the
* target buffer and check against it, but I'm leaving it out for brevity.
*
* Tokens are either integers (strings of digits) or operators.
*
* Return 1 if we successfully read a token, 0 if we encountered an unexpected
* character, and EOF if the next character is the end of the input string.
*/
int getToken(char **source, char *token)
{
enum {START, DIGIT, ERROR, DONE} state = START;
size_t i = 0;
char *operators="+-*/";
if (**source == 0) // at end of input
return EOF;
while (**source != 0)
{
switch(state)
{
/**
* Initial state for this call.
*/
case START:
if (isdigit(**source))
{
state = DIGIT;
token[i++] = *(*source)++; // append the digit to the token
}
else if (strchr(operators, **source) != NULL)
{
state = DONE;
token[i++] = *(*source)++; // add the operator to the token
token[i++] = 0; // and terminate the string
}
else if (isspace(**source))
{
(*source)++; // ignore whitespace
}
else
{
/**
* We've read something that isn't a digit, operator, or
* whitespace; treating it as an error for now.
*/
state = ERR;
}
break;
/**
* We've read at least one digit.
*/
case DIGIT:
if (isdigit(**source))
{
token[i++] = *(*source)++; // append next digit to token
}
else
{
/**
* We've read a non-digit character; terminate the token
* and signal that we're done.
*/
token[i++] = 0;
state = DONE;
}
break;
case DONE:
return 1;
break;
case ERR:
return 0;
break;
}
}
return 1;
}
int main(int argc, char **argv)
{
char token[20];
char *input = argv[1];
for (;;)
{
int result = getToken(&input, token);
if (result == 1)
printf("%s\n", token);
else if (result == 0)
{
printf("Bad character '%c'; skipping\n", *input);
input++;
}
else if (result == EOF)
{
printf("done\n");
break;
}
}
return 0;
}
Почему (*source)++
вместо *source++
или source++
?Я не хочу обновлять source
, я хочу обновить то, что source
указывает на , поэтому я должен разыменовать указатель до применения ++
.Выражение *(*source)++
в основном переводится как «дайте мне значение символа, на которое указывает выражение *source
, затем обновите значение *source
».