zoukankan      html  css  js  c++  java
  • C++_IO与文件3-用cin进行输入

    接下来讨论的是如何给程序提供数据

    cin对象将标准输入表示为字节流

    通常情况下是通过键盘来生成这种字节流

    cin对象根据接收值得变量类型,使用其方法将字符序列转换为所需的类型。

    cin>>value_holder;

    cin解释输入的方式取决于value_holder的数据类型;value_holder为存储输入的内存单元;

    istream类重载了抽取运算符>>,使之能够识别下面这些基本类型;

    signed char &; unsigned char &;

    char &; short &; ...

    典型的运算符函数的原型如下:

    istream & operator>>(int &);

    参数和返回值都是引用。引用参数意味着下面这样的语句将导致operator>>()函数处理变量staff_size本身,而不是像常规参数那样处理它的副本;

    cin >> staff_size;

    每个抽取运算符都返回调用对象的引用,这使得能够将输入拼接起来,就像拼接输出那样:

    char name[20];

    float fee;

    int group;

    cin>>name>>fee>>group;

    =====================================================

    一、cin>>如何检查输入

    不同版本的抽取运算符查看输入流的方法是相同的。

    它们跳过空白,直到遇到非空白字符。

    空白字符(空格、换行符、制表符);

    例如对于下面的代码:

    int elevation;

    cin >>elevation;

    假设输入下面的字符:

    -123Z

    运算符将读取字符 - 、1、2和3;因为它们都是整数的有效部分;但Z不是有效字符。

    因此输入中最后一个可接受的字符是3。Z将留在输入流中。下一个cin语句将从这里开始读取。

    与此同时,运算符将字符序列-123转换为一个整数值,并将它赋给elevation。

    输入有时候并不能满足要求,例如输入的是Zcar,而不是-123Z。在这种情况下,抽取运算符将不会修改elevation的值,并返回0;

    返回值false让程序能够检查输入是否满足要求。

    //check_it.cpp  -- checking for valid input
    #include<iostream>  
    
    
    int main()
    {
        using namespace std;
        cout<<"Enter numbers: ";
        
        int sum = 0;
        int input;
        
        while(cin>>input)
        {
            sum += input;
        }
        
        cout<<"Last value entered = "<<input<<endl;
        cout<<"Sum = "<< sum <<endl;
        return 0;
    }

    运行结果

    Enter numbers: 200
    10 -50 -123Z 60
    Last value entered = -123
    Sum = 37

    分析

    由于输入是被缓冲的,因此通过键盘输入的第二行在用户按下回车键之前,是不会被发送给程序的。

    然而,循环在字符Z处停止了对输入的处理,因此它不与任何一种浮点格式匹配。

    输入与预期不匹配反过来讲导致表达式cin>>input的结果为false,因此while循环被终止。

    =====================================================

    二、流状态

    接下来进一步讨论不适当的输入会带来什么后果;

    cin或cout对象包含一个描述流状态(stream state)的数据成员。

    流状态有3个ios_base元素组成:eofbit、badbit、failbit;

    流状态被定义为iostate类型,是一种bitmask类型;

    当全部3种状态被设置为0时,说明一切顺利;

    当cin操作到达文件末尾时,它将设置eofbit;

    当cin操作未能读到预期的字符时,它将设置为failbit;

    在一些无法诊断的失败破坏流时,badbit元素将被设置failbit;

    1、设置状态

    下面介绍两种方法——clear()setstate()很相似;

    clear();               //这样的调用将使用默认参数0,这将清除全部3个状态位(eofbit、badbit、failbit);

    clear(eofbit);      //这将会设置eofbit位,同时另外两个状态位被清除;

    setstate()方法只影响其参数中已被设置的位。

    setstate(eofbit)   //只会影响eofbit,而不会影响其他位;

    为什么需要重新设置位状态?

      这取决于程序需要执行的任务;

    2、I/O和异常

    cin.exceptions(badbit);  //如果badbit位被设置,将引发异常;

    cin.exceptions(badbit|eofbit);  //如果badbit或eofbit被设置,将引发异常; 

    下面的程序,能够在failbit被设置时引发并捕获异常;

     1 //cinexcp.cpp  -- having cin throw an exception
     2 #include<iostream>  
     3 #include<exception>
     4 
     5 int main()
     6 {
     7     using namespace std;
     8     cin.exceptions(ios_base::failbit);
     9     cout<<"Enter numbers: ";
    10     
    11     int sum = 0;
    12     int input;
    13     try{
    14         while(cin>>input)
    15         {
    16             sum += inputs;
    17         }
    18     }catch(ios_base::failure & bf)
    19     {
    20         cout<<bf.what()<<endl;
    21         cout<<"O! the error!
    ":
    22     }
    23 
    24     cout<<"Last value entered = "<<input<<endl;
    25     cout<<"Sum = "<< sum <<endl;
    26     return 0;
    27 }

    运行结果

    Enter numbers: 20 30 40 pi 6

    ios_base failure in clear

    O! the horror!

    Last value entered = 40.00

    Sum = 90.00

    3、流状态的影响

    设置流状态可以有非常重要的后果:流将对后面的输入或输出关闭,直到位被清除;

     

    如果希望程序在流状态位被设置后能够读取后面的输入,就必须将流状态重置为良好。

    这可以通过调用clear()方法来实现;

    while(cin>>input)

    {

      sum += input;

    }

    cout<<"Last value entered = "<<input<<endl;

    cout<<" Sum = "<<sum<<endl;

    cout<<"Now enter a new number: ";

    cin.clear();  //reset stream state

    while(!isspace(cin.get()))

      continue;

    cin>>input;   //will work now

    注意这还不足以重新设置流状态。导致输入循环终止的不匹配输入仍留在输入队列中,程序必须跳过它。

    一种方法是一直读取字符,直到到达空白为止;

    isspace()函数是一个cctype函数,它在参数是空白字符时返回true。

    另一种方法是丢弃行中的剩余部分,

    while(cin.get() != ' ')

      continue;  //get rid rest of line

     

     

    现在假设循环是由于到达文件尾或者由于硬件故障而终止的。

    可以使用fail()方法检测假设是否正确,来修复问题。

    fail()在failbit或eofbit被设置时返回true。因此代码必须排除这种情况;

    while(cin>>input)

    {

      sum += input;

    }

    cout<<"Last value entered = "<<input<<endl;

    cout<<" Sum = "<<sum<<endl;

    if(cin.fail() && !cin.eof())

    {

     

    }

    else  //else bail out

    {

      cout<<"I cannot go on! ";

      exit(1);

    }

    cout<<"Now enter a new number:";

    cin>>input;   //will work now

    =====================================================

    三、其他istream类方法

    非格式化输入函数,因为它们只是读取字符输入,而不会跳过空白,也不进行数据转换;

      函数get(char*, int, char)getline(char*, int, char)在默认情况下读取整行而不是一个单词;

      方法get(char &)get(void)提供不跳过空白的单字符输入功能; 

    1、单字输入

    在使用char参数或没有参数的情况下,get()方法读取下一个输入字符,即使该字符是空格、制表符或换行符;

    get(char & ch)版本将输入字符赋给其参数。

    而get(void)版本将输入字符转换为整型(通常是int),并将其返回。

    1)成员函数get(char &)

    int ct = 0;

    char ch;

    cin.get(ch);

    while(ch != ' ')

    {

      cout<<ch;

      ct++;

      cin.get(ch);

    }

    cout<<ct<<endl;

    假设输入了 I C++ clearly.<Enter>

    输出的结果是 I C++ clearly.

    假设程序试图使用>>
    int ct = 0;

    char ch;

    cin>>ch;

    while(ch != ' ')

    {

      cout<<ch;

      ct++;

      cin>>ch;

    }

    cout<<ct<<endl;

    则代码首先跳过空格,这样做将不考虑空格。因此相应的输出压缩为:

    IC++clearly.

    这里有个很糟糕的情况,抽取运算符跳过了换行符,因此代码不会将换行符赋给ch,所以while循环测试将不会终止;

    get(char &)成员函数返回一个指向用于调用它的istream对象的引用,这意味着可以拼接get(char &)后面的其他抽取:

    char c1,c2,c3;

    cin.get(c1).get(c2)>>c3;

    首先cin.get(c1)将第一个输入字符赋给c1,并返回调用对象——cin.

    这样代码缩为cin.get(c2)>>c3,它将第二个输入字符赋给c2.该函数调用返回cin,将代码缩为cin>>c3。

    这将把下一个非空白字符赋给c3。因此c1和c2的值最后为空格,但c3不是。

    如果cin.get(char &)到达文件尾——无论是真正的文件尾还是模拟尾。它都不会给参数赋值了。也就是说到达文件尾时,没有值可以赋给参数了。

    char ch;

    while(cin.get(ch))

    {

      //process input

    只要存在有效输入,cin.get(ch)的返回值都是cin,此时判定结果为true,因此循环将继续。

    到达文件尾时,返回值判定为true,循环终止。

    2) 成员函数get(void) 

    get(void)使用返回值的形式来将输入传递给程序。

    可以这样使用它:

    int ct = 0;

    char ch;

    ch = cin.get();

    while(ch != ' ')

    {

      cout<<ch;

      ct++;

      ch = cin.get();

    }

    cout<<ct<<endl;

    get(void)成员函数的返回类型为int,这使得下面的代码是非法的:

    char c1, c2, c3;

    cin.get().get()>>c3; //not valid

    到达文件尾后,cin.get(void)都将返回值EOF——头文件提供的iostream提供的一个符号常量。

    int ch;

    while((ch = cin.get()) != EOF)

    {

      //process input

    }

    2、采用哪种单字输入形式

    假设可以选择>>、get(char &)、get(void),应该使用哪一种呢?

    首先应该确定是否希望跳过空白。

    如果跳过空白更加方便,则使用抽取运算符>>;

    如果希望程序检查每个字符,请使用get()方法。

    3、字符串输入:getline()、get()和ignore()

    istream & get(char *, int, char);

    istream & get(char *, int);

    istream & getline(char *, int, char);

    istream & getline(char *, int);

      第一个参数哟用于防止输入字符串的内存单元的地址;第二个参数比要读取的最大字符数大1;第三个参数指定用作分界符的字符;

    例如,下面的代码将字符输入读取到字符数组line中:

    char line[50];

    cin.get(line,50);

    cin.get()函数在到达第49个字符或遇到换行符后停止将输入读取到数组中。

    get()和getlne()的区别在于;get()将换行符留在输入流中,那么接下来的输入操作首先看到的将是换行字符。而getline()抽取并丢弃输入流中的换行符。

    第三个参数的用法,遇到分界符后,输入将停止,即使还未读取到最大数目的字符。

    默认情况下,get()将分界字符留在输入队列中,getline()不保留。

     1 //get_fun.cpp  -- using get() and getline()
     2 #include<iostream>  
     3 const int Limit = 255;
     4 
     5 int main()
     6 {
     7     using std::cout;
     8     using std::cin;
     9     using std::endl;
    10 
    11     char input[Limit];
    12     cout<<"Enter a string for getline() processing:
    ";
    13     cin.getline(input, Limit, '#');
    14     cout<<"Here is your input: ";
    15     cout<<input<<"
    Done with phase 1
    ";
    16 
    17     char ch;
    18     cin.get(ch);
    19     cout<<"The next input character is "<<ch<<endl;
    20     
    21     if(ch != '
    ')
    22         cin.ignore(Limit, '
    ');
    23     
    24     cout<<"Enter a string for get() processing:
    ";
    25     cin.get(input, Limit, '#');
    26     cout<<"Here is your input:
    ";
    27     cout<<input<<"
    Done whti phase 2
    ";
    28     
    29     return 0;
    30 }

    运行结果:

    Enter a string for getline() processing:

    Please pass

    me a #3 melon!

    Here is your input:

    Please pass

    me a 

    Done with phase 1

    The next input character is 3

    Enter a string for get() processing:

    I still

    want my #3 melon!

    Here is your input:

    I still

    want my

    Done with phase 2

    The next input character is #

    其中的cin.ignore(Limit, ' ');  这个函数调用将读取并丢弃接下来的255个字符直到到达第一个换行符;

    看下原型:

    istream & ignore(int = 1, int = EOF);

    默认参数值EOF导致igonre()读取指定数目的字符或读取到文件尾。

    该函数返回调用对象,这使得能够拼接函数调用;

    cin.ignore(255, ‘ ’).ignore(255, ' ');

    其中,第一个调用读取并丢弃一行,第二个调用读取并丢弃一行。因此一共读取了两行

     

    4、意外字符串输入

    get(char* , int)和getline()的某些输入形式将影响流状态。

    这两个函数在遇到文件尾时,将设置eofbit;

    遇到流破坏时,将设置badbit;

    另外两种情况是,无输入以及输入到达或超过函数调用指定的最大字符数。 

    如果不能抽取字符,它将把空值字符放置到输入字符串中,并使用setstate()设置failbit

    什么时候不能抽取字符:1)输入方法到达了文件尾;2)输入了空行

    输入空行并不会导致getline()设置failbit。这是因为getline()仍将抽取换行符,虽然不会存储它。

    如果希望getline()在遇到空行时终止循环,可以这样编写:

    char temp[30];

    while(cin.getline(temp,80)  && temp[0] != '')  //terminate on empty line

    如果输入队列中的字符数超过了输入方法中指定的最大字符数;

    如果读取了最大字符数,并且下一个字符不是换行符,则设置failbit;

    接下来看一下get(char * int)方法:

    它首先读取字符数,然后测试是否为文件尾以及下一个字符是否是换行符

    提供它读取了最大数目的字符,则不设置failbit标记;

    可以用peek()查看下一个输入字符,如果是换行符,则说明已经读取了整行;如果不是换行符,则说明get()在到达行尾前停止;

    getline()不适合使用peek()方法,是因为getline()读取并丢弃了换行符;

    如果使用get(),则可以知道是否读取了一整行;

    =====================================================

    四、其他istream方法

    其他istream方法还包括read()、peek()、gcount()和putback()。

    read()函数读取指定数目的字节,并将它们存储在指定的位置中。

    char gross[144];

    cin.read(gross, 144);

    这段代码从标准输入中读取144个字节,并将它们存储在gross数组中;

    注意read()不会在输入后面加上空值字符,因此不能将输入转换为字符串;

    read()方法不是为键盘而设计的,它与ostream write()函数配合使用,来完成文件输入和输出

    该方法的返回类型为istream &,因此可以像下面这样将它拼接起来:

    char gross[144];

    char score[20];

    cin.read(gross, 144).read(score, 20);

    peek()函数返回输入中的下一个字符,但不抽取输入流中的字符。

    可以用peek()查看输入流中的下一个字符,以此来判断是否继续读取:

    char great_input[80];

    char ch;

    int i = 0;

    while(ch = cin.peek() != ' . '  && ch != ' ')

      cin.get(great_input[i++]);

    great_input [i] = '';

    gcount()方法返回最后一个非格式化抽取方法读取的字符数。

    这指的是有get()、getline()、ignore()、read()方法读取的;

    putback()函数将一个字符插入到输入字符串中

    putback()方法接受一个char参数——要插入的字符,其返回类型是istream &;这使得可以将该函数调用与其他istream方法拼接起来;

    被插入的字符是下一条输入语句读取的第一个字符。

     1 //peeker.cpp  -- some istream methods
     2 #include<iostream>  
     3 
     4 int main()
     5 {
     6     using std::cout;
     7     using std::cin;
     8     using std::endl;
     9 
    10 //read and echo input up to a # character
    11     char ch;
    12     
    13     while(cin.get(ch))  //terminate on EOF
    14     {
    15         if(ch != '#')
    16             cout<<ch;
    17         else
    18         {
    19             cin.putback(ch); //reinsert character
    20             break;
    21         }
    22     }
    23     
    24     if(!cin.eof())
    25     {
    26         cin.get(ch);
    27         cout<<endl<<ch<<" is next input character.
    ";
    28     }
    29     else
    30     {
    31         cout<<"End of file reached.
    ";
    32         std::exit(0);
    33     }
    34     
    35     while(cin.peek() != '#')  // look ahead
    36     {
    37         cin.get(ch);
    38         cout<<ch;
    39     }
    40     
    41     if(!cin.eof())
    42     {
    43         cin.get(ch);
    44         cout<<endl<<ch<<" is next input character.
    ";
    45     }
    46     else
    47         cout<<"End of file reached.
    ";
    48     return 0;
    49 }

    运行结果

    I used a #3 pencil when I should have used a #2.

    I used a

    # is next input character.

    3 pencil when I should have used a 

    # is next input character.

     1 //truncate.cpp  -- using get() to truncate input line, if necessary
     2 #include<iostream>  
     3 const int SLEN = 10;
     4 inline void eatline() {while (std::cin.get() != '
    ') continue;}
     5 
     6 int main()
     7 {
     8     using std::cout;
     9     using std::cin;
    10     using std::endl;
    11 
    12     char name[SLEN];
    13     char title[SLEN];
    14     cout<<"Enter your name: ";
    15     cin.get(name,SLEN);
    16     
    17     if(cin.peek() != '
    ')
    18         cout<<"Sorry, we only have enough room for "<<name<<endl;
    19     eatline();
    20     
    21     cout<<"Dear"<<name<<", enter your title: 
    ";
    22     cin.get(title, SLEN);
    23     if(cin.peek() != '
    ')
    24         cout<<"We were forced to truncate your title.
    ";
    25     eatline();
    26     cout<<"Name: "<<name<<"
    Title: "<<title<<endl;
    27     
    28     return 0;
    29 }

    运行结果:
    Enter your name: Ella Fishsniffer

    Sorry, we only have enough room for Ella Fish

    Dear Ella Fish, enter your title:

    Executive Adjunct

    We were forced to truncate your title.

     Name: Ella Fish

    Title: Executive

    注意,下面的代码确定第一条输入语句是否读取了整行:

    while(com.get() != ' ') continue;

  • 相关阅读:
    面向对象--封装
    面向对象--多态
    面向对象编程
    类的特殊成员
    新式类 VS 经典类
    python类的继承
    python析构函数
    类的公有属性
    (转)JAVA AJAX教程第二章-JAVASCRIPT基础知识
    (转)JAVA AJAX教程第一章-初始AJAX
  • 原文地址:https://www.cnblogs.com/grooovvve/p/10493557.html
Copyright © 2011-2022 走看看