面向对象程序设计——Visual C++
第16章 输入/输出流

思考题

1、简述输入/输出流的特性?

输入/输出流是C++中用于输入和输出数据的机制,具有以下特性:

  1. 流是一种抽象的概念,可以表示各种不同的输入/输出设备,如键盘、屏幕、文件等。
  2. 流可以分为输入流和输出流,输入流用于读取数据,输出流用于写入数据。
  3. 流可以通过运算符重载来实现输入和输出操作,如“<<”和“>>”运算符。
  4. 流可以进行格式化输出,可以指定输出的精度、宽度、填充字符等。
  5. 流可以进行缓冲操作,可以将数据缓存到内存中,提高输入/输出效率。
  6. 流可以进行错误处理,可以检测输入/输出错误,并进行相应的处理。 总之,输入/输出流是C++中非常重要的机制,可以方便地进行数据的输入和输出操作,提高程序的可读性和可维护性。

2、系统预定义的流类对象中,cin和cout具有哪些功能?

系统预定义的流类对象中,cin和cout是C++中最常用的输入/输出流对象,具有以下功能:

  1. cin用于从标准输入设备(通常是键盘)读取数据,可以读取各种类型的数据,如整数、浮点数、字符等。
  2. cout用于向标准输出设备(通常是屏幕)输出数据,可以输出各种类型的数据,如整数、浮点数、字符等。
  3. cin和cout都可以进行格式化输入/输出,可以指定输出的精度、宽度、填充字符等。
  4. cin和cout都可以进行缓冲操作,可以将数据缓存到内存中,提高输入/输出效率。
  5. cin和cout都可以进行错误处理,可以检测输入/输出错误,并进行相应的处理。 总之,cin和cout是C++中非常重要的输入/输出流对象,可以方便地进行数据的输入和输出操作,提高程序的可读性和可维护性。

3、如何对用户自定义类型对象用输入/输出流的方式进行输入/输出?

对用户自定义类型对象用输入/输出流的方式进行输入/输出,需要重载输入/输出运算符(“<<”和“>>”运算符)。具体步骤如下:

  1. 在类的定义中声明友元函数,以便该函数能够访问类的私有成员。
  2. 定义输入/输出运算符函数,函数名为“operator<<”和“operator>>”,分别用于输出和输入对象。
  3. 在输入/输出运算符函数中,按照需要进行输入/输出操作,可以使用类的成员函数和数据成员。
  4. 在主函数中,使用输入/输出流对象来输入/输出自定义类型对象,可以直接使用“<<”和“>>”运算符。 例如,对于一个自定义的Person类,可以按照以下方式重载输入/输出运算符:
class Person {
private:
    string name;
    int age;
public:
    friend ostream& operator<<(ostream& os, const Person& p);
    friend istream& operator>>(istream& is, Person& p);
};
ostream& operator<<(ostream& os, const Person& p) {
    os << "Name: " << p.name << ", Age: " << p.age;
    return os;
}
istream& operator>>(istream& is, Person& p) {
    cout << "Enter name: ";
    is >> p.name;
    cout << "Enter age: ";
    is >> p.age;
    return is;
}

在主函数中,可以使用cout和cin对象来输出和输入Person对象:

Person p;
cout << "Enter person information: " << endl;
cin >> p;
cout << "Person information: " << p << endl;

4、如何输出一个浮点数的不同精度?

输出一个浮点数的不同精度可以使用流控制符来实现。流控制符是一种特殊的符号,用于控制输出流的格式。 常用的流控制符包括以下几种:

  1. 精度控制符“setprecision(n)”:用于设置输出浮点数的精度,n表示要输出的小数位数。例如,cout<<setprecision(2)<<3.1415926;会输出3.14
  2. 宽度控制符“setw(n)”:用于设置输出字段的宽度,n表示输出的字符数。例如,cout<<setw(5)<<3.14;会输出“ 3.14”
  3. 填充控制符“setfill(c)”:用于设置输出字段的填充字符,c表示填充字符。例如,cout<<setfill('*')<<setw(5)<<3.14;会输出“**3.14”。

总之,使用流控制符可以方便地控制输出流的格式,实现不同精度的输出。

5、确定数据项的域宽有几种方法?分别举例说明。

确定数据项的域宽有以下几种方法:

  1. 使用流控制符“setw(n)”:可以设置输出字段的宽度,n表示输出的字符数。例如,cout<<setw(5)<<3.14;会输出“3.14”。
  2. 使用格式化输出函数:可以使用printf或sprintf等函数来进行格式化输出,可以指定输出的域宽。例如,printf("%5d", 123);会输出“ 123”。
  3. 使用字符串流:可以使用ostringstream或stringstream等字符串流来进行格式化输出,可以指定输出的域宽。例如,ostringstream oss;oss<<setw(5)<<123;string s=oss.str();会将输出结果存储在字符串s中。 总之,确定数据项的域宽可以使用流控制符、格式化输出函数或字符串流等方法,根据具体情况选择合适的方法来实现。

6、对文件进行操作包括哪几个步骤?

对文件进行操作包括以下几个步骤:

  1. 打开文件:使用文件流对象打开文件,可以指定文件名、打开方式等参数。
  2. 读写文件:使用文件流对象进行读写操作,可以使用输入/输出运算符、getline函数等进行数据的读写。
  3. 关闭文件:使用文件流对象关闭文件,释放文件资源。

在进行文件操作时,需要注意以下几点:

  1. 文件操作可能会出现错误,需要进行错误处理,可以使用异常处理机制或者检查文件流对象的状态来处理错误。
  2. 文件操作需要注意文件的打开方式,可以使用不同的打开方式来实现不同的操作,如读取、写入、追加等。
  3. 文件操作需要注意文件的编码格式,可以使用不同的编码格式来实现不同的操作,如文本文件、二进制文件等。

总之,对文件进行操作需要进行一系列的步骤和注意事项,需要根据具体情况选择合适的方法来实现文件操作。

习题

1、选择题

(1)进行文件操作时需要包含( )文件。 A. iostream.h B. fstream.h C. stdio.h D. stdlib.h (2)使用流操纵算子对数据进行格式输出时,应包含( )文件。 A. iostream.h; B. fstream.h; C. iomanip.h D.stdlib.h (3)已知:int a,*pa=&a;输出指针pa十进制的地址值的方法试( )。 A. cout<<pa; B. cout<<*pa; C. cout<<&pa; D. cout<<long(pa); (4)下列输出字符’A’的方法中,( )是错误的。 A. cout<<put(‘A’); B. cout<<’A’; C. cout.put(‘A’); D. char a=’A’;cout<<A; (5)关于getline()函数的下列描述中,( )是错的。 A.该函数可用来从键盘上读取字符串; B.该函数读取的字符串长度是受限制的; C.该函数读取字符串时遇到终止符便停止; D.该函数中所使用的终止符只能是换行符。 (6)关于read()函数的下列描述中,( )是对的。 A.该函数只能从键盘输入中获取字符串; B.该函数所获取的字符多少是不受限制的; C.该函数只能用于文本文件的操作中; D.该函数只能按规定读取所指定的字符数。 (7)在ios中提供控制格式的标志位中,( )是转换为十六进制形式的标志位。 A. hex B. oct C. dec D. left (8)控制格式输入输出的流操纵算子中,( )是设置域宽的。 A. ws B. oct C. setfill() D. setw() (9)下列函数中,( )是对文件进行写操作的。 A. get() B. read() C. seekg() D. put() (10)从类库的层次结构来看,fstream直接从( )类派生。 A. istream B. ostream C. iostream D. ios

答:
B
C
D
A
D
D
A
D
D
C

2、判断题

(1)使用插入符“<<”可以输出各种类型的变量值,也可以输出指针值。 (2)预定义的插入符和提取符是可以重载的。 (3)到cerr的输出是非缓冲输出,到clog的输出是缓冲输出。 (4)记录流的当前格式化的状态标志字中每一位用于记录一种格式,这种格式是不能被设置或清除的。 (5)流操纵算子本身是一个对象,它可以直接被插入符或提取符操作。 (6)get()函数不能从流中提取终止字符,终止字符仍留在流中;getline()函数从流中提取终止字符,但终止字符被丢弃。 (7)以app方式打开文件时,当前的读指针和写指针都定位于文件尾。 (8)打开文本文件和二进制文件时,打开方式是相同的。 (9)使用关闭文件函数close()关闭一个文件时,但流对象仍存在。 (10)read()和write()函数可以读/写文本文件,也可以读写二进制文件。 答:

  1. False。插入符“<<”可以输出各种类型的变量值,但不能直接输出指针值,需要使用强制类型转换或打印指针变量的地址。
  2. False。预定义的插入符和提取符是不能重载的。
  3. True。
  4. False。记录流的当前格式化的状态标志字中每一位用于记录一种格式,这种格式是可以被设置或清除的。
  5. True。
  6. False。get()函数可以从流中提取终止字符,但终止字符仍留在流中;getline()函数从流中提取终止字符,终止字符被丢弃。
  7. True。
  8. False。打开文本文件和二进制文件时,打开方式是不同的。
  9. False。关闭文件函数close()同时也会销毁流对象。
  10. True。但需要注意,在读/写二进制文件时,read()和write()函数不会对数据进行格式转换,而是直接以二进制方式读/写数据。

3、编程题

(1)编写一个程序能实现复数的输入/输出与复数的加减运算,要求先输入两组复数,然后对两组复数分别进行加减运算,并输出原来两组复数及运算后复数的值。

#include <iostream>
using namespace std;

class Complex {
private:
    double real;
    double imag;
public:
    Complex() : real(0), imag(0) {}
    Complex(double r, double i) : real(r), imag(i) {}

    friend istream& operator>> (istream& in, Complex& c);
    friend ostream& operator<< (ostream& out, const Complex& c);
    friend Complex operator+ (const Complex& c1, const Complex& c2);
    friend Complex operator- (const Complex& c1, const Complex& c2);
};

istream& operator>> (istream& in, Complex& c) {
    cout << "Enter real part: ";
    in >> c.real;
    cout << "Enter imaginary part: ";
    in >> c.imag;
    return in;
}

ostream& operator<< (ostream& out, const Complex& c) {
    if (c.imag >= 0)
        out << c.real << "+" << c.imag << "i";
    else
        out << c.real << c.imag << "i";
    return out;
}

Complex operator+ (const Complex& c1, const Complex& c2) {
    return Complex(c1.real + c2.real, c1.imag + c2.imag);
}

Complex operator- (const Complex& c1, const Complex& c2) {
    return Complex(c1.real - c2.real, c1.imag - c2.imag);
}

int main() {
    Complex c1, c2, c3, c4;

    cout << "Enter the first complex number: " << endl;
    cin >> c1;

    cout << "Enter the second complex number: " << endl;
    cin >> c2;

    c3 = c1 + c2;
    c4 = c1 - c2;

    cout << "The first complex number is: " << c1 << endl;
    cout << "The second complex number is: " << c2 << endl;
    cout << "The sum of the two complex numbers is: " << c3 << endl;
    cout << "The difference of the two complex numbers is: " << c4 << endl;

    return 0;
}

这个程序中,我们定义了一个复数类 Complex,并重载了输入/输出运算符和加减运算符。在 main 函数中,我们首先输入两组复数,然后对这两组复数进行加减运算,并输出结果。

(2)编写一个程序,从键盘输入一个字符串,判断字符串的长度,然后以字符串长度的两倍作为域宽打印出该字符串。

#include <iostream>
#include <cstring>
#include <iomanip>

using namespace std;

int main() {
    const int MAX_LEN = 100;
    char str[MAX_LEN];

    // 从键盘输入字符串
    cout << "请输入字符串: ";
    cin >> str;

    // 计算字符串长度并输出
    int len = strlen(str);
    cout << "字符串长度为: " << len << endl;

    // 设置域宽并打印字符串
    cout << "打印字符串: " << setw(len * 2) << setiosflags(ios::left) << str << endl;

    return 0;
}

(3)输入一个浮点数,要求以人民币的格式形式输出该数,长度一律10位,小数点后两位,币值前有一‘¥’符号。

#include <iostream>
#include <iomanip>
using namespace std;

int main() {
    double num;
    cout << "请输入一个浮点数:";
    cin >> num;

    cout << fixed << setprecision(2);  // 设置输出精度为小数点后两位

    // 输出人民币符号和数字部分
    cout << "¥" << setw(10) << num;

    return 0;
}

程序中使用了 fixed 和 setprecision 控制输出精度,使用 setw 控制输出宽度,最终输出结果类似于以下形式:

请输入一个浮点数:123.456
¥    123.46

需要注意的是,上述程序只是简单地输出了一个带有人民币符号的浮点数,并未考虑格式化输出金额的具体规则。实际中,金额的格式化输出可能会包括更多的细节和规则。

(4)编写程序建立输出文件,向该文件中写入信息,然后关闭文件,再打开该文件,将文件的信息读出显示在屏幕上。

#include <iostream>
#include <fstream>
#include <string>

using namespace std;

int main() {
    // 建立输出文件
    ofstream outfile("output.txt");

    // 写入信息
    outfile << "Hello, world!\n";
    outfile << "This is some text written to the file.\n";
    outfile << "And here is a number: " << 42 << endl;

    // 关闭文件
    outfile.close();

    // 打开文件并读出信息
    ifstream infile("output.txt");

    // 如果文件打开失败,则提示错误并退出程序
    if (!infile.is_open()) {
        cout << "Error opening file!" << endl;
        return 1;
    }

    // 读出信息并显示在屏幕上
    string line;
    while (getline(infile, line)) {
        cout << line << endl;
    }

    // 关闭文件
    infile.close();

    return 0;
}

(5)编一个程序拷贝文本文件,在拷贝文件过程中,将所有小写改成大写。

#include <iostream>
#include <fstream>
#include <cctype>

using namespace std;

int main() {
    string input_file, output_file;
    cout << "Enter the name of the input file: ";
    cin >> input_file;
    cout << "Enter the name of the output file: ";
    cin >> output_file;

    ifstream in_file(input_file);
    if (!in_file) {
        cerr << "Error opening input file!" << endl;
        return 1;
    }

    ofstream out_file(output_file);
    if (!out_file) {
        cerr << "Error creating output file!" << endl;
        return 1;
    }

    char c;
    while (in_file.get(c)) {
        if (islower(c)) {
            c = toupper(c);
        }
        out_file.put(c);
    }

    in_file.close();
    out_file.close();

    cout << "Copy complete!" << endl;

    return 0;
}

该程序首先要求用户输入要拷贝的文件的文件名和要写入的文件的文件名。然后,它使用ifstreamofstream对象打开输入和输出文件。接下来,程序遍历输入文件的每个字符,如果字符是小写字母,则将其转换为大写字母,并将其写入输出文件。最后,程序关闭输入和输出文件,并输出一条完成的消息。

(6)实现一个通讯录打印程序。通讯录的记录格式为:(姓名、单位、电话、住址、宅电),要求先建立一个Person类,然后按对象读入和打印。

#include <iostream>
#include <string>
#include <vector>
#include <fstream>

using namespace std;

class Person {
public:
    Person(const string& name, const string& company, const string& phone, const string& address, const string& homePhone)
        : mName(name), mCompany(company), mPhone(phone), mAddress(address), mHomePhone(homePhone) {}

    void print() const {
        cout << "Name: " << mName << endl;
        cout << "Company: " << mCompany << endl;
        cout << "Phone: " << mPhone << endl;
        cout << "Address: " << mAddress << endl;
        cout << "Home Phone: " << mHomePhone << endl;
        cout << endl;
    }

private:
    string mName;
    string mCompany;
    string mPhone;
    string mAddress;
    string mHomePhone;
};

int main() {
    vector<Person> persons;

    // 读取通讯录信息
    ifstream in("addressbook.txt");
    string name, company, phone, address, homePhone;
    while (in >> name >> company >> phone >> address >> homePhone) {
        Person person(name, company, phone, address, homePhone);
        persons.push_back(person);
    }
    in.close();

    // 打印通讯录信息
    for (const auto& person : persons) {
        person.print();
    }

    return 0;
}

该程序使用Person类表示通讯录中的每个人,使用一个vector来存储所有的通讯录记录。主程序首先从文件中读取通讯录信息,并创建一个Person对象来存储每个记录,然后将这些对象添加到vector中。最后,程序遍历vector,输出所有通讯录记录的信息。