Маркировать строку в C? - PullRequest
       15

Маркировать строку в C?

3 голосов
/ 28 декабря 2010

Я работаю над анализатором терминалов для калькулятора, написанного на C. Я не могу понять, как объединить все числа между операторами, чтобы поместить их в массив.

Например,если бы вход (аргумент командной строки) был "4+342", в идеале это было бы input[] = {"4", "+", "342"}.

Вот мой код.Я включаю <stdio.h>, <stdlib.h> и <ctype.h>.

typedef char * string;

int main(int argc, char *argv[])
{
  string inputS = argv[1];
  string input[10];
  string temp;
  printf("%s\n", inputS);
  int i;
  int len = strlen(inputS);
  printf("parsed:\n");
  for(i = 0; i < len; inputS++, i++)
  { 
    if(isdigit(*inputS))
    {
      printf("%c",*inputS);
    }
    else
    {
      printf("\n%c\n",*inputS);
    }
  }
  printf("\n");
  return 0;
}

Если он работает с ./calc 4+5-546, он выдаст:

4
+
5
-
546

Так что жесамый простой способ получить каждую строку этого в свой собственный слот массива?

Ответы [ 6 ]

2 голосов
/ 28 декабря 2010

Самое простое решение - использовать инструмент типа 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».

2 голосов
/ 28 декабря 2010

Попробуйте для размера ...

#include <stdio.h>
#include <ctype.h>

typedef char * string;

int main(int argc, char *argv[])
{
    string inputS = argv[1];
    string input[50];   /* Up to 50 tokens */
    char   buffer[200];
    int    i;
    int    strnum = 0;
    char  *next = buffer;
    char   c;

    if (argc != 2)
    {
        fprintf(stderr, "Usage: %s expression\n", argv[0]);
        return 1;
    }

    printf("input: <<%s>>\n", inputS);
    printf("parsing:\n");

    while ((c = *inputS++) != '\0')
    { 
        input[strnum++] = next;
        if (isdigit(c))
        {
            printf("Digit: %c\n", c);
            *next++ = c;
            while (isdigit(*inputS))
            {
                c = *inputS++;
                printf("Digit: %c\n", c);
                *next++ = c;
            }
            *next++ = '\0';
        }
        else
        {
            printf("Non-digit: %c\n", c);
            *next++ = c;
            *next++ = '\0';
        }
    }

    printf("parsed:\n");
    for (i = 0; i < strnum; i++)
    {
        printf("%d: <<%s>>\n", i, input[i]);
    }

    return 0;
}

Учитывая, что программа называется tokenizer и команда:

tokenizer '(3+2)*564/((3+4)*2)'

Это дает мне вывод:

input: <<(3+2)*564/((3+4)*2)>>
parsing:
Non-digit: (
Digit: 3
Non-digit: +
Digit: 2
Non-digit: )
Non-digit: *
Digit: 5
Digit: 6
Digit: 4
Non-digit: /
Non-digit: (
Non-digit: (
Digit: 3
Non-digit: +
Digit: 4
Non-digit: )
Non-digit: *
Digit: 2
Non-digit: )
parsed:
0: <<(>>
1: <<3>>
2: <<+>>
3: <<2>>
4: <<)>>
5: <<*>>
6: <<564>>
7: <</>>
8: <<(>>
9: <<(>>
10: <<3>>
11: <<+>>
12: <<4>>
13: <<)>>
14: <<*>>
15: <<2>>
16: <<)>>
1 голос
/ 02 января 2011

strsep - хороший выбор здесь - возьмите токен и затем решите, что вы хотите с ним сделать ...

char * string = "(3+ (5 + 6) / 8)"

char токен;while ((token = strsep (& string, "(+ / )"))) {// Сохранить токен ... если это не (или) или пробел}

Здесь - токен будетобрабатывается аналогично Split () в Java / C #.Это искажает строку при ее обработке - однако с правильными разделителями - все будет хорошо:)

1 голос
/ 28 декабря 2010

-> MAN STRCAT

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main (int argc, const char **argv)
{
    char *toto_str = "Toto";
    char *is_str = "Is";
    char *awesome_str = "Awesome";
    char *final_str;
    size_t i;

    i = strlen(toto_str);
    i += strlen(is_str);
    i += strlen(awesome_str);

    final_str = malloc((i * sizeof(char)) + 1);
    strcat(final_str, toto_str);
    strcat(final_str, is_str);
    strcat(final_str, awesome_str);

    printf("%s", final_str);
    free(final_str);

    return 0;
}
0 голосов
/ 28 декабря 2010

это даст вам представление:

#include <stdio.h>
#include <string.h>
main(int argc, char *argv[])
{
    printf("\nargv[1]: %s",argv[1]);
    char *p;
    p = strtok(argv[1],"+");
    printf("\np: %s", p);
    p = strtok(NULL,"+");
    printf("\np: %s", p);
    p = strtok(NULL,"+");
    printf("\np: %s", p);
    printf("\n");
}

Это просто пример кода, демонстрирующий, как это делается с использованием только регистра сложений.
Получите основную идею этого кода и примените его в своем коде.
Пример вывода для этого:

./a.out 5+3+9

argv[1]: 5+3+9
p: 5
p: 3
p: 9

Опять же, я только демонстрирую знак "+". Возможно, вы захотите проверять p, пока оно не станет NULL, затем перейдите к следующей операции, скажем, вычитанию, затем умножению, затем делению.

0 голосов
/ 28 декабря 2010

Звучит так, будто вы хотите взглянуть на стандартную функцию strtok.

...