2011-08-01 104 views
1

这是从Bjarne Stroustrup的“C++编程语言”我只想澄清一下他如何将数字累加到变量(int number_value)中。请不要撕毁代码,我没有写(完整的代码从第6章的底部帖子)。关于std :: cin的澄清:

特别是当解析器调用词法分析器时,词法分析器如何通过使用cin来构建数字。我相信答案是在这八行中,但我想解释它是如何工作的。

if(isalpha(ch)) { 
    (*input).putback(ch); 
    (*input) >> string_value; 
    return curr_tok=NAME; 
} else { 
    error("bad token "); 
    return curr_tok=PRINT; 
} 

这在我看来,get_token中第一次()被调用时,它把全expression_list为CIN或任何输入流点(内部的get_token())。

(*input) >> ch; 

我知道ch声明为char,但如果您键入123.4 + 5.432,会发生什么情况; (假设输入是cin),cin现在包含其流中包含的“字符串”123.4 + 5.432。然后我们转到lexer中的switch语句(get_token())。我假设::

ch == 1? 

在这一点上?接下来在switch语句中,我们将“通过”到“。”案件。在这里,我们将'1'放回到流中并将其写入number_value?

(*input).putback(ch); 
(*input) >> number_value; 

现在number_value = 1,我们返回解析器。由于我们找到了一个N​​UMBER,它再次调用get_token()。再次调用cin运算符< <。下一次调用(* input)>> number_value将2置入数值,覆盖1(假设输入仍然是123.4 + 5.432)?这里发生了什么。我想我需要更好地理解流如何工作。如果有人可以花时间,并给出一个简短的解释,并指出我一个很好的资源,我将不胜感激。

谢谢

马修Hoggan

对于那些没有书,代码:

#include <iostream> 
#include <stdlib.h> 
#include <string> 
#include <sstream> 
#include <map> 
#include <cctype> 

std::istream *input; 

double number_value; 
int no_of_errors; 
std::string string_value; 
std::map<std::string,double> table; 

enum Token_value { 
    NAME,  NUMBER,  END, 
    PLUS='+', MINUS='-',  MUL='*',  DIV='/', 
    PRINT=';', ASSIGN='=', LP='(',  RP=')' 
}; 

Token_value curr_tok=PRINT; 

double expr(bool); 
double term(bool); 
double prim(bool); 
Token_value get_token(); 
double error(std::string s) { 
    no_of_errors++; 
    std::cerr << "error: " << s << std::endl; 
    return 1.0; 
} 

Token_value get_token() { 
    char ch = 0; 
    (*input) >> ch; 

    switch(ch) { 
     case 0: { 
      return curr_tok=END; 
     } 
     case ';': 
     case '*': 
     case '/': 
     case '+': 
     case '-': 
     case '(': 
     case ')': 
     case '=': { 
      return curr_tok = static_cast<Token_value>(ch); 
     } 
     case '0': 
     case '1': 
     case '2': 
     case '3': 
     case '4': 
     case '5': 
     case '6': 
     case '7': 
     case '8': 
     case '9': 
     case '.': { 
      (*input).putback(ch); 
      (*input) >> number_value; 
      return curr_tok=NUMBER; 
     } 
     default: { 
      if(isalpha(ch)) { 
       (*input).putback(ch); 
       (*input) >> string_value; 
       return curr_tok=NAME; 
      } else { 
       error("bad token "); 
       return curr_tok=PRINT; 
      } 
     } 
    } 
} 

int main(int argc, char *argv[ ]) { 
    switch(argc) { 
     case 1: { 
      input = &std::cin; 
      break; 
     } 
     case 2: { 
      input = new std::istringstream(argv[1]); 
      break; 
     } 
     default: { 
      error(" To many arguments"); 
      return 1; 
     } 
    } 
    table["pi"] = 3.1415926535897932385; 
    table["e"] = 2.7182818284590452354; 

    while((*input)) { 
     get_token(); 
     if(curr_tok == END) { 
      break; 
     } 
     if(curr_tok == PRINT) { 
      continue; 
     } 
     std::cout << expr(false) << std::endl; 
    } 

    if(input != &std::cin) { 
     delete input; 
    } 

    return 0; 
} 

double expr(bool get) { 
    double left = term(get); 

    for(; ;) { 
     switch(curr_tok) { 
      case PLUS: { 
       left += term(true); 
       break; 
      } 
      case MINUS: { 
       left -= term(true); 
       break; 
      } 
      default: { 
       return left; 
      } 
     } 
    } 
} 

double term(bool get) { 
    double left = prim(get); 
    for(; ;) { 
     switch(curr_tok) { 
      case MUL: { 
       left *= prim(true); 
       break; 
      } 
      case DIV: { 
       if(double d = prim(true)) { 
        left /= d; 
        break;     
       } 
       else { 
        return error("divide by 0"); 
       } 
      } 
      default: { 
       return left; 
      } 
     } 
    } 
} 

double prim(bool get) { 
    if(get) { 
     get_token(); 
    } 

    switch(curr_tok) { 
     case NUMBER: { 
      double v = number_value; 
      get_token(); 
      return v; 
     } 
     case NAME: { 
      double &v = table[string_value]; 
      if(get_token() == ASSIGN) { 
       v = expr(true); 
       return v; 
      } 
     } 
     case MINUS: { 
      return -prim(true); 
     } 
     case LP: { 
      double e = expr(true); 
      if(curr_tok != RP) { 
       return error("')' expected"); 
      } 
      get_token(); 
      return e; 
     } 
     default: { 
      return error("primary expected"); 
     } 
    } 
} 

回答

2

“特技”是由以下三行的不同行为引起正确的类型。

字符串超载全局operator>>函数为字符串提供版本,并且此版本使用空格作为分隔符(如果需要在字符串中输入空格,则应查看getline)。

该双重版本使用istream& operator>> (double& val);成员函数,只读取字符,但它们在形成双精度值时有意义。

因此,假设您输入abc。代码cin >> ch将使用字符'a'填充ch,将其从输入流中移除。您将在默认情况下使用isapha来检测此情况,因为它与任何其他情况都不匹配。

在这一点上,你把这个角色'a'回输入流,因此您可以重新读取它,并执行cin >> string_value它得到整个字符串abc单个字符。

同样,如果您输入3.14159,这将是由case '3'支票夹,字符将被上推回输入流,然后cin >> number_value将获得全部价值。

2

现在NUMBER_VALUE = 1,我们回到解析器。

(*input) >> number_value;读入整个double,即123.4,因为number_value是double类型的。除此之外,你是对的。

char ch;     std::cin >> ch; 
std::string string_value; std::cin >> string_value; 
double number_value;  std::cin >> number_value; 

第一只是得到一个字符,第二和第三个获得多个人物打造的一个变量:

+0

所以它读取,直到它找到空白?我记得阅读>>阅读,直到它发现空白在哪里(cin.get(ch)&&!isalpha(ch)){;}会读直到找到第一个非数字? –

+1

@Mthethew;否。流操作符将读取不符合正在读取的类型的第一个字符。 –