输入函数

  读输入的部分通常都是程序里最麻烦的部分,这是因为程序必须与人交流,它必须处理人们的奇思怪想、各种习惯和看起来像是随机的错误。试图强求人按照某种更适合机器的方式活动通常被(正确地)认为是很令人讨厌的。低级输入例程的工作是读入字符,并由它们组合出高级的单词,这些单词随之成为高级例程的输入单位。在这里的低级输入由get_token()完成。写低级输入例程并不是每天都要做的工作,许多系统中提供了完成这些事情的标准函数。

  我将分两个阶段完成get_token()。首先我将提供一个不太靠得住的简单版本,它给用户强加了很大负担。而后,我要把它决定需要去组合的哪种单词,而后返回表示被读单词的Token_value值。

  初始化语句将第一个非空白字符读入ch,并检查该读入操作的成功完成:

    Token_value get_token()
    {
        char ch = 0;
        cin >> ch;

        switch(ch)
        case 0:
            return curr_tok = END;        // 赋值和返回
    }

按默认方式,运算符 >> 将跳过空白(即空格、制表符、换行符等),如果操作失败还将保持ch不变。因此,ch==0的情况就表示了输入的结束。

  赋值也是一个运算符,赋值表达式的结果就是赋给变量那个值。这使我能在同一个语句里将值END赋给curr_tok,并且返回它。在一个语句中做两件事对于维护很有帮助。如果赋值和返回在代码中分开做,程序员就可能会修改了一个而忘记另一个。

  在考虑整个函数之前,让我们先分开来看一些情况。表达式结束符';',括号和运算符的处理都很简单,只需要返回它们的值:

    case ';':
    caes '*':
    case '/':
    case '+':
    case '-':
    case '(':
    case ')':
    case '=':
        return curr_tok = Token_value(ch);

数可以按如下方式处理:

    case '0': case '1': case '2': case '3': case '4:'
    case '5': case '6': case '7': case '8': case '9':
    case '.':
        cin.putback(ch);
        cin >> number_value;
        return curr_tok = NUMBER;

按水平方式将case标号堆在一起而不是垂直地放置并不是什么好主意,因为这种安排更难阅读。然而,在每行中放一个数字也很令人生厌。由于运算符 >> 已经有针对将浮点常数读入double的定义,这里的代码就非常简单:首先把第一个字符(数字或者圆点)放回cin,而后将常数读到number_value里。

  名字的处理也与此类似:

    default:            // NAME, NAME =, 或者错误
        if(isalpha(ch))
        {
            cin.putback(ch);
            cin >> string_value;
            return curr_tok = NAME;
        }
        error("bad_token");
        return curr_tok = PRINT;

这里用了标准库函数isalpha()(20.4.2节),以避免将每个字母列为一个case标号。运算符 >> 用于字符串(这里是string_value)的结果是不断读入字符,直到遇到空白为止。这样,用户要想把某个名字作为运算符的运算对象,就必须用空白表示名字的结束。这一情况当然不太理想,我们将6.1.3节重新回到这个问题。

这里是最后的完整的输入函数:

    Token_value get_token()
    {
        char ch = 0;
        cin >> cin;
        switch(ch)
        {
        case 0:
            return curr_tok = END;
        case ';':
        case '*':
        case '/':
        case '+':
        case '-':
        case '(':
        case ')':
        case '=':
            return curr_tok = Token_value(ch);
        case '0': case '1': case '2': case '3': case '4':
        case '5': case '6': case '7': case '8': case '9':
        case '.':
            cin.putback(ch);
            cin >> number_value;
            return curr_tok = NUMBER;
        default:                // NAME, NAME=, 或者错误
            if(isalpha(ch))
            {
                cin.putback(ch);
                cin >> string_value;
                return curr_tok = NAME;
            }
            error("bad token");
            return curr_tok = PRINT;
        }
    }

由于前面将运算符的Token_value值定义成该运算符的整数值(4.8节),从运算符到对应的单词值的转换就非常简单了。

🔚