C++ Primer Plus 学习记录(第六章节-分支语句和逻辑运算符-包含练习题答案)


本章将首次介绍文件输入\输出

6.1 if语句

if语句的语法与while相似:

if (test-condition)
	statement

例子:计算输入中的空格数和字符总数,则可以在while循环中使用cin.get(char)来读取字符,然后使用if语句识别空格字符并计算其总数。程序清单6.1,使用句点(.)来确定句子的结尾:

#include <iostream>
int main()
{
	using std::cin;
	using std::cout;
	char ch;
	int spaces = 0;
	int total = 0;
	cin.get(ch);
	while (ch != '.')
	{
		if (ch == ' ')
			++spaces;
		++total;
		cin.get(ch);
	}
	cout << spaces << " spaces, " << total;
	cout << " characters total in sentence\n";
	return 0;
}

下面是该程序的输出:
请添加图片描述
字符总数包括按回车键生成的换行符

6.1.1 if else语句

p168电子书
例子:通过对字母进行加密编码来修改输入的文本(换行符不变)。这样,每个输入行都被转换为一行输出,且长度不变。
这意味着程序对换行符采用一种操作,而对其他字符采用另一种操作。如程序清单6.2所示:

#include <iostream>
int main()
{
    using namespace std;
    char ch;
    cout << "Type, and I shall repeat.\n";
    cin.get(ch);
    while(ch != '.')
    {
        if (ch == '\n')
            cout << ch;
        else
            cout << ++![请添加图片描述](https://img-blog.csdnimg.cn/d84a35afeb1d47a5aaa474338ae81f11.png)
ch;
        cin.get(ch);
    }
    cout << "\nPlease excuse the slight confusion.\n";
    return 0;
}

该程序的运行情况:
请添加图片描述
将++ch改为ch+1,运行结果变成:
请添加图片描述
这是由于cout处理不同的类型有不同的方式。

6.1.2 格式化if else语句

1、C++不会自动将if和else之间的所有代码视为一个代码块。必须使用大括号将这些语句租车一个代码块。

2、C++对大括号的位置没有任何限制

6.1.3 if else if else结构

看上去这是一种新的控制结构–if else if else结构。
它只是一个if else被包含在另一个if else中。

条件运算符和错误防范
使用下述方法防范将相等运算符误写成赋值运算符的错误:

if (3 == myNumber)

如果使用下面的,编译器将生成错误消息:

if (3 = myNumber)

6.2 逻辑表达式

逻辑OR(||)、逻辑AND(&&)和逻辑NOT(!)

6.2.1 逻辑OR运算符:||

表达式中有任何一个或者全部都为true(或非零),则得到的表达式的值为true;否则,表达式的值为false。

C++规定,||运算符是个顺序点(sequence point)。先修改左侧的值,再对右侧的值进行判定(C++11的说法是,运算符左边的子表达式先于右边的子表达式)。

如果||运算符左侧的表达式为true,则C++将不会去判定右侧的表达式,因为只要一个表达式为true,则整个逻辑表达式true。

6.2.2 逻辑AND运算符:&&

仅当原来的两个表达式都为true时,得到的表达式的值为true。

其他的同||

6.2.3 用&&来设置取值范围

&&运算符还允许建立一系列if else if else语句,其中每种选择都对应于一个特定的取值范围。

if (age > 17 && age < 35)
	index = 0;
else if (age >= 35 && age < 50)
	index = 1;
else if (age >= 50 && age < 50)
	index = 2;
else
	index = 3;
补充:一种用于处理一系列消息的计数,char数组标识一些类字符串

char指针数组也可以标识一系列字符串,只要将每一个字符串的地址赋给各个数组元素即可。

下面程序使用qualify数组存储4个字符串的地址。
qualify[1]存储字符串”mud tug-of-war\n“的地址。然后,程序便能够将cout、strlen()或strcmp()用于qualify[1],就像用于其他字符串指针一样,使用const限定符可以避免无意间修改这些字符串。

const char * qualify[4] =
{
	"10,000-meter race.\n",
	"mud tug-of-war.\n",
	"masters canoe jousting.\n",
	"pie-throwing festival.\n"
};
if (17 < age < 35)说明

取值范围测试
关系:

if (age > 17 && age < 35)

不要使用数字符号将其表示为:

if (17 < age < 35)

编译器不不会捕获这种错误,因为他仍是有效的C++语法。上述式子的含义为:

if ((17 < age) < 35)

如果age为15,则1 < 35 为true

6.2.4 逻辑NOT运算符:!

!运算符对于返回true-false值或可以被解释成true-false值的函数来说很有用。

例如:C-风格字符串s1和s2不同,则strcmp(s1,s2)将返回非零(true)值,否则返回0。这意味着如果这两个字符串相同,则!strcmp(s1,s2)为true。

6.2.5 逻辑运算符细节

1、优先级(!、&& 、||、 关系)

C++逻辑OR和逻辑AND运算符的优先级都低于关系运算符。

逻辑AND高于OR。

age > 30 && age << 45 || weight > 300
//被解释成:
(age > 30 && age < 45) || weight > 300

!运算符的优先级高于所有的关系运算符和算术运算符,因此,要对表达式求反,必须用括号将其括起

int x = 2;
!(x > 5)//true
!x > 5//false,(!x) > 5

当然,可以使用括号将所希望的解释告诉程序。

C++确保程序从左向右进行计算逻辑表达式,并在直到答案后立刻停止。

6.2.6 其他表示方式

使用AND、OR和!,C++不要求使用头文件。意味着不能将它们用作变量名,它们不是关键字,都是已有语言特性的另一种表示方式。

它们不是C语言中的保留字,但是C语言程序可以将他们用作运算符,但是要在头文件中包含iso646.h

6.3 字符函数库cctype

这是C++从C语言中继承的一个与字符相关的、非常方便的函数软件包,它可以简化诸如确定字符是否为大写字母,数字、标点符号等工作。

这个函数的原型在头文件cctype(老式是ctype.h)中定义的

如:
如果ch是一个字母,则isalpha(ch)函数返回一个非零值,否则返回0;

如果ch是标点符号(如逗号或句号),函数ispunct(ch)将返回true。

(这些函数的返回类型为int,而不是bool,但通常bool转换可以视为bool)

使用isdigits()来测试字符是否为数字字符

使用isspace()来测试字符是否为空白

表6.4 cctype中的字符函数

请添加图片描述

6.4 ?:运算符

条件运算符(?:),他是C++中唯一一个需要3个操作数的运算符。通用格式为:

expression1 ? expression2 : expression3

如果expression1为true,则整个条件表达式的值为expression2的值;否则,整个表达式的值为expression3的值。

得到两个值中较大的一个为:

int c = a > b ? a : b;

6.5 switch语句

C++的switch语句能够更容易从大型列表中进行选择。switch语句的通用格式:

switch (integer-expression)//integer-expression必须是一个结果为整数值的表达式
{
	//每个标签都必须是整数常量表达式
	case label1 : statement(s)
	case label2 : statement(s)
	...
	default	    : statement(s)//是可选的,如果被省略,有没有匹配的标签,程序跳到switch后面的语句初执行
}

**break的重要性:**程序跳到swtich中特定代码后,依次执行之后的所有语句,除非有明确的其他指示。程序不会执行到下一个case处自动停止,要让程序执行完一组特定语句后停止,必须使用break语句。

6.5.1 将枚举量用作标签

程序清单6.11使用enum定义了一组相关的常量,然后在switch语句中使用这些常量。

通常,cin无法识别枚举类型(他不知道程序员是如何定义他们的),因此该程序要求用户选择选项时输入一个整数。

当switch语句将int值和枚举量标签进行比较时,将枚举量提升为int。另外,在while循环测试条件中,也会将枚举量提升为int类型。

程序清单6.11

#include <iostream>

enum {red, orange, yellow, green, blue, violet, indigo};

int main()
{
	using namespace std;
	cout << "Enter color code (0-6): ";
	int code;
	cin >> code;
	while (code >= red && code <= indigo)
	{
		switch (code)
		{
			case red : cout << "red.\n";break;
			case orange : cout << "orange\n";break;
			case yellow : cout << "yellow\n";break;
			![请添加图片描述](https://img-blog.csdnimg.cn/d5088199cc0042a9b72554f4bd3b1857.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA6LiP56C05LiH6YeM5peg5LqR,size_11,color_FFFFFF,t_70,g_se,x_16)

			case blue   : cout << "blue\n";break;
			case violet : cout << "violet\n";break;
			case indigo : cout << "indigo\n";break;
		}
		cout << "Enter color code (0-6): ";
		cin >> code;
	}
	cout << "Bye \n";
	return 0;
}

下面是程序的输出
请添加图片描述

6.5.2 switch 和if else

switch语句不是为了处理取值范围而设计的。switch语句中每一个case标签都必须是一个单独值。

这个值必须是整数(包括char),因此swtich无法处理浮点测试。另外,case标签值还必须是常量。

如果选项设计取值范围、浮点测试或两个变量的比较,则应该使用if else语句。

如果所有的选项都可以使用整数常量来标识,两个都可以用,但是switch专门为这种情况设计的,所以选项超过两个,就代码长度和执行速度来言,switch语句的效率更高

6.6 break和continue语句

break和continue语句都使程序能够跳过部分代码。

可以在switch语句或任何循环中使用break语句,使程序跳到switch或循环后面的语句处执行。

continue语句用于循环中,让程序跳过循环体中余下的代码,并开始新一轮循环。

1、程序代码例子

程序清单6.12:
该程序让用户输入一行文本,循环将回显每个字符,如果该字符为句点,则使用break结束循环。可以在某种条件为true时,使用break结束循环。接下来,程序计算空格数,不计算其他字符,当字符不为空格时,循环将使用continue语句跳过计数部分。

#include <iostream>

const int ArSize = 80;
int main()
{
    using namespace std;
    char line[ArSize];
    int spaces = 0;

    cout << "Enter a line of text:\n";
    cin.get(line,ArSize);
    cout << "\nComplete line: \n" << line << endl;
    cout << "\nLine through first period.\n";

    for (int i = 0; line[i] != '\0'; i++)
    {
        cout << line[i];
        if (line[i] == '.')
            break;
        if (line[i] != ' ')
            continue;
        spaces++;
    }
    cout << "\n\n" << spaces << " spaces\n";
    cout << "\nDone.\n";
    return 0;
}

运行如下:
请添加图片描述
程序说明:
在for循环中,continue语句使程序直接跳到更新表达式处,然后跳到测试表达式处。

对于while循环来说,continue将使程序直接跳到测试表达式处,因此while循环体中位于continue之后的更新表达式都将被跳过。

该程序可以不使用continue,使用代码:

if (line[i] == ' ')
	spaces++;
2、C++也有goto语句

下面的语句将跳到使用pairs:作为标签的位置:

goto pair;

也就是说,可以有下面这样的代码:

char ch;
cin >> ch;
if (ch == 'p')
	goto pairs;
cout << ...
...
pairs: cout << "You've just arrived at Pairs.\n";

大多数情况下,使用goto语句不好,而应使用结构化控制语句(if else、switch、continue)来控制程序的流程。

6.7 读取数字的循环

1、读取数组到数组,在数组满前可以结束(捕鱼计数代码例子)

一种方法使用cin,请看代码:

int n;
cin >> n;

如果用户输入一个单词,而不是一个数字,情况如何?发生这种类型不匹配的情况时,发生4中情况:

  • n的值保持不变
  • 不匹配的输入将被留在输入队列中
  • cin对象中的一个错误标记被设置
  • 对cin方法的调用将返回false(如果被转换为bool类型)

方法返回false意味着可以使用非数字输入结束读取数字循环。非数字输入设置错误标记意味着必须重置该标记,程序才能继续读取输入。clear()方法重置错误输入标记,同时也重置文件尾(EOF条件,参见第五章)。输入错误和EOF都将导致cin返回fasle。

程序清单6.13
计算平均每天捕获鱼的重量。这里假设每天最多捕获5条鱼,因此一个包含5个元素的数组将足以存储所有的数据,但也可能没有捕获那么多鱼。

下面数组被填满或者输入了非数字输入,循环将结束

#include <iostream>
const int Max = 5;
int main()
{
    using namespace std;

    double fish[Max];
    cout << "Please enter the weights of your fish.\n";
    cout << "You may enter up to " << Max
         << " fish <q to terminate>.\n";
    cout << "fish #1: ";
    int i = 0;
    while (i < Max && cin >> fish[i])//cin方法函数调用,该函数返回cin,如果cin位于测试条件中,将被转换为bool类型,如果输入成功,则转换后的值为true,否则为false。如果表达式的值为false,则循环结束。
    {
        if (++i < Max)
            cout << "fish #" << i+1 << ": ";
    }

    double total = 0.0;
    for (int j = 0; j < i; j++)
        total += fish[j];
    if (i == 0)
        cout << "No fish\n";
    else
        cout << total / i << " = average weight of "
            << i << "  fish\n";
    cout << "Done.\n";
    return 0;
}

程序运行:
请添加图片描述
**注意!!!:**在前面说过,为了窗口能够打开看到输出,需要添加额外的代码,这里由于输入q结束输入,处理起来更加复杂一些:

if (!cin)
{
	cin.clear();
	cin.get();
}
cin.get();
cin.get();

在程序清单6.13中,让程序结束循环后可以接受输入,也可以使用类似的代码。
程序清单6.14中,使用cin来返回值重置cin。

上述例子当用户输入的不是数字时,该程序不再读取输入。下面看一个继续读取的例子。
程序清单6.14
该程序要求用户提供5个高尔夫得分,以计算平均成绩。如果用户输入非数字输入,程序将拒绝,并要求用户继续输入数字。可以看到使用cin输入表达式的值来检测输入是不是数字。程序发现用户输入错误内容时,应采取3个步骤:

  • 重置cin以接受新的输入
  • 删除错误输入
  • 提示用户再输入

注意!!:程序必须先重置cin,然后才能删除错误输入。


#include <iostream>
const int Max = 5;
int main()
{
    using namespace std;

    int golf[Max];
    cout << "Please enter your golf scores.\n";
    cout << "You must enter " << Max << " rounds.\n";
    int i ;
    for (i = 0;i < Max; i++)
    {
        cout << "round #" << i+1 << ": ";
        while (!(cin >> golf[i]))
        {
            cin.clear();
            while (cin.get() != '\n')
                continue;
            cout << "Please enter a number: ";
        }//这个while循环是错误处理代码的关键部分

    }
    double total = 0.0;
    for (i = 0; i < Max; i++)
        total +=golf[i];
    cout << total /Max << " = average score " << Max << " rounds\n";
    return 0;
}

程序运行:
请添加图片描述

6.8 简单文件输入/输出

C++使得将读取键盘输入和在屏幕上显示输出(统称为控制台输入/输出)的技巧用于文件输入/输出(文件I/O)非常简单。

第17章更详细地讨论这些主题,这里简单的介绍文本文件I/O。

6.8.1 文本I/O和文本文件

在使用cin进行输入时,程序将输入视为一系列的字节,其中每个字节都被解释为字符编码。不管目标数据类型是什么,输入一开始都是字符数据–文本数据。然后,cin对象负责将文本转换为其他类型。

1、cin处理同一个输入行的代码方法

假如有如下示例输入行:

38.5 19.2

来看一下使用不同的数据类型的变量来存储时,cin是如何处理该输入行的。

a、char数据
char ch;
cin >> ch;

输入行的第一个字符被赋给ch。

在这里,就是第一个字符是数字3,其字符编码(二进制)被存储在变量ch中。

输入和目标变量都是字符,因此不需要进行转换。这里存储的不是数值3,而是字符3的编码。输入队列中的下一个字符为字符8,下一个输入操作将对其进行处理。

b、int类型
int n;
cin >> n;

cin将不断地读取,直到遇到非数字字符。

在这里,他将读取3和8,这样句点将成为输入队列中的下一个字符。

cin通过计算发现,这两个字符对应数值38,因此将38的二进制编码复制到变量n中。

c、double类型
double x;
cin >> x;

cin将不断的读取,直到遇到第一个不属于浮点数的字符。

在这里,cin读取3、8、句点和5,使得空格成为输入队列中的下一个字符。

cin通过计算发现,这四个字符对应于数值38.5,因此将38.5的二进制编码(浮点格式)复制到变量x中。

d、char数组
char word[50];
cin >> word;

cin将不断读取,直到遇到空白字符。

在这里,他将读取3、8、句点和5,使得空格成为输入队列中的下一个字符。

cin将这4个字符的字符编码存储到数组word中,并在末尾加上一个空字符。这里不需要进行任何转换。

e、使用getline()存储数组输入
char word[50];
cin.getline(word,50);

cin将不断读取,直到遇到换行符(示例输入行少于50个字符)。

所有的字符都将被存储到数组word中,并在末尾加上一个空字符。换行符被丢弃,输入队列中的下一个字符是下一行的第一个字符。这里不需要进行任何转换。

关于文本文件的说明

输入一开始为文本,因此,控制台输入的文件版本是文本文件,即每个字节都存储了一个字符编码的文件。并非所有的文件都是文本文件,例如,数据库和电子表格以数值格式(即二进制整数或浮点格式)来存储数值数据。
字处理文件中可能包含文本信息,但也可能包含用于描述格式、字体、打印机的非文本数据。

本章讨论的文件I/O相当于控制台I/O,因此仅适用于文本文件。

要创建文本文件,用于提供输入,可以使用文本编译器,如Windows中的“记事本”UNIX/Linux系统中的vi或emacs。也可以使用字处理程序来创建,但必须将文件保存为文本格式。
IED中的源代码编辑器生成的也是文本文件,事实上,源代码文件就属于文本文件。同样,可以使用文本编辑器来查看通过文本输出创建的文件。

6.8.2 写入到文本文件中

1、cout和文件输出区别

对于文件输入,C++使用类似于cout的东西。下面来复习一些有关将cout用于控制台输出的基本事实,为文本输出做准备。

  • 1、必须包含头文件iostream。
  • 2、头文件iostream定义了一个用于处理输出的ostream类。
  • 3、头文件iostream声明了一个名为cout的ostream变量(对象)。
  • 4、必须指明名称空间std;例如,为引用元素cout和endl,必须使用编译指令using或前缀std::。
  • 5、可以结合使用cout和运算符<<来显示各种类型的数据。

文件输出与此极为相似:

  • 1、必须包含头文件fstream。
  • 2、头文件fstream定义了一个用于处理输出的ofstream类。
  • 3、需要声明一个或多个ofstream变量(对象),并以自己喜欢的方式对其进行命名,条件是遵守常用的命名规则。
  • 4、必须指明名称空间std;例如,为引用元素ofstream,必须使用编译指令using或前缀std::。
  • 5、需要将ofstream对象与文件关联起来。为此,方法之一是使用open()方法。
  • 6、使用完文件后,应使用方法close()方法将其关闭。
  • 7、可结合使用ofstream对象和运算符<<来输出各种类型的数据。
2、ofstream对象的使用

虽然头文件iostream提供了一个预先定义好的名为cout的ostream对象,但必须声明自己的ofstream对象,为其命名,并将其同文件关联起来。

下面演示了如何声明这种对象:

ofstream outFile;
ofstream fout;

下面演示了如何将这种对象与特定的文件关联起来:

outFile.open("fish.txt");//outFile被用作向fish.txt写入
char filename[50];
cin >> filename;
fout.open(filename);//fout被用作读取特定文件

方法open()接受一个C-风格字符串作为参数,这可以是一个字面字符串,也可以是存储在数组中的字符串。
下面演示了如何使用这种对象:

double wt = 125.8;
outFile << wt;//向fish.txt写一个数wt
char line[81] = "Objects are closer than they appear.";
fout << line << endl;//向fout写入一串数组

重要的是,声明一个ofstream对象并将其同文件关联起来后,便可以像使用cout那样使用它。所有可以用于cout的操作和方法(如<<、endl和setf())都可以用于ofstream对象(如前述示例中的outFile和fout)

总之,使用文件输出的主要步骤如下:

  • 1、包含头文件fstream
  • 2、创建一个ofstream对象
  • 3、将该ofstream对象同一个文件关联起来
  • 4、就像使用cout那样使用该ofstream对象
3、程序清单6.15

要求用户输入信息,然后将信息显示到屏幕上,再将这些信息写入到文件中。读者可以使用文本编辑器来查看该输入文件的内容。

#include <iostream>
#include <fstream>

int main()
{
    using namespace std;

    char automobile[50];
    int year;
    double a_price;
    double d_price;

    ofstream outFile;
    outFile.open("carinfo.txt");//在这里,程序运行之前,文件不存在。这种情况下,方法open()将新建一个名为carinfo.txt的文件。
    //如果文件carinfo.txt已经存在,默认情况下,open()将首先截断该文件,即将其长度截短到零--丢弃原有的内容,然后将新的输出加入到该文件中。
    //第17章介绍如何修改这种默认的行为

    cout << "Enter the make and model of automobile: ";
    cin.getline(automobile, 50);
    cout << "Enter the model year: ";
    cin >> year;
    cout << "Enter the original asking price: ";
    cin >> a_price;
    d_price = 0.913 * a_price;

    cout << fixed;
    cout.precision(2);
    cout.setf(ios_base::showpoint);
    cout << "Make and model: " << automobile << endl;
    cout << "Year: " << year << endl;
    cout << "Was asking $" << a_price << endl;
    cout << "Now asking $" << d_price << endl;

    outFile << fixed;
    outFile.precision(2);//创建好ofstream对象后,便可以像使用cout那样使用它。
    outFile.setf(ios_base::showpoint);
    outFile << "Year: " << year << endl;
    outFile << "Was asking $" << a_price << endl;
    outFile << "Now asking $" << d_price << endl;

    outFile.close();//不需要使用文件名作为参数,因为outFile已经同特定的文件关联起来。忘记关闭文件,程序正常终止时将自动关闭它。
    return 0;
}

该程序的最后一部分与cout部分相同,只是将cout替换为outFile而已。下面是运行情况:
请添加图片描述
屏幕输出是使用cout的结果,查看该程序的可执行文件所在的目录,将看到一个名为carinfo.txt的新文件,根据编译器的设置,该文件也可以在其他文件夹),

请添加图片描述
其中包含使用outFile生成的输出。如果使用文本编辑器打开该文件,将发现其内容如下:

请添加图片描述

**警告:**打开已有的文件,以接受输出时,默认将它长度截短为零,因此原来的内容将丢失。

打开文件用于接受输入时可能失败。指定的文件可能已经存在,但禁止对其进行访问,因此细心的程序源将检查打开文件的操作是否成功,这将在下一个例子介绍。

6.8.3 读取文本文件

1、cin和文本文件输入

接下里介绍文本文件输入,它是基于控制台输入的,控制台输入涉及多个方面,下面首先总结这些方面:

  • 1、必须包含头文件iostream
  • 2、头文件iostream定义了一个用处理输入的istream类
  • 3、头文件iostream声明了一个名为cin的istream变量(对象)
  • 4、必须指明名称空间std;例如,为引用元素cin,必须使用编译指令using或前缀std::。
  • 5、可以结合使用cin和运算符>>来读取各种类型的数据
  • 6、可以使用cin和get()方法来读取一个字符,使用cin和getline()来读取一行字符。
  • 7、可以结合使用cin和eof()、fail()方法来判断输入是否成功。
  • 8、对象cin本身被用作测试条件时,如果最后一个读取操作成功,它将被转换为布尔值true,否则被转换为false。

文件输出与此极其相似

  • 1、必须包含头文件fstream
  • 2、头文件fstream定义了一个用于处理输入的ifstream类
  • 3、需要声明一个或多个ifstream变量(对象),并以自己喜欢的方式对其进行命名,条件是遵守常用的命名规则。
  • 4、必须指明名称空间std;例如,为引用元素ifstream,必须使用编译指令using或前缀std::。
  • 5、需要将ifstream对象与文件关联起来。为此,方法之一是使用open()方法。
  • 6、使用完文件后,应使用close()方法将其关闭。
  • 7、可结合使用ifstream对象和运算符>>来读取各种类型的数据。
  • 8、可以使用ifstream对象和get()方法来读取一个字符,使用ifstream对象和getline() 来读取一行字符。
  • 9、可以结合使用ifstream和eof()、fail()等方法来判断输入是否成功。
  • 10、ifstream对象本身被用作测试条件时,如果最后一个读取操作成功,它将被转换为布尔值true,否则转换为false。
2、ifstream对象的使用

注意,虽然头文件iostream提供了一个预先定义好的名为cin的istream对象,但您必须声明自己的ifstream对象,为其命名,并将其同文件关联起来。下面演示了如何声明这种对象:

ifstream inFile;
ifstream fin;

下面演示了如何将这种对象与特定的文件关联起来:

inFile.open("bowling.txt");
char filename[50];
cin >> filename;
fin.open(filename);

方法open()接受一个C-风格字符串作为参数,这可以是一个字面字符串,也可以是存储在数组中的字符串,下面演示了如何使用这种对象:

double wt;
inFile >> wt;
char line[81];
fin.getline(line, 81);

声明一个ifstream对象并将其同文件关联起来后,便可以像使用cin那样使用它,所有可用于cin的操作和方法都可用于ifstream对象(如前述示例中的inFile和fin)

如果试图打开一个不存在的文件用于输入,情况会如何?
这种错误将导致后面使用ifstream对象进行输入时失败。检查文件是否被成功打开的首先方法是使用方法is_open(),为此,可以使用类似于下面的代码:

inFile.open("bowling.txt");
if (!inFlie.is_open())
{
	exit(EXIT_FAILURE);
}

如果文件被成功地打开,方法is_open()将返回true;因此如果文件没有被打开,表达式!inFile.is_open()将为true。

函数exit()的原型是在有文件cstdlib中定义的,在该头文件中,还定义了一个用于同操作系统通信的参数值EXIT_FAILURE。函数exit()终止程序。

方法is_open()是C++中相对较新的内容。如果读者的编译器不支持它,可使用较老的方法good()来代替。正如第17章讨论的,方法good()在检查可能存在的问题方面,没有is_open()那么广泛。

3、程序清单6.16

程序打开用户指定的文件,读取其中的数字,然后指出文件中包含多少个值以及它们的和与平均值。正确地设计输入循环至关重要,详细请参阅后面的“程序说明”。注意,通过使用了if语句,该程序受益匪浅。

#include <iostream>
#include <fstream>
#include <cstdlib>
const int SIZE = 60;
int main()
{
    using namespace std;
    char filename[SIZE];
    ifstream inFile;
    cout << "Enter name of data file: ";
    cin.getline(filename,SIZE);
    inFile.open(filename);
    if (!inFile.is_open())
    {
        cout << "Could not open the file " << filename << endl;
        cout << "Program terminating.\n";
        exit(EXIT_FAILURE);
    }
    double value;
    double sum = 0.0;
    int count = 0;

    inFile >> value;
    while (inFile.good())//这里有点问题,之后有时间修改
    //方法good()指出最后一次读取输入的操作是否成功,这点至关重要。这意味着应该在执行读取输入的操作后,立刻应用这种测试。
    //一种标准被方法是,在循环之前(首次执行循环测试前)放置一条输入语句,并在循环的末尾(下次执行循环测试之前)放置另一条输入语句
    /*
	inFile >> value;
	while (inFile.good())
	{
		//loop body
		inFile >> value;
	}
	可对上述代码精简:表达式inFile>>value的结果为inFile,而在需要一个bool值得情况下,inFile的结果为inFile.good(),即true或false。
	则:
	while (inFile >> value)
	{
		//loop body
	}
	*/
    {
        ++count;
        sum += value;
        inFile >> value;
    }
    if (inFile.eof())//用于判断是否到达EOF
        cout << "End of file reached.\n";
    else if (inFile.fail())//fail()可用于检查EOF和类型不匹配
        cout << "Input terminated by data mismatch.\n";
    else
        cout << "Input terminated for unknown reason.\n";
    if (count == 0)
        cout << "No data processed.\n";
    else
    {
        cout << "Items read: " << count << endl;
        cout << "Sum: " << sum << endl;
        cout << "Average: " << sum / count << endl;
    }
    inFile.close();
    return 0;
}

要运行代码,首先需要创建一个包含数字的文本文件。为此,可以使用文本编辑器(如用于编写源代码的文本编辑器)。假设该文件名为scores.txt,
请添加图片描述

包含的内容如下:

请添加图片描述

18 19 18.5 13.5 14
16 19.5 20 18 12 18.5
17.5

程序还必须能够找到这个文件。

通常,除非在输入的文件名中包含路径,否则程序将在可执行文件所属的文件夹中查找。

警告: Windows文本文件的每行都以回车字符和换行符结尾;通常情况下,C++在读取文件时将这两个字符转换为换行符,并在写入文件时执行相反的转换。有些文本编辑器(如Metrowerks CodeWarrior IDE编辑器),不会自动在最后一行末尾加上换行符。因此,如果读者使用的是这种编辑器,请在输入最后的文本后按下回车键,然后再保存文件。

下面是程序的运行情况:

程序说明:
检查文件是否被成功打开至关重要,检查文件是否被成功打开可避免这种精力放在错误地方的情况发生,下面是一些可能出问题的地方:

  • 指定的文件可能不存在;
  • 文件可能位于另一个目录中;
  • 访问可能被拒绝;
  • 用户可能输错了文件名或省略了文件扩展名。

读者需要特别注意的是文件读取循环的正确设计:

  • 首先,程序读取文件时不应超过EOF。如果最后一次读取数据时遇到EOF,方法eof()将返回true。
  • 其次,程序可能遇到类型不匹配的i情况。例如,程序清单6.16期望文件中只包含数字。如果最后一次读取操作中发生了类型不匹配的情况,方法fail()将返回true(如果遇到了EOF,该方法也将返回true)。
  • 最后,可能遇到意外的情况,如文件受损或硬件故障。如果最后一次读取文件时发生了这样的问题,方法bad()将返回true。不要分别检查这些情况,一种更简单的方法是使用good()方法,该方法在没有发生任何错误时返回true。

6.9 总结

请添加图片描述
请添加图片描述

6.10 复习题

请添加图片描述
请添加图片描述
1、请看下面两个计算空格和换行符数目的代码片段
//版本1

while (cin.get(ch))
{
	if (ch == '')
		spaces++;
	if (ch == '\n')
		newlines++;
}

//版本2

while (cin.get(ch))
{
	if (ch == ' ')
		spaces++;
	else if (ch == '\n')
		newlines++;
}

第二种格式比第一种格式好在哪里喃?

答:这两个版本将给出相同的答案,但if else版本的效率更高。例如,考虑当ch为空格时的情况。版本1对空格加1,然后看它是否为换行符。这将浪费时间,因为程序已经知道ch为空格,因此它不是换行符。在这种i情况下,版本2将不会查看字符是否为换行符。

2、在程序清单6.2中,用ch+1替换++ch将会发生什么情况?

程序清单6.2所示:

#include <iostream>
int main()
{
    using namespace std;
    char ch;
    cout << "Type, and I shall repeat.\n";
    cin.get(ch);
    while(ch != '.')
    {
        if (ch == '\n')
            cout << ch;
        else
            cout << ++![请添加图片描述](https://img-blog.csdnimg.cn/d84a35afeb1d47a5aaa474338ae81f11.png)
ch;
        cin.get(ch);
    }
    cout << "\nPlease excuse the slight confusion.\n";
    return 0;
}

该程序的运行情况:
请添加图片描述
将++ch改为ch+1,运行结果变成:
请添加图片描述
这是由于cout处理不同的类型有不同的方式。

++ch和ch+1得到的数值相同。但++ch的类型为char,将作为字符打印,而ch+1是int类型(因为将char和int相加),将作为数字打印。

3、请认真考虑下面的程序:

#include <iostream>
using namespace std;
int main()
{
	char ch;
	int ct1, ct2;

	ct1 = ct2 = 0;
	while ((ch = cin.get()) != '$')
	{
		cout << ch;
		ct1++;
		if (ch = '$')
			ct2++;
		cout << ch;
	}
	cout << "ct1 = " << ct1 << ", ct2 = " << ct2 << "\n";
	return 0;
}

假设输入如下(请在每行末尾按回车键)
Hi!
Send $10 or $20 new!
则输出将是什么(还记得吗,输入被缓冲)?

Hi!
H$i$!$

$Send $10 or $20 now!
S$e$n$d$ $ ct1 = 9, ct2 = 9

4、创建表示下述条件的逻辑表达式:
a、weight大于或等于115,但小于125.

weight >= 115 && weight < 125

b、ch为q或Q

ch == 'q' || ch == 'Q'

c、x为偶数,但不是26

x != 26 && (x % 2 == 0)

d、x为偶数,但不是26的倍数

x % 2 ==0 && (x % 26 != 0)

e、donation为1000-2000或guest为1

donatin <= 2000 && donation >= 1000 || guest == 1

f、ch是小写字母或大写字母(假设小写字母是依次编码的,大写字母也是依次编码的,但在大小写字母间编码不是连续的)。

(ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') 

5、在英语中,“I will not not speak(我不会不说)”的意思与“I will speak(我要说)”相同。在C++中,!!x是否与x相同喃?

不一定,例如,如果x为10,!x为0,!!x为1。然而,如果x为bool变量,则!!x为x。

6、创建一个条件表达式,其值为变量的绝对值。也是说,如果变量x为正,则表达式的值为x;但如果x为负,则表达式的值为-x——这是一个正值。

(x < 0) ? -x : x;

7、用switch改写下面的代码片段:

if (ch == 'A')
	a_grade++;
else if (ch == 'B')
	b_grade++;
else if (ch == 'C')
	c_grade++;
else if (ch == 'D')
	d_grade++'
else 
	f_grade++;

参考答案:

switch (ch)
{
	case 'A': a_grade++;break;
	case 'B': b_grade++;break;
	case 'C': c_grade++;break;
	case 'D': d_grade++;break;
	default:  f_grade++;break;
}

8、对于程序清单6.10,与使用数字相比,使用字符(如a或c)表示菜单选项和case标签有什么优点呢?(提示,想想用户输入q和输入5的情况)

如果使用整数标签,且用户输入了非整数(如q),则程序将因为整数输入不能处理字符而挂起。但是,如果使用字符标签,而用户输入了整数(如5),则字符输入将5作为字符处理,然后,switch语句的default部分将提示输入另一个字符。

9、请看下面的代码片段:

int line = 0;
char ch;
while (cin.get(ch))
{
	if (ch == 'Q')
		break;
	if (ch != '\n')
		continue;
	line++;
}

请重写改代码片段,不要使用break和continue语句。

int line = 0;
char ch;
while (cin.get(ch) && ch != 'Q')
{
	if (ch == '\n')
		line++;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值