2015-01-12 2 views
0

Я хочу создать массив, в котором я могу сохранить введенное время выполнения, например - + 2 * 3 4/12 6 (всего 9 элементов здесь). Предположим, что ни один из входных экземпляров не требует более 50 индексов в массиве. Я думаю о целочисленном массиве для этой цели, но я не получаю никакого спецификатора формата, который может быть использован в scanf() для ввода ввода (завершен при возврате/Enter keypress), чтобы я мог отличить последнее в программе, был ли конкретный индекс char или int и какова его ценность.Хранение и получение комбинации символов и целых чисел в массиве

Если я использую %c или даже функцию getchar(), я сталкиваюсь с проблемой обработки целых чисел более одной цифры. если я использую %d символы, такие как * + -/не сохраняются. так далее и так далее.

Так что любезно предложите какой-либо способ сделать это, если это возможно.

+1

Вы спрашиваете 2 разные вопросы в одном: как разобрать в строки ввода и отдельных символов из цифр (первый вопрос в тексте) и как сохранить это в массиве (второй вопрос в заголовке). И вы не даете достаточно контекста для правильного ответа на первый вопрос. Что ** ** ** ваши спецификации для ввода? Только 4 оператора и десятичные целые числа? Любая многочленная функция ('inv' для 1/x или sqrt)? Любые круглые скобки ('' ')')? Любые десятичные числа (1.25)? Или это действительно обратный полис 4 операции целочисленного калькулятора? И вы принимаете несколько пробелов, вкладок, ...? –

+0

На самом деле я хочу реализовать что-то вроде префиксного анализатора выражений. В котором я не хочу использовать круглые скобки, я просто хочу различать операнды с использованием индекса массива. как и в моем примере 12, нужно сохранить во втором последнем индексе, а 6 - в последнем индексе массива. Теперь я просто рассматриваю целые числа, но было бы здорово, если бы вы могли предложить способ для чисел с плавающей запятой. да, я использую только 5 операторов. + - */и% –

ответ

1

Когда я узнал, программирование (давно ...) мой учитель сказал, что «никогда не начинает кода, пока не станет ясно, что вы хотите достичь с правильным анализом ». Если я правильно понял, вы строите калькулятор, только пяти операторов (+ -/*%), который следует следующей грамматика:

expr : number 
    | operator expr expr 

with the following lexical tokens : 

operator: single character among +-*/% 
number: consecutive sequence of decimal digits ([0-9]*) 
not printing characters (space, tab, \r, \n) are used as delimiters or otherwise ignored 

any other character causes an error. 

Ok: это текущие функции, если позже вы хотите использовать десятичное число просто нужно изменить определение числа, чтобы разрешить одну необязательную десятичную точку.

Написано так, что было бы легко использовать lex и yacc, но для такой простой грамматики это, безусловно, перебор.

Даже если мы определили пространство как разделитель, это не представляется возможным использовать scanf, чтобы получить жетоны, потому что он молча ест + признаков: +12 такие же, как 12.

Таким образом, вы должны построить простой лексера с помощью getc, который возвращает токены, а затем парсер, который рекурсивно вычисляет выражения. Нет необходимости хранить что-либо в массивах:

typedef struct _token { 
    enum {OPERATOR, INT, END, ERROR } type; 
    union { 
     int ival; 
     char op; 
    } value; 
} TOKEN; 

TOKEN getToken(FILE *fdin) { 
    static const char valid_op[] = "+-*/%"; 
    static const char spaces[] = " \t\r\n"; 
    int c; 
    TOKEN tok; 
    int val = 0; 
    int isval = 0; 
    while ((c = getc(fdin)) != EOF) { 
     if ((c >= '0') && (c <= '9')) { 
      val = 10 * val + (c - '0'); 
      isval = 1; 
     } 
     else if (isval != 0) { 
      tok.type = INT; 
      tok.value.ival = val; 
      ungetc(c, fdin); 
      return tok; 
     } 
     else if (strchr(valid_op, c)) { 
      tok.type = OPERATOR; 
      tok.value.op = c; 
      return tok; 
     } 
     else if (! strchr(spaces, c)) { 
      tok.type = ERROR; 
      return tok; 
     } 
    } 
    tok.type = END; 
    return tok; 
} 

int parse(FILE *fdin, int *typ) { 
    int i, j; 
    *typ = INT; 
    for(;;) { 
     TOKEN tok = getToken(fdin); 
     if (tok.type == INT) { 
      return tok.value.ival; 
     } 
     else if (tok.type == OPERATOR) { 
      i = parse(fdin, typ); 
      if (*typ != INT) { 
       *typ = ERROR; 
       return 0; 
      } 
      j = parse(fdin, typ); 
      if (*typ != INT) { 
       *typ = ERROR; 
       return 0; 
      } 
      switch(tok.value.op) { 
       case '+': return i+j; 
       case '-': return i-j; 
       case '*': return i*j; 
       case '/': return i/j; 
       case '%': return i * j/100; 
      } 
     } 
     else { 
      *typ = tok.type; 
      return 0; 
     } 
    } 
} 
0

Использование

  1. fgets() читать до конца строки.
  2. Разберите линию и проломите линию в токены, используя strtok() с пробелом в качестве разделителя.
  3. Проверьте, является ли каждый токен символом или цифрой. Существует несколько способов проверить, является ли токен цифрой или нет. Используйте strtol() для преобразования маркеров в целые числа, это поможет вам найти разницу между 0 и character также
+0

вы можете продемонстрировать это, используя какой-то фрагмент кода. как использовать fgets() в контексте stdin и fgets принимает указатель на массив символов в качестве входного аргумента, как его обрабатывать. –

+0

@DeepankarSingh Можете ли вы рассказать мне, что вы хотите делать с каждой точкой входа после pasring? – Gopi

+0

Я хочу выполнить арифметический расчет на основе символов (оператора) и целых чисел (операндов). –

1

Вы не нужен тип данных, который может содержать различные типы данных: операторы и целые числа, может быть, даже числа с плавающей точкой или имен переменных (или функций).

Общим подходом в C является использование union, который может содержать несколько типов в одном и том же пространстве. Вы можете использовать только один из этих типов за раз, поэтому вам нужно указать, какой из типов активен, что можно сделать с помощью enum. Затем оберните enum и union в struct, чтобы они были аккуратно рядом друг с другом.

Ниже приведен пример реализации такого типа данных.Он не выполняет никаких операций, он только анализирует строку и печатает токены.

Как и в вашем примере, все токены должны быть разделены пробелом, так что strtok может их найти. Если вы хотите распознать 5/2 в качестве трех токенов, вы можете построить лексер, как предложил Серж Балеста в его очень систематическом ответе. Нижеприведенная реализация не распознает отрицательные числа, такие как -1. Обработка ошибок также очень проста.

Этот код может еще служить вам в качестве отправной точки для решения:

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

enum Type {      /* enumeration of possible types */ 
    Operator,  
    Integer, 
    Float, 
    Name, 
    Illegal 
}; 

struct Token { 
    enum Type type;    /* token type */ 
    union {      /* mutually exclusive data fields */ 
     long long int l;  /* ... for Integer */ 
     double x;    /* ... for Float */ 
     char name[20];   /* ... for Name and Operator */ 
    } data; 
}; 

struct Token illegal(const char *str) 
{ 
    struct Token tk = {Illegal}; 

    snprintf(tk.data.name, 20, "%s", str); 
    return tk; 
} 

struct Token parse(const char *str) 
{ 
    struct Token tk = {Illegal}; 

    if (strchr("+-*/%", *str)) { 
     if (str[1]) return illegal("Overlong operator"); 
     tk.type = Operator; 
     strcpy(tk.data.name, str); 
     return tk; 
    } 

    if (isdigit(*str)) { 
     double x; 
     long long l; 
     char *end; 

     l = strtoll(str, &end, 0); 
     if (end != str && *end == '\0') { 
      tk.type = Integer; 
      tk.data.l = l;  
      return tk; 
     } 

     x = strtod(str, &end); 
     if (end != str && *end == '\0') { 
      tk.type = Float; 
      tk.data.x = x;  
      return tk; 
     } 

     return illegal("Illegal number"); 
    } 

    if (isalpha(*str)) { 
     const char *p = str; 

     while (*p) { 
      if (!isalnum(*p++)) return illegal("Illegal name"); 
     } 
     tk.type = Name; 
     snprintf(tk.data.name, 20, "%s", str);   
     return tk; 
    } 

    return illegal("Illegal character"); 
} 

int split(struct Token tk[], int max, char *str) 
{ 
    int n = 0; 
    char *p; 

    p = strtok(str, " \t\n"); 
    while (p) { 
     struct Token curr = parse(p); 

     if (curr.type == Illegal) { 
      fprintf(stderr, "Parse error: %s.\n", curr.data.name); 
      return -1; 
     } 

     if (n < max) tk[n] = curr; 
     n++; 

     p = strtok(NULL, " \t\n"); 
    } 

    return n; 
} 

void print(struct Token tk) 
{ 
    switch (tk.type) { 
    case Operator: printf("operator %c\n", tk.data.name[0]); 
        break; 
    case Integer: printf("integer %lld\n", tk.data.l); 
        break; 
    case Float:  printf("float %g\n", tk.data.x); 
        break; 
    case Name:  printf("name \"%s\"\n", tk.data.name); 
        break; 
    default:  printf("illegal token\n"); 
    } 
} 

int main() 
{ 
    char line[] = "- + 2 * alpha beta/12.0 6"; 
    struct Token tk[20]; 
    int i, n; 

    n = split(tk, 20, line); 
    for (i = 0; i < n; i++) { 
     print(tk[i]); 
    } 

    return 0; 
} 
Смежные вопросы