• VC++ 之 输入/输出类库(二)


      本节对cin,cout,cerr,clog,>>和<<(提取和插入运算符)的使用细节作进一步讨论。

    提高标准输入/输出的健壮性

    ◆ 1、标准设备输入使用要点

    • cin为缓冲流。键盘输入的数据保存在缓冲区中,当要提取时,是从缓冲区中拿。如果一次输入过多,会留在那儿慢慢用,如果输入错了,必须在回车之前修改,如果回车键按下就无法挽回了。只有把输入缓冲区中的数据取完后,才要求输入新的数据。不可能用刷新来清除缓冲区,所以不能输错,也不能多输!
    • 输入的数据类型必须与要提取的数据类型一致,否则出错。出错只是在流的状态字state(枚举类型io_state)中对应位置位(置1),程序继续。所以要提高健壮性,就必须在编程中加入对状态字state的判断。
    • 空格和回车都可以作为数据之间的分格符,所以多个数据可以在一行输入,也可以分行输入。但如果是字符型和字符串,则空格(ASCII码为32)无法用cin输入,字符串中也不能有空格。回车符也无法读入。
    • 输入数以后再输入字符或字符串:如果数后直接加回车,应该用cin.get()提取回车。如果还有空格,则要清空缓冲区。

    ◆ 2、程序运行状态
    状态字state为整型,其的各位在ios中说明:
    enum ios_state
    {
           goodbit=0x00, //流正常
           eofbit=0x01,    //输入流结束忽略后继提取操作;或文件结束已无数据可取
           failbit=0x02, //最近的I/O操作失败,流可恢复
           badbit=0x04, //最近的I/O操作非法,流可恢复
           hardfail=0x08     // I/O出现致命错误,流不可恢复,VC6.0++不支持
    }

    读取状态的有关操作如下:
    inline int ios::rdstate() const //读取状态字
    {return state;}

    inline int ios:operator!() const //可用操作符!()代替fail()
    {return state&(badbit|failbit);}

    inline int ios::bad() //返回非法操作位
    { return state & badbit;}

    inline void ios::clear(int _i) //人工设置状态,可用来清状态
    { lock();state=_i;unlock();}

    inline int ios::eof() const //返回流(文件)结束位
    {return state&eofbit;}

    inline int ios::fail() const //返回操作非法和操作失败这两位
    {return state&(badbit|failbit);}

    inline int ios::good() const //正常返回1,否则返回0
    {return state==0;}

      示例 1提高输入的健壮性。输入时需要故意输错,以测试健壮性。
    #include<iostream>
    using namespace std;
    int main(){
        char str[256];
        int i;
        cout<<"请输入整数:"<<endl;//强制清空缓冲区,保证输出,不会等缓冲区溢出再输出
        cin>>i;//可故意输入若干非数字字符,下次再输入若干字符加数字串加若干非数字字符进行检测
        while(cin.fail()){
            cout<<"状态字为:"<<cin.rdstate()<<endl;
            cin.clear(0);
            cin.getline(str,255);//读空缓冲区
            cout<<"输入错误,请重新输入整数"<<endl;
            cin>>i;
        }
        cin.getline(str,256);//读空缓冲区
        cout<<"请输入字符串"<<endl;
        cin.getline(str,255);//B行
        cout<<"输入整数为:"<<i<<endl;
        cout<<"输入字符串为:"<<str<<endl;
        return 0;
    }

    标准输入/输出成员函数

     1、输入流成员函数声明
    (1)字符输入:
        int istream::get();
        //提取一个字符,包括空格,制表,backspace和回车等,
        //与cin有所不同.注意返回为整型
        istream&istream::get(char &);
        istream&istream::get(unsigned char &);
    提取一个字符,放在字符型变量中

    (2)字符串输入:
        istream&istream::get(char *,int,char=’ ’);
        istream&istream::get(unsigned char *,int,char=’ ’);
        istream&istream::getline(char *,int,char=’ ’);
        istream&istream::getline(unsigned char *,int,char=’ ’);
    提取的串放在第一个参数为开始地址的存储区(不查边界);第二个参数为至多提取的字符个数(指定为n,最多取n-1个,再加一个字符串结束符);第三个参数为结束字符,遇此字符则结束,默认为回车换行符。

    get系列函数要求单独提取结束字符。getline提取字符串时如遇到指定结束符则提取该结束符,但不保存在串中。这两个函数都会在提取的一系列字符后加一个串结束符,返回值为对象本身(*this)。

    (3)其他函数:
    函数gcount()返回最后一次提取的字符数量,包括回车:
        int istream::gcount();

    函数ignore()读空(指定一个大的数量)缓冲区:
        istream&istream::ignore(int=1,int=EOF);
    第一个参数为要提取的字符数量,默认为1;第二个参数为结束字符,提取该结束字符,但对所提取的字符不保存不处理,作用是空读。第二个参数的默认值EOF为文件结束标志。

    在iostream中EOF定义为-1,在int get()函数中,读入输入流结束标志Ctrl+Z(^Z)时,函数返回EOF,为了能表示EOF的“-1”值,返回类型为int。采用cin.eof()函数,当前所读为EOF则返回非零,注意函数自身未从流中读取。

      示例2: ignore()和gcount()函数使用。
    #include<iostream>
    #include<cstring>
    using namespace std;
    
    int main(){
        char str[255];
        int i,n;
        cout<<"输入字符"<<endl;        //输入^Z,一旦输入^Z全部结束,不能输入其它字符
        i=cin.get();
        cout<<endl;
        n=cin.rdstate();                         //读取状态字
        cout<<"状态字为:"<<n<<endl;             //状态字为1,流结束
        cout<<"当输入字符时,取得的是:"<<i<<endl; //-1,输入^Z时,返回EOF,即-1
        if(n==0) cin.ignore(255,'
    ');           //清除多余的字符和回车符
        cin.clear(0);                            // A    使流恢复正常
        cout<<"输入字符串1:"<<endl;
        cin.getline(str,255);
        cout<<endl;
        cout<<"状态字为:"<<cin.rdstate()<<endl;
        i=cin.gcount();
        cout<<"字符串为:"<<str<<'	'<<"读入字符数为:"<<i<<'	';
        cout<<"串长为:"<<strlen(str)<<endl;
        cin.clear(0);                            // A    使流恢复正常
        cout<<"输入字符串2:"<<endl;
        cin.getline(str,255);
        cout<<endl;
        cout<<"状态字为:"<<cin.rdstate()<<endl;
        i=cin.gcount();
        cout<<"字符串为:"<<str<<'	'<<"读入字符数为:"<<i<<'	';
        cout<<"串长为:"<<strlen(str)<<endl;
        return 0;
    }

      2、输出流成员函数声明
        ostream&ostream::put(char);
        //输出参数字符
        ostream&ostream::put(unsigned char);
        ostream&ostream::put(signed char);
        ostream&ostream::flush();
        //刷新一个输出流,用于cout和clog

    重载插入和提取运算符

    重载必须保留原来的使用特性。重载只能在用户定义类中,将重载的运算符的函数说明为该类的友元函数:
        friend istream&operator>>(istream&,className&);
        friend ostream&operator<<(ostream&,className&);
    函数的返回值是对输入或输出流的引用,这是为了保证在cin和cout中可以连续使用“>>”或“<<”运算符,与所有“>>”和“<<”重载函数一致。第一个参数是输入或输出流的引用,作为“>>”或“<<”的左操作数;第二个参数为用户定义类的引用,作为右操作数,流用作函数参数,必须是引用调用,不能是传值调用。

      示例3 重载插入运算符“<<”
    #include<iostream>
    using namespace std;
    
    template <typename T>struct Node{
        T  key;
        // 其他域省略
    };//再次指出分号不可少
    template <typename T,int size>class Orderedlist{
        int maxsize;
        int last;
        Node<T> slist[size];
    public:
        Orderedlist(){last=-1;maxsize=size;}
        void BubbleSort();
        bool Insert(Node<T> & elem,int i);
        void print();
        // 无关成员函数省略
    };//再次指出分号不可少
    template <typename T,int size> bool Orderedlist<T,size>::Insert(Node<T> & elem ,int i){
        if (i<0||i>last+1||last==maxsize-1) return false;
        else{
            for (int j=last;j>i;j--) slist[j]=slist[j-1];
            slist[i]=elem;
            last++;
            return true;
        }
    }
    template <typename T,int size> void Orderedlist<T,size>::print(){
        int i;
        for(i=0;i<=last;i++){
            cout<<slist[i].key;
            if(i%5==4) cout<<endl;
        }
        cout<<endl;
    }
    template <typename T,int size> void Orderedlist<T,size>::BubbleSort(){
        bool noswap;
        int i,j;
        Node<T> temp;
        for (i=0;i<last;i++){//最多做n-1趟
            noswap=true;    //未交换标志为真
            for(j=last;j>i;j--){//从下往上冒泡
                if(slist[j].key<slist[j-1].key){
                    temp=slist[j];
                    slist[j]=slist[j-1];
                    slist[j-1]=temp;
                    noswap=false;
                }            
            }
            if(noswap) break;    //本趟无交换,则终止算法。
        }
    }
    
    class mystring{
        char str[20];
        int maxsize;
        int last;
    public:
        mystring(){
            last=-1;
            maxsize=20;
            str[0]='';
        }
        mystring(char *s){//本例为了简化,健壮性并不好
            last=-1;
            maxsize=20;
            do{
                last++;
                str[last]=s[last];
            }while(s[last]!='');
        }
        ~mystring(){}
        friend ostream & operator<<(ostream & ,const mystring &);//流类作为形式参数必须是引用
        bool operator<(mystring &);
        mystring & operator=(char * ms);
    };
    bool mystring::operator<(mystring & ms){//重载<运算符
        int i=0,k;
        do{
            k=str[i]-ms.str[i];
            i++;
        }while(k==0&&i<last&&i<ms.last);
        if(k<0) return true;
        if(i==last&&i!=ms.last) return true;
        return false;
    }
    ostream & operator<<(ostream & s,const mystring & cstr){
        return s<<cstr.str<<'	';
    }
    mystring & mystring::operator=(char * ms){
        last=-1;    
        do{
            last++;
            str[last]=ms[last];
        }while(ms[last]!=''&&last<maxsize-1);
        ms[last]='';
        return *this;
    }
    
    int main(){
        const int h=10;
        int i;
        Orderedlist<mystring,100> ordlist;
        char mslist[h][5]={"cat","book","car","zoo","fish","cab","dog","cap","fox","can"};
        Node<mystring> n[h];//定义结点数组
        for(i=0;i<h;i++)  n[i].key=mslist[i];// 结点数组赋值
        for(i=0;i<h;i++)  ordlist.Insert(n[i],i); //建立顺序表
        cout<<"未排序表:"<<endl;
        ordlist.print();
        ordlist.BubbleSort();
        cout<<"已排序表:"<<endl;
        ordlist.print();
        return 0;
    }
    示例 4 用户定义的复数类Complex的输入与输出。
    #include<iostream>
    using namespace std;
    
    class Complex{
        double Real,Image;
    public:
        Complex(double r=0.0, double i=0.0):Real(r),Image(i){}//定义构造函数
        friend ostream&operator<<(ostream&s,const Complex&z);
        friend istream&operator>>(istream&s,Complex&a);
    };
    ostream&operator<<(ostream&s,const Complex &z){     //流类作为形式参数必须是引用
        return s<<'('<<z.Real<<','<<z.Image<<')';
    }
    istream&operator>>(istream&s,Complex &a){//格式为d,(d),(d,d)
        double re=0,im=0;
        char c=0;
        s>>c;//是否由括号开始
        if(c=='('){
            s>>re>>c;//实部
            if(c==',')s>>im>>c;//虚部
            if(c!=')')s.clear(ios::failbit);//漏了括号给一个操作失败标志
        }
        else{
            s.putback(c);//无括号,返回一个字符到输入缓冲区
            s>>re;//实数
        }
        if(s)a=Complex(re,im);
        return s;
    }
    int main(){
        Complex a,b,c;
        cout<<"输入一个实数"<<endl;
        cin>>a;
        cout<<"输入一个用括号括起来的实数"<<endl;
        cin>>b;
        cout<<"输入一个用括号括起来复数"<<endl;
        cin>>c;
        cout<<"a="<<a<<'	'<<"b="<<b<<'	'<< "c="<<c<<'
    ';
        return 0;
    }
  • 相关阅读:
    Static了解和复习继承。
    复习篇1.对象和封装
    第一章笔记
    A + B Problem II
    Number Sequence
    Fibonacci Again
    8615 快乐
    8635 气球
    大牛之路II
    8617 阶乘数字和
  • 原文地址:https://www.cnblogs.com/delphi2014/p/4072950.html
Copyright © 2020-2023  润新知