第6章——分支语句和逻辑运算符

编程入门 行业动态 更新时间:2024-10-11 01:12:07

第6章——<a href=https://www.elefans.com/category/jswz/34/1762439.html style=分支语句和逻辑运算符"/>

第6章——分支语句和逻辑运算符

本章内容包括:
*if语句
*if else语句
*逻辑运算符:&&、||和!
*cctype字符函数库
*条件运算符:?:
*switch语句
*continue和break语句
*读取数字的循环
*基本文件输入\输出

	设计智能程序的一个关键是使程序具有决策能力。先前介绍了一种决策方式—循环,在循环中,程序决定是否循环。C++提供了if和switch语句来进行决策,它们是本章的主要主题。另外,条件运算符和逻辑运算符,前者提供了另一种决策方式,而后者允许将两个测试组合在一起。

6.1 if语句
当C++程序必须决定是否执行某个操作时,通常使用if语句来实现选择。if有两种格式:if和if else。首先看一看简单的if,它模仿英语,如“If you have a Captain Cookie card,you get a free cookie”。如果测试条件为true,则if语句将引导程序执行语句或语句块;如果条件是false,程序将跳过这条语句或语句块。因此,if语句让程序能够决定是否应执行特定的语句。
if语句的句法与while相似:

if (test-condition)statement
	如果test-condition为true,则程序将执行statement,后者既可以是一条语句,也可以是语句块。如果测试条件是false,程序将跳过这条语句或语句块。和循环测试条件一样,if测试条件也将被强制转换为bool值,因此0将被转换为false,非零为true。整个if语句被视为一条语句。通常情况下,测试条件都是关系表达式,如那些用来控制循环的表达式。

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

#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;
}


程序说明:仅当ch为空格时,语句++spaces;才被执行。因为语句++total;位于if语句的外面,因此在每轮循环中都将被执行。注意,字符总数中包括按回车键生成的换行符。
6.1.1 if else语句
if语句让程序决定是否执行特定的语句或语句块,而if else语句则让程序决定执行两条语句或语句块中的哪一条,这种语句对于选择其中一种操作很有用。C++的if else语句模仿了简单的英语。

程序清单:通过对字母进行加密编码来修改输入的文本(换行符不变)。这样,每个输入行都被转换为一行输出,且长度不变。这意味着程序对换行符采用一种操作,而对其他字符采用另一种操作。if else使得这项工作非常简单。

#include <iostream>
int main()
{char ch;std::cout<<"Type,and I shall repeat.\n";std::cin.get(ch);while (ch != '.'){if (ch == '\n')std::cout<<ch;elsestd::cout<<++ch;std::cin.get(ch);}std::cout<<"\nPlease excuse the slight confusion.\n";return 0;
}


6.1.2 格式化if else语句
if else中的两种操作都必须是一条语句。如果需要多条语句,必须用大括号将它们括起来,组成一个块语句。和有些语言不同的是,由于C++不会自动将if和else之间的所有代码视为一个代码块,因此必须使用大括号将这些语句组成一个语句块。例如,下面的代码将出现编译器的错误:

if (ch == 'z')zerro++;	//if ends herecout<<"Another Zorro candidate\n";
elsedull++;cout<<"Not a Zorro andidate\n";
编译器把它看作是一条以Zorro++;语句结尾的简单if语句,接下来是一条cout语句。到目前为止,一切正常。但之后编译器发现一个独立的else,这将被视为语法错误。
请求加入大括号,将语句组合成一个语句块:
if (ch == 'z')
{zerro++;	//if ends herecout<<"Another Zorro candidate\n";
}
else
{dull++;cout<<"Not a Zorro andidate\n";
}

由于C++是自由格式的语言,因此只要使用大括号将语句括起,对大括号的位置没有任何限制。上述代码演示了一种流行的格式,下面是另一种流行的格式:

if (ch == 'z'){zorro++;cout<<"Another Zorro candidate\n";}
else{dull++;cout<<"Not a Zorro candidate\n";}

第一种格式强调的是语句的块结构,第二种格式则将语句块与关键字if和else更紧密地结合在一起。这两种风格清晰、一致,应该能够满足要求。

6.1.3 if else if else结构
与实际生活中发生的情况类似,计算机程序也可能提供两个以上的选择。可以将C++的if else 语句进行扩展来满足这种需求。正如读者知道的,else之后是一条语句,也可以是语句块。由于if else语句本身是一条语句,所以可以放在else的后面:

if (ch = = 'A')a_grade++;
elseif(ch == 'B')b_grade++;elsesoso++;
如果ch不是'A',则程序将执行else。执行到那里,另一个if else又提供了两种选择。C++的自由格式允许将这些元素排列成便于阅读的格式:
if (ch == 'A')a_grade++;
else if (ch =='B')b_grade++;
elsesoso++;

这看上去是一个新的控制结构——if else if else 结构。但实际上,它只是一个if else被包含在另一个if else中。修订后的格式更为清晰,是程序员通过浏览代码便能确定不同的选择。整个构造仍被视为一条语句。

程序清单:使用if else if else这种格式创建了一个小型测验程序

#include <iostream>
const int Fave = 27;
int main()
{using namespace std;int n;cout<<"Enter a number in the range 1-100 to find ";cout<<"my favorite number: ";do{cin>>n;if (n<Fave)cout<<"Too low -- guess again: ";else if (n>Fave)cout<<"Too high -- guess again: ";elsecout<<Fave<<" is right!\n";}while (n != Fave);return 0;
}


条件运算符和错误示范
很多程序员将更直观的表达式variable == value反转为value ==variable,以此来捕获将相等运算符误写为赋值运算符的错误。例如,下面的条件有效,可以正常工作:

if (3 == myNumber)
	但如果错误地使用下面的条件,编译器将生成错误消息,因为它以为程序员将一个值赋给一个字面值(3总是等于3,而不能将另一个值赋给它):
if (3 = myNumber)

假设犯了类似的错误,但使用的是前一种表示方法:

if (myNumber = 3)

编译器将只是把3赋给myNumber,而if中的语句块将包含非常常见的、而又非常难以发现的错误(然而,很多编译器会发出警告,因此注意警告是明智的)。一般来说,编写让编译器能够发现错误的代码,比找出导致难以理解的错误的原因要容易得多。

6.2 逻辑表达式
经常需要测试多种条件。例如,字符要是小写,其值就必须大于或等于’a’,且小于或等于’z’。如果要求用户使用y或n进行响应,则希望用户无论输入大写(Y和N)或小写都可以。为满足这种需要,C++提供了3种逻辑运算符,来组合或修改已有的表达式。这些运算符分别是逻辑OR(||)、逻辑AND(&&)和逻辑NOT(!)。

6.2.1 逻辑OR运算符:||
在英语中,当两个条件中有一个或全部满足要求时,可以用单词or来指明这种情况。例如,如果您或您的配偶在MegaMicro公司工作,您就可以参加MegaMicro公司的野餐会。C++可以采用逻辑OR运算符||,将两个表达式组合在一起。如果原来表达式中的任何一个或全部为true,则得到的表达式的值为true;否则,表达式的值为false,下面是一些例子:

5 == 5 || 5 == 9		//true because first expression is true
5 > 3 || 5 > 10		//true because first expression is true 
5 > 8 || 5 < 10		//true because second expression is true 
5 < 8 || 5 > 2 		//true because both expressions is true 
5 > 8 || 5 < 2 		//false because both expressions is false
	由于||的优先级比关系运算符低,因此不需要在这些表达式中使用括号。

程序清单:在一条if语句中使用||运算符来检查某个字符的大写或小写。另外,它还使用了C++字符串的拼接特性将一个字符串拼接在3行中。

#include <iostream>
int main()
{using namespace std;cout<<"This program may reformat your hard disk\n""and destroy all your data.\n""Do you wish to continue?<y/n>";char ch;cin>>ch;if (ch == 'y' || ch == 'Y')cout<<"You were warned!\a\a\n";else if (ch == 'n' || ch == 'N')cout<<"A wise choice ... bye\n";elsecout<<"That wasn't a y or n!Apparently you ""can't follow\ninstructions,so ""I'll trash you disk anyway.\a\a\n";return 0;
}


由于程序只读取一个字符,因此只读取响应的第一个字符。这意味着用户可以用NO!(而不是N)进行回答,程序将只读取N。然而,如果程序后面再读取输入时,将从O开始读取。

6.2.2 逻辑AND运算符:&&
逻辑AND运算符&&,也是将两个表达式组合成一个表达式。仅当原来的两个表达式都为true时,得到的表达式的值才为true。

5 == 5 && 4 == 4		//true because both expressions are true 
5 == 3 && 4 == 4		//false because first expression is false
5 > 3 && 5 > 10			//false because second expression is true
5 > 8 && 5 < 10 		//false because first expression is false
5 < 8 && 5 > 2			//true because both expressions are true
5 > 8 && 5 < 2			//false because both exprssions are false
	由于&&的优先级低于关系运算符,因此不必在这些表达式中使用括号。和||运算符一样,&&运算符也是顺序点,因此将首先判断左侧,并且在右侧被判定之前产生所有的副作用。如果左侧为false,则整个逻辑表达式必定为false,在这种情况下,C++将不会再对右侧进行判定。

程序清单:演示如何使用&&来处理一种常见的情况——由于两种不同的原因而结束while循环。一个while循环将值读入到数组。一个测试在数组被填满时循环结束,另一个测试让用户通过输入一个负值来提前结束循环。该程序使用&&运算符将两个测试组合成一个条件。该程序还使用了两条if语句、一条if else语句和一个for循环。

#include <iostream>
const int ArSize = 6;
int main()
{using namespace std;float naaq[ArSize];cout<<"Enter the NAAQs (New Age Awareness Quotients)"<<"of\nyour neighbors.Program terminates "<<"when you make\n"<<ArSize<<" entries "<<"or enter a negative value.\n";int i = 0;float temp;cout<<"First value: ";cin>>temp;while(i<ArSize && temp >=0){naaq[i] = temp;++i;if (i<ArSize){cout<<"Next value: ";cin>>temp;}}if (i == 0)cout<<"No data--bye\n";else{cout<<"Enter your NAAQ: ";float you;cin>>you;int count = 0;for (int j = 0;j < i;j++)if(naaq[j]>you)++count;cout<<count;cout<<" of your neighbors have greater awareness of\n"<<"the New Age than you do.\n";}return 0;
}


该程序首先将第一个输入值读入到临时变量temp中。然后,while测试条件查看数组中是否还有空间以及输入值是否为负。如果满足条件,则将temp的值复制到数组中,并将数组索引加1,。此时,由于数组下标从0开始,因此i指示输入了多少个值。也是说,如果i从0开始,则第一轮循环将一个值赋给naaq[0],然后将i设置为1.
当数组被填满或用户输入了负值时,循环将结束。注意,仅当i小于ARSize时,即数组中还有空间时,循环才将另外一个值读入到temp中。
获得数据后,如果没有输入任何数据,程序将使用if else语句指出这一点,如果存在数据,就对数据进行处理。

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

程序清单:演示了一种用于处理一系列消息的技术。与char指针变量可以通过指向一个字符串的开始位置来标识该字符串一样,char指针数组也可以标识一系列字符串,只要将每一个字符串的地址赋给各个数组元素即可。使用qualify数组来存储4个字符串的地址。

#include <iostream>
const char * qualify[4] = 
{"10.000-meter race.\n","mud tug-of-war.\n","masters canoe jousting.\n","pie-throwing festival.\n"
};
int main()
{using namespace std;int age;cout<<"Enter your age in years: ";cin>>age;int index;if (age=17&&age<35)index=0;else if(age >= 35&&age<50)index=1;else if(age>=50&&age<65)index=2;else index=3;cout<<"You qualify for the "<<qualify[index];return 0;
}


6.2.4 逻辑NOT运算符:!
!运算符将它后面的表达式的真值取反,也是说,如果expression为true,则!expression是false;如果expression是false,则!expression是true。

程序清单:使用!运算符来筛选可赋给int变量的数字输入。如果用户定义的函数is int()的参数位于int类型的取值范围内,则它将返回true。然后,程序使用while测试来拒绝不在该取值范围内的值。

#include <iostream>
#include <climits>
bool is_int (double);
int main()
{using namespace std;double num;cout<<"Yo,dude! Enter an integer value: ";cin>>num;while (!is_int (num)){cout<<"Out of range -- please try again: ";cin>>um}int val = int (num);cout<<"You've entered the integer "<<val<<"\nBye\n";return 0;
}bool is_int(double x)
{if (x<=INT_MAX&&x>=INT_MIN)return true;else return false;
}


程序说明:如果给读取int值的程序一个过大的值,很多C++实现只是将这个值截短为合适的大小,并不会通知丢了数据。此程序避免了这样的问题,它首先将可能的int值作为double值来读取。double类型的精度足以存储典型的int值,且取值范围更大。另一种选择是,使用longlong来存储输入的值,因为其取值范围比int大。
布尔函数is_int()使用了climits文件中定义的两个符号常量(INT_MAX和INT_MIN)来确定其参数是否位于适当的范围内。如果是,该函数返回true,否则返回false。
main()程序使用while循环来拒绝无效输入,直到用户输入有效的值为止。可以在输入超出取值范围时显示int的界限,这样程序将更为友好。确认输入无效后,程序将其赋给一个int变量。

6.2.5 逻辑运算符细节
正如本章前面指出的,C++逻辑OR和逻辑AND运算符的优先级都低于关系运算符。
另一方面,!运算符的优先级高于所有的关系运算符和算术运算符。因此,要对表达式求反,必须用括号将其括起。
虽然C++运算符的优先规则常可能不使用括号便可以编写复合比较的语句,但最简单的方法还是用括号将测试进行分组,而不管是否需要括号,这样可以减少由于没有准确记住所使用的规则而出错的可能性。

6.2.6 其他表达方式
并不是所有的键盘都提供了用作逻辑运算符的符号,因此C++标准提供了另一种表示方式,标识符and、or和not都是C++保留字,这意味着不能将它们用作变量名等。它们不是关键字,因为它们都是已有语言特性的另一种表示方式。另外,它们并不是C语言中的保留字,但C语言程序可以将它们用作运算符,只要在程序中包含了头文件iso646.h。C++不要求使用头文件。

6.3 字符函数库 cctype
C++从C语言继承了一个与字符相关的、非常方便的函数软件包,它可以简化诸如确定字符是否为大写字母、数字、标点符号等工作,这些函数的原型是在头文件cctype中定义的。例如,如果ch是一个字母,则isalpha(ch)函数返回一个非零值,否则返回0。同样,如果ch是标点符号,函数ispunct(ch)将返回true。这些函数的返回类型为int,而不是bool,但通常bool转换让您能够将它们视为bool类型。)
使用这些函数比使用AND和OR运算符更方便。例如,下面是使用AND和OR来测试字符ch是不是字符的代码:

if ((ch >= 'a'&&ch <= 'z'))

与使用isalpha()相比:

if (isalpha(ch))
	isalpha()不仅更容易使用,而且更通用。AND/OR格式假设A-Z的字符编码是连续的,其他字符的编码不在这个范围内。这种假设对于ASCII码来说是成立的,但通常并非如此。程序清单:演示一些ctype库函数,它使用isalpha()来检查字符是否为字母字符,使用isdigits()来测试字符是否为数字字符,使用isspace()来测试字符是否为空白,如换行符、空格和制表符,使用ispunct()来测试字符是否为标点符号。
#include <iostream>
#include <cctype>
int main()
{using namespace std;cout<<"Enter text for analysis,and type @"" to terminate input.\n";char ch;int whitespace = 0;int digits = 0;int chars = 0;int punct = 0;int others = 0;cin.get(ch);while (ch!='@'){if(isalpha (ch))chars++;else if(isspace(ch))whitespace++;else if(isdigit(ch))digits++;else if(ispunct(ch))punct++;elseothers++;cin.get(ch);}cout<<chars<<" letters, "<<whitespace<<" whitespace, "<<digits<<" digits, "<<punct<<" punctuations, "<<others<<" others.\n";return 0;
}



6.4 ?:运算符
C++有个常被用来代替if else语句的运算符,这个运算符被称为条件运算符(?:),它是C++中唯一有一个需要3个操作数的运算符。该运算符的通用格式如下:

expression1 ? expression2 : expression3
	如果expression1为true,则整个条件表达式的值为expression2的值;否则,整个表达式的值为expression3的值。下面的两个示例演示了该运算符是如何工作的:
5>3?10:12		//5>3 is true, so expression value is 10
3==9?25:18		//3 == 9 is false,so expression value is 18
	可以这样解释第一个示例:如果5大于3,则整个表达式的值为10,否则为12.当然,在实际的编程中,这些表达式中将包含变量。

程序清单:使用条件运算符来确定两个值中较大的一个

#include <iostream>
int main()
{using namespace std;int a,b;cout<<"Enter two integers: ";cin>>a>>b;cout<<"The larger of "<<a <<" and "<<b;int c=a>b?a:b;cout<<" is "<<c<<endl;return 0;
}


它与下面的语句等效:

int c;
if (a>b)c=a;
elsec=b;

与if else序列相比,条件运算符更简洁,但第一次遇到时不那么容易理解。这两种方法之间的区别是,条件呢运算符生成一个表达式,因此是一个值,可以将其赋给变量或将其放到一个更大的表达式中。

6.5 switch 语句
假设要创建一个屏幕菜单,要求用户从五个选择中选择一个,例如便宜、适中、昂贵、奢侈、过度。虽然可以扩展if else if else 序列来处理这5种情况,但C++的switch语句能够更容易地从大型列表中进行选择。下面是switch语句的通用格式:

switch (integer expression)
{case label1:statement(s)case label2:statement(s)default : statement(s)
}
	C++的switch语句就像指路牌,告诉计算机接下来应执行哪行代码。执行到switch语句时,程序将跳到使用integer-expression的值标记哪一行。例如,如果integer-expression的值为4,则程序将执行标签为case4:那一行。顾名思义,integer-expression必须是一个结果为整数值的表达式。另外,每个标签都必须是整数常量表达式。最常见的标签是int或char常量,也可以是枚举量。如果integer-expression不与任何标签匹配,则程序将跳到标签为default的那一行。default标签是可选的,如果被省略,而又没有匹配的标签,则程序将跳到switch后面的语句处执行。switch语句与Pascal等语言中类似的语句之间存在重大的差别。C++中的case标签只是行标签,而不是选项之间的界线。也是说,程序跳到switch中特定代码行后,将依次执行之后的所有语句,除非有明确的其他指示。程序不会在执行到下一个case处自动停止,要让程序执行完一组特定语句后停止,必须使用break语句。这将导致程序跳到switch后面的语句处执行。

程序清单:演示了如何使用switch和break来让用户选择简单菜单。该程序使用showmenu()函数显示一组选项,然后使用switch语句,根据用户的反应执行相应的操作。

#include <iostream>
using namespace std;
void showmenu();
void report();
void comfort();
int main()
{showmenu();int choice;cin>>choice;while (choice != 5){switch (choice){case 1 :cout<<"\a\n";break;case 2 :report();break;case 3 :cout<<"The boss was in all day.\n";break;case 4 :comfort();break;default :cout<<"That's not a choice.\n";}showmenu();cin>>choice;}cout<<"Bye!\n";return 0;
}
void showmenu()
{cout<<"Please enter 1,2,3,4,or 5:\n""1) alarm		2)report\n""3)alibi		4)comfort\n""5)quit\n";
}
void report()
{cout<<"It's been an excellent week for business.\n""Sales are up 120%.Expenses are down 35%.\n";
}
void comfort()
{cout<<"Your employees think you are the finest CEO\n""in the industry.The board of directors thinks\n""you are the finest CEO in the industry.\n";
}


程序说明:当用户输入了5时,while循环结束。输入1到4将执行switch列表中相应的操作,输入6将执行默认语句。
为让这个程序正确运行,输入必须是整数。例如,如果输入一个字母,输入语句将失效,导致循环不断运行,直到您终止程序。为应对不按指示办事的用户,最好使用字符输入。
如前所述,该程序需要break语句来确保只执行switch语句中的特定部分。为检查情况是否如此,可以删除程序清单中的break语句,然后看看其运行情况。

6.5.1 将枚举量用作标签
程序清单使用enum定义了一组相关的常量,然后在switch语句中使用这些常量。通常,cin无法识别枚举类型(它不知道程序员是如何定义它们的),因此该程序要求用户选择选项时输入一个整数。当switch语句将int值和枚举量标签进行比较时,将枚举量提升为int。另外,在while循环测试条件中,也会将枚举量提升为int类型。

#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<<"Her lips were red.\n";break;case orange :cout<<"Her hair was orange.\n";break;case yellow :cout<<"Her shoes were yellow.\n";break;case green :cout<<"Her nails were green.\n";break;case blue :cout<<"Her sweatsuit wass blue.\n";break;case violet :cout<<"Her eyes were violet.\n";break;case indigo :cout<<"Her mood is indigo.\n";break;}cout<<"Enter color code (0-6): ";cin>>code;}cout<<"Bye\n";return 0;
}


6.5.2 switch 和if else
switch语句和if else语句都允许程序从选项中进行选择。相比之下,if else更通用。例如,它可以处理取值范围,如下所示:

if (age > 17 && age < 35)index=0;
else if (age >= 35 && age < 50)index=1;
else if (age >= 50 && age <65)index=2;
elseindex=3;
	然而,switch并不是为处理取值范围而设计的。switch语句中的每一个case标签都必须是一个单独的值。另外,这个值必须是整数(包括char),因此switch无法处理浮点测试。另外case标签值还必须是常量。如果选项涉及取值范围、浮点测试或两个变量的比较,则应使用if else语句。然而,如果所有选项都可以使用整数常量来标识,则可以使用switch语句或if else语句。由于switch语句是专门为这种情况设计的,因此,如果选项超过两个,则就代码长度和执行速度而言,switch语句的效率更高。提示:如果既可以使用if else if语句,也可以使用switch语句,则当选项不少于3个时,应使用switch语句。

6.6 break 和 continue 语句
break和continue语句都使程序能够跳过部分代码。可以在switch语句或任何循环中使用break语句,使程序跳到switch或循环后面的语句处执行。continue语句用于循环中,让程序跳过循环体中余下的代码并开始新一轮循环。

程序清单:演示了这两条语句是如何工作的,该程序让用户输入一行文本。循环将回显每个字符,如果该字符为句点,则使用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<<"Complete line:\n"<<line<<endl;cout<<"Line 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"<<spaces<<" spaces\n";cout<<"Done.\n";return 0;
}


程序说明:虽然continue语句导致该程序跳过循环体的剩余部分,但不会跳过循环的更新表达式。在for循环中,continue语句使程序直接跳到更新表达式处,然后跳到测试表达式处。然而,对于while循环来说,continue将使程序直接跳到测试表达式处,因此while循环体中位于continue之后的更新表达式都将被跳过。在某些情况下,这可能是个问题。

6.7 读取数字的循环
假设要编写一个将一系列数字读入到数组中的程序,并允许用户在数组填满之前结束输入。一种方法是利用cin。
如果用户输入一个单词,而不是一个数字,情况将如何呐?发生这种类型不匹配的情况时,将发生4种情况:
*n的值保持不变
*不匹配的输入将被留在输入队列中
*cin对象中的一个错误标记被设置
*对cin方法的调用将返回false(如果被转换为bool类型)
方法返回false意味着可以用非数字输入来结束读取数字的循环。非数字输入设置错误标记意味着必须重置该标记,程序才能继续读取输入。clear()方法重置错误输入标记,同时也重置文件尾(EOF条件)。输入错误和EOF都将导致cin返回false。
假设要编写一个程序,来计算每天平均捕获的鱼的重量。这里假设每天最多捕获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]){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";elsecout<<total/i<<"=average weight of "<<i<<" fish\n";cout<<"Done.\n";return 0;
}


6.8 简单文件输入/输出
有时候,通过键盘输入并非是最好的选择。例如,假设您编写了一个股票分析程序,并下载了一个文件,其中包含1000中股票的价格。在这种情况下,让程序直接读取文件,而不是手工输入文件中所有的值,将方便得多。同样,让程序将输出写入到文件将更为方便,这样可得到有关结果的永久性记录。
幸运的是,C++使得程序将读取键盘输入和在屏幕上显示输出(统称为控制台输入/输出)的技巧用于文件输入/输出(文件I/O)非常简单。

6.8.1 文本I/O和文本文件
这里介绍一下文本I/O的概念。使用cin进行输入时,程序将输入视为一系列字节,其中每个字节都被解释为字符编码。不管目标数据类型是什么,输入一开始都是字符数据——文本数据。然后,cin对象负责将文本转换为其他类型。为说明这是如何完成的,来看一些处理同一个输入行的代码。
假设有如下示例输入行:
38.5 19.2
来看一下使用不同数据类型的变量来存储时,cin是如何处理该输入行的。首先,来看使用char数据类型的情况:
char ch;
cin>>ch;
输入行中的第一个字符被赋给ch。在这里,第一个字符是数字3,其字符编码(二进制)被存储在变量ch中。输入和目标变量都是字符,因此不需要进行转换。注意,这里存储的数值3,而是字符3的编码。执行上述输入语句后,输入队列中的下一个字符为字符8,下一个输入操作将对其进行处理。
接下来看int类型:
int n;
cin>>n;
在这种情况下,cin将不断读取,直到遇到非数字字符。也就是说,它将读取3和8,这样句点将成为输入队列中的下一个字符。cin通过计算发现,这两个字符对应数值38,因此将38的二进制编码复制到变量n中。
接下来看double类型:
double x;
cin>>x;
在这种情况下,cin将不断读取,知道遇到第一个不属于浮点数的字符。也就是说,cin读取3、8、句点和5,使得空格成为输入队列中的下一个字符。cin通过计算发现,这四个字符对应于数值38.5,因此将38.5的二进制编码(浮点格式)复制到变量x中。
接下来看看char数组的情况:
char word[50];
cin>>word;
在这种情况下,cin将不断读取,直到遇到空白字符。也就是说,它读取3、8、句点和5,使得空格成为输入队列中的下一个字符。然后,cin将这4个字符的字符编码存储到数组word中,并在末尾加上一个空字符。这里不需要进行任何转换。
最后,来看一下另一种使用char数组来存储输入的情况:
char word[50];
cin.geline(word,50);
在这种情况下,cin将不断读取,知道遇到换行符(示例输入行少于50个字符)。所有字符都将被存储到数组word中,并在末尾加上一个空字符。换行符被丢弃,输入队列中的下一个字符是下一行中的第一个字符。这里不需要进行任何转换。
对于输入,将执行相反的转换。即整数被转换为数字字符序列,浮点数被转换为数字字符和其他字符组成的字符序列(如284.53或-1.58E+06)。字符数据不需要做任何转换。
这里的要点是,输入一开始为文本。因此,控制台输入的文件版本是文本文件,即每个字节都存储了一个字符编码的文件。并非所有文件都是文本文件,例如,数据库和电子表格以数值格式(即二进制整数或浮点格式)来存储数值数据。另外,字处理文件中可能包含文本信息,但也可能包含用于描述格式、字体、打印机等的非文本数据。
本章讨论的文件I/O箱相当于控制台I/O,因此仅适用于文本文件。要创建文本文件,用于提供输入,可使用文本编译器,如DOS中的EDIT、Windows中的“记事本”和UNIX/Linux中的vi或emacs。也可以使用字处理程序来创建,但必须将文件保存为文本格式。IDE中的源代码编译器生成的也是文本文件,事实上,源代码文件就属于文本文件。同样,可以使用文本编译器来查看通过文本输出创建的文件。

6.8.2 写入到文本文件中
对于文件输入,C++使用类似于cout的东西。有关将cout用于控制台输出的基本事实,为文件输出做准备。
*必须包含头文件iostream
*头文件iostream定义了一个用处理输出的ostream类
*头文件iostream声明了一个名为cout的ostream变量(对象)
*必须指明名称空间std;例如,为引用元素cout和endl,必须使用编译指令using或前缀std::。
*可以结合使用cout和运算符<<来显示各种类型的数据
文件输出与此极其相似
*必须包含头文件fstream
*头文件fstream定义了一个用于处理输出的ofstream类
*需要声明一个或多个ofstream变量(对象),并以自己喜欢的方式对其进行命名,条件是遵守常用的命名规则。
*必须指明名称空间std;例如,为引用元素ofstream,必须使用编译指令using或前缀std::。
*需要将ofstream对象与文件关联起来。为此,方法之一是使用open()方法。
*使用完文件后,应使用方法close()将其关闭
*可结合使用ofstream对象和运算符<<来输出各种类型的数据
注意,虽然头文件iostream提供了一个预先定义好的名为cout的ostream对象,但您必须声明自己的ofstream对象,为其命名,并将其同文件关联起来。

ofstream outFile;		//outFile an ofstream object
ofstream fout;			//fout an ofstream object

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

outFile.open("fish.txt");
char filename[50];
cin>>filename;
fout.open(filename);

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

double wt =125.8;
outFile << wt;
char line[81] = "Objects are closer than they appear.";
fout<<line<<endl;

重要的是,声明一个ofstream对象并将其同文件关联起来后,便可以像使用cout那样使用它。所有可用于cout的操作和方法(如<<、endl和setf())都可用于ofstream对象(如前述示例中的outFile和fout)。
总之,使用文件输出的主要步骤如下。
1、包含头文件fstream。
2、创建一个ofstream对象
3、将该ofstream对象同一个文件关联起来
4、就像使用cout那样使用该ofstream对象

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

#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");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<<"Year: "<<year<<endl;cout<<"Was asking $"<<a_price<<endl;cout<<"Now asking $"<<d_price<<endl;outFile<<fixed;outFile.precision(2);outFile.setf(ios_base::showpoint);outFile <<"Make  and model: "<<automobile<<endl;outFile<<"Year: "<<year<<endl;outFile<<"Was asking $"<<a_price<<endl;outFile<<"Now asking $"<<d_price<<endl;outFile.close();return 0;
}


该程序的最后一部分与cout部分相同,只是将cout替换为outFile而已。屏幕输出是使用cout的结果。如果您查看该程序的可执行文件所在的目录,将看到一个名为carinfo.txt的新文件(根据编译器的配置,该文件也可能位于其他文件夹),其中包含使用outFile生成的输出。

6.8.3 读取文本文件
接下来介绍文本文件的输入,它是基于控制台输入的。控制台输入设计多个方面,下面首先总结这些面:
*必须包含头文件iostream。
*头文件iostream定义了一个用处理输入的istream类
*头文件iostream声明了一个名为cin的istream变量(对象)
*必须指明名称空间std;例如,为引用元素cin,必须使用编译指令using或前缀std::。
*可以结合使用cin和运算符>>来读取各种类型的数据
*可以使用cin和get()方法来读取一个字符,使用cin和getline()来读取一行字符
*可以结合使用cin和eof()、fail()方法来判断输入是否成功
*对象cin本身被用作测试条件时,如果最后一个读取操作成功,它将被转换为布尔值图文,否则转换为false。

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

程序清单:程序打开用户指定的文件,读取其中的数字,然后指出文件中包含多少个值以及它们的和与平均值。正确地设计输入循环至关重要。

#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()){++count;sum+=value;inFile >>value;}if(inFile.eof())cout<<"End of file reached.\n";else if(inFile.fail())cout<<"Input terminated by data mismatch.\n";elsecout<<"Input terminated for unknown reason.\n";if (count == 0)cout<<"No data processed.\n";else{cout<<"Items resd: "<<count<<endl;cout<<"Sum: "<<sum<<endl;cout<<"Average: "<<sum/count<<endl;}inFile.close();return 0;
}

6.9 总结
便于引导程序选择不同操作的语句后,程序和编程将更有趣。C++提供了if语句、if else语句和switch语句来管理选项。if语句使程序有条件地执行语句或语句块,也就是说,如果满足特定的条件,程序将执行特定的语句或语句块。if else语句程序选择执行两个语句或语句块之一。可以在这条语句后再加上if else,以提供一系列的选项。switch语句引导程序执行一系列选项之一。

6.10 复习题
1、请看下面两个计算空格和换行符数目的代码片:

//Version 1
while (cin.get(ch))
{if (ch ==  ' ')spaces++;if (ch == '\n')newlines++;
}//version 2
while (cin.get(ch))
{if (ch == ' ')spaces++;else if (ch == '\n')newlines++;
}

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

2、用ch+1替换++ch将发生什么情况呢
答:这两个得到的数值相同,但++ch的类型为char,将作为字符打印,而ch+1是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 n o w ! 则 输 出 将 是 什 么 ? 答 : 由 于 程 序 使 用 的 是 c h = ′ 20 now! 则输出将是什么? 答:由于程序使用的是ch=' 20now!则输出将是什么?答:由于程序使用的是ch=′‘,而不是ch==’ ′ , 因 此 输 入 和 输 出 将 如 下 : H i ! H ',因此输入和输出将如下: Hi! H ′,因此输入和输出将如下:Hi!Hi ! ! !
$Send $10 or 20 n o w ! S 20 now! S 20now!Se n n nd$ c t 1 = 9 , c t 2 = 9 在 第 二 次 打 印 前 , 每 个 字 符 都 被 转 换 为 ¥ 字 符 。 另 外 , 表 达 式 c h = ct1 = 9,ct2 = 9 在第二次打印前,每个字符都被转换为¥字符。另外,表达式ch= ct1=9,ct2=9在第二次打印前,每个字符都被转换为¥字符。另外,表达式ch=的值为¥字符的编码,因此它是非零值,为true,所以每次ct2都将被加1

4、创建表示下述条件的逻辑表达式:
a.weight大于或等于115,但小于125。
weight >=15&&weight<125;
b.ch为q或Q。
ch==‘q’ || ch ‘Q’
c.x为偶数,但不是26
x%2
0&&x!=26
d.x为偶数,但不是26的倍数
x%20&&!(x%260)
e.donation为1000-2000或guest为1
donation>=1000&&donation<=2000||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++;
elsef_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、与使用数字相比,使用字符表示菜单选项和case标签有何优点呢?
答:使用整数标签,且用户输入了非整数,则程序将因为整数输入不能处理字符而挂起。但是,如果使用的字符标签,则字符输入将数字作为字符处理。然后,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++;
}

更多推荐

第6章——分支语句和逻辑运算符

本文发布于:2024-03-06 16:14:05,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1715769.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:分支   语句   运算符   逻辑

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!