【问题标题】:Clarification on std::cin澄清 std::cin
【发布时间】:2011-08-01 02:06:13
【问题描述】:

这取自 Bjarne Stroustrup 的“C++ 编程语言”。我想澄清一下他如何将数字累积到变量(int number_value)中。请不要撕代码,不是我写的(帖子底部第 6 章的完整代码)。

特别是当解析器调用词法分析器时,词法分析器如何使用 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。然后我们转到词法分析器中的 switch 语句 (get_token())。我假设::

ch == 1?

此时?接下来在 switch 语句中,我们将“失败”到 '.'案子。这里我们将'1'放回流中并将其写入number_value?

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

现在 number_value = 1,我们返回解析器。由于我们找到了一个 NUMBER,它再次调用 get_token()。并再次调用 cin operator> number_value 的调用不会将 2 放入覆盖 1 的数值中(假设输入仍然是 123.4+5.432)吗?这里发生了什么。我想我需要更好地了解流的工作原理。如果有人能花时间,给我一个简短的解释,并指出一个好的资源,我将不胜感激。

谢谢,

马修·霍根

没有书的,代码是:

#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" );
        }
    }
}

【问题讨论】:

    标签: c++ stream


    【解决方案1】:

    'trick' 是由以下三行的不同行为引起的:

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

    第一个只获取一个字符,第二个和第三个获取多个个字符以构建正确类型的变量。

    字符串重载了全局operator&gt;&gt;函数,为字符串提供了一个版本,这个版本使用空格作为分隔符(如果你的字符串需要输入空格,你应该查看getline)。

    double 版本使用 istream&amp; operator&gt;&gt; (double&amp; val); 成员函数,并且只会在字符形成 double 值时读取字符。

    所以,假设您输入abc。代码cin &gt;&gt; ch 将使用字符'a' 填充ch,将其从输入流中删除。然后,您将在默认情况下使用isapha 检测到这一点,因为它与其他任何情况都不匹配。

    此时,您将该字符 'a' 推回输入流,以便您可以重新读取它,并执行 cin &gt;&gt; string_value 得到整个字符串 abc不是一个字符。

    同样,如果您输入3.14159,它会被case '3' 检查捕获,该字符将被推回输入流,然后cin &gt;&gt; number_value 将获得整个值。

    【讨论】:

      【解决方案2】:

      现在 number_value = 1,我们返回解析器。

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

      【讨论】:

      • 所以它会一直读取直到找到空白?我记得读过 >> 一直读到找到空白,而 while(cin.get(ch) && !isalpha(ch)) { ;} 会一直读到找到第一个非数字?
      • @Matthew;不会。流操作符将读取到第一个不适合正在读取的类型的字符。
      猜你喜欢
      • 2015-08-08
      • 1970-01-01
      • 2023-03-23
      • 2021-08-04
      • 1970-01-01
      • 2011-06-12
      • 2011-10-12
      • 1970-01-01
      相关资源
      最近更新 更多