文章目录
一、条件结构
1、程序的条件结构
- C++通过使用分支语句在可选择的操作中做出决定。 C++提供了两种语句和两种运算符来提供选择执行,分别是if语句,switch语句和逻辑运算符、条件运算符。后者是前者的实际化,前者提供了告诉程序要进行判断,告诉了程序要走向不同的路口,后者将每个路口要进行的实际判断,满足的条件,不同的测试(关系表达式)组合在一起,使程序有了判断的目标从而进行了数据信息筛选,而筛选的方式是通过关系表达式,算术表达式进行的。总而言之,就是告诉系统需要进行路径分支判断,再将有效信息数据化表达(可视化信息数据化的罗列),最后将这些表达(不同关系之间的关系)通过逻辑运算组合起来。
2、条件结构的优选
-
- 选择if-else语句: 对于设计到连续范围,取值范围的关联支路问题,用if-else语句更合适。
-
- 选择switch-case语句: 对于设计到特定功能对应特定路径的问题时,switch-case语句是首选,使功能结构化更清晰。例如:菜单程序,交互程序(获取键盘操作,鼠标消息等等),数组贴图等等一些指定功能的实现。
-
- 什么时候用if-if-if? if语句很普适,在哪里用都是很适合的,当然,对于特定路径,关联性不大并且与取值范围有关的几条路径,用if,if更清晰。
3、逻辑表达式
- 逻辑运算符构成逻辑表达式语句,用于需要测试的多种条件。 C++提供了3种逻辑运算符,来组合或修改已有的表达式。这些运算符分别是逻辑OR(||),逻辑AND(&&)和逻辑NOT(!)。
I、逻辑OR运算符和逻辑AND运算符
- 当两个条件中最多需要一个条件某个要求的时候,用逻辑OR运算符(||),即原来表达式中有一个子表达式为真,则整个表达式布尔值为真;当两个条件中需要全部满足某个要求的时候,用逻辑AND运算符(&&),即原来表达式中只有子表达式全为真,整个表达式布尔值才为真。
- C++规定,||运算符和&&运算符都是顺序点(sequence point)。 也就是说,先修改这两个运算符左侧的值,再对右侧的值进行判定和修改(C++的说法是,运算符左边的子表达式优先于右边的子表达式),如果||左边的子表达式结果为真,则系统就会判断||整个表达式为真,则不进行右侧子表达式的判断,因此进行的表达式只有左侧的,右侧被忽略跳过。
- 用&&和||来设置取值范围
II、逻辑NOT运算符
- !运算符将它后面的表达式的真值取反。
III、逻辑运算符之间的优先级规则
- (1). !运算符的优先级高于所有的关系运算符和算术运算符。
- (2). 逻辑AND运算符的优先级高于逻辑OR运算符。也就是说当&&和||在同一个逻辑表达式语句中时,不再完全从左到右运算,而是先将&&运算符两侧子表达式当作一个表达式整体,优先进行&&运算,得到的表达式结果成为||的另一个子表达式结果,再根据结合规则,从左到右依次判断。
二、cctype字符函数库
-
C++从C语言继承了一个与字符相关的,非常方便的函数软件包,它可以简化诸如确定字符是否为大写字母,数字,标点符号等工作。简单来说,这个cctype字符函数库(C语言中是ctype.h)就是用来判断字符,测试字符具体内容的。可以与另一个字符串处理函数(string.h/cstring)形成对比,字符串处理函数是将字符串进行一系列有效的处理,因为单个字符处理起来非常方便,因此不需要特别内置像处理字符串那样的函数库,只需要进行判断,再简单修改即可。
-
cctype中的字符函数(部分)
-
- isalpha() :如果参数是字母,该函数返回true;
-
- isdigit() :如果参数是数字(0~9),该函数返回true;
-
- iscntrl() :如果参数是控制字符(在ASCII码中,第0~31号及第127号(共33个)是控制字符或通讯专用字符),该函数返回true;
-
- isgraph() :如果参数是除了空格以外的可打印字符(通常是ASCII字符集,但也有西文字符集等等,可以将文本信息输出到显示台上,而文本信息也就是字符序列),该函数返回true;
-
- islower() :如果参数是小写字母,该函数返回true;
-
- ispunct() :如果参数是标点符号,该函数返回true;
-
- isspace():如果参数是空白符(空格,进纸,换行符,回车,水平制表符和垂直制表符),该函数返回true;
-
- isuper() :如果参数是大写字母,该函数返回true;
-
使用这些函数进行判断,比使用关系表达式加上逻辑表达式更加简洁清晰,例如:if((ch>=‘a’&&ch<=‘z’)||(ch>=‘A’&&ch<=‘Z’))用于判断ch是否是英文字母,则可以用if(isalpha(ch))来代替。
-
对于控制字符相关信息的补充
-
-
-
-
三、读取数字的循环
- 上次我们讨论了对文本信息的输入输出,利用单个字符完成文本输入或存储。当我们用数组去存储字符串的时候,若对于输入到数组的内容难以把控,则无法决定要循环的次数,这个时候我们可以利用缓冲区原理加循环来解决这个问题(也就是利用像文本输入那样的方法去给数组赋值),实现按照实际情况进行即输即停。而因为引入了字符串处理函数以及string类的方法,因此让字符串数组部分赋值,即输即停的操作问题得以很轻松的解决。如C语言中的gets,C++中的cin.getline和cin.get都很好地代替了哨兵字符的处理。
- 虽然字符串数组能够很简单处理,但数值数组就不一样了,因为数值不存在空字符来表示结束的情况,因此只剩下哨兵这个方法来完成数值数组的部分赋值,提前结束赋值等操作。因为没有专门用来输入数值数据的成员函数,所以我们用的方法是用cin来实现。
- 既然我们决定了用cin来实现数值的输入(这也就好比在C语言中我们用getchar处理字符变量,用gets处理字符串,而输入数值的时候只会用到scanf一样),那么来看看cin如何处理哨兵的:int n; cin>>n;(如果用户输入一个单词,而不是一个数字,情况会如何呢?发生这种类型不匹配的情况时,将发生4种情况)
-
- n的值保持不变,也就是非数字的内容并不会被存入n的存储单元
-
- 不匹配的输入将被留在输入队列种
-
- cin在该位置设置错误标记
-
- 对cin方法的调用将返回false(cin对象在逻辑表达式中被自动转化为bool类型)
- 方法返回false意味着可以用非数字输入来结束读取数字的循环。非数字输入设置错误标记来防止读取后续数据,而这意味着必须重置该标记,程序才能继续读取输入。而将该标记重置(清除)的方法是用cin.clear()成员函数来清除,同时也重置文件尾(EOF)。输入错误和EOF都将导致cin返回false。而由此可见另外一个消息,对于数值来说不会将除了数字以外的字符存入数值数组,因此哨兵其实就是cin对象本身,利用的是将cin对象返回当作哨兵。
- 接下来我们看一个具体程序作为例子(1):
/*
假设要编写一个程序,来计算平均每天捕获的鱼的重量。这里假设每天最多捕获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缓冲原理了,因为如果想利用
{
if(++i<Max)
cout<< "fish #"<<i+1<<":";
}
}
- 该程序种的表达式cin>>fish[i]实际上是一个cin方法函数调用,该函数返回cin。如果cin位于测试条件中,则将被转换为bool类型。如果输入成功,则转换后的值为true,否则为false。
- 以上程序只实现了提前结束数组的循环输入,但如果我们在中途输入错误了,还想继续读取该怎么做呢?下面程序是例子(2):
/*
程序要求用户提供5个高尔夫得分,以计算平均成绩。如果用户输入非数字输入,程序将拒绝,并要求用户继续输入数字。
*/
#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])) //equivalent to cin>>golf[i]==false 测试当返回false则再次输入,如果依然返回false则重复该动作
{ //如果输入的是数字则为正常输入,不进入内循环再次输入
cin.clear(); //reset input
while(cin.get()!='\n') //get rid of bad input 清空缓冲区
continue;
cout<<"Please enter a number:"; //提示下一次应该输入数字
}
}
return 0;
}
-
可以看到,可以使用cin输入表达式的值来检测输入是不是数字。程序发现用户输入了错误内容时,应采取三个步骤:
-
- 重置cin以接受新的输入 (使用成员函数cin.clear()来清除错误标记)
-
- 删除后续的错误输入 (利用循环通过成员函数cin.get()来吸收缓冲区剩余内容,清空缓冲区,其实最主要作用是吸收换行符,因为cin无法吸收换行符,系统会一直将换行符留在缓冲区中,因此如果不吸收则会阻断后续输入)
-
- 提示用户在输入
-
读取数字的循环总的来说就是利用cin调用是否成功返回当作哨兵,来完成数字数组的部分赋值或者说是临时性赋值。所以读取数字,是比读取字符难的,因为字符有两个思路:第一种是哨兵,但哨兵又分为两种,第一种是利用特殊字符,第二种是利用cin调用是否成功的返回;第二种直接用字符串处理函数或string数据类型。
四、简单文件输入/输出
- 有时候,通过键盘输入并非最好的选择。例如,假设您编写了一个股票分析程序,并下载了一个文件,其中包含1000种股票的价格。在这种情况下,让程序直接读取文件,而不是手工输入文件中所有的值,将方便得多。同样,让程序将输出写入到文件中将更为方便,这样可得到有关结果的永久性记录。
- 幸运的是,C++使得将读取键盘输入和在屏幕上显示输出(统称为控制台输入/输出)的技巧用于文件输入/输出(文件I/O)非常简单。
1、文本I/O的概念
- 这里先介绍一下文本I/O的概念:使用cin进行输入时,程序将输入视为一系列的字符,其中每个字符都被解释为相应的字符编码。不管目标数据类型是什么,输入一开始都是字符数据——文本数据,然后,cin对象负责根据目标数据类型在系统内部进行相应的编码转换,转换成对应数据类型下的该字符所想表达的数据二进制编码,再存储到对象的内存单元中。
-
- 例如我们来看一些处理同一个输入行的代码:
-
- 假设有如下示例输入行:
-
- 38.5 19.2
-
- 首先,来看使用char数据类型的情况:char ch; cin>>ch; 系统判断目标变量数据类型为char,输入行中的第一个字符被赋给ch。在这里,第一个字符是数字3,其字符编码(二进制)被存储在变量ch中。系统判断出输入和目标变量都是字符,因此不需要进行转换。
-
- 接下来,来看看int数据类型的情况:int n; cin>>n; 在这种情况下,系统判断目标变量数据类型为int,cin将不断读取,直到遇到非数字字符。也就是说,它将读取3和8,然后句点将成为输入队列中的下一个字符,形成了分隔作用(但为了与浮点数据区分开来,一般人为规定用空格将数据之间分隔开来)。接着,系统判断出目标变量是int类型,并且通过计算将字符38转换成整数38对应的编码存入变量n中。
-
- 再来看看double数据类型的情况:double x; cin>>x; 在这种情况下,系统判断目标变量数据类型为double,cin同样将不断读取,直到遇到第一个不属于浮点数的字符。也就是说,cin读取3、8、句点和5,更直白点说就是读取窗口内容为句点链接的数字,使得空格成为输入队列中的下一个字符。同样地,系统判断出目标变量类型为double,因此将该窗口内容(38.5字符)转换为浮点数38.5对应编码数据,以浮点数据存储方式赋值存入到变量x中。
-
- 最后,来看看char数组的情况:char word[50]; cin>>word; 在这种情况下,系统判断目标变量数据类型为数组,cin将不断读取,直到遇到空白字符,因为cin压根不吸收任何空白符,当cin遇到字符序列结尾空白,系统将会判断在该处字符序列输入已结束输入,并在末尾自动加上空字符。也就是说,它读取3、8、句点和5,使得空格成为判断输入结束的标记。然后系统将这四个字符的字符编码分别存储到数组word中,并在末尾加上一个空字符,这里不需要进行任何转换。
- 这里的要点是,输入一开始为文本。因此,控制台输入的文件版本是文本文件,即每个字节都存储了一个字符编码的文件。但并非所有的文件都是文本文件,例如:数据库和电子表格以数值格式(即二进制整数和浮点格式)来存储数值数据。
2、写入文本文件
-
对于控制台屏幕的显示,C++使用cout(cout是ostream类预定义的对象)。而关于输出到文件中,C++也有相应的处理方法,那就是与cout类似的ofstream对象。(o可以理解为output,f为file,也就是将信息写入文件中并在文件中显示出来)
-
具体操作如下
-
- 必须包含头文件fstream;
-
- 头文件fstream定义了一个用于处理输出的ofstream类;
-
- 需要声明一个或多个ofstream变量(对象),并以自己喜欢的方式对其进行命名,条件是遵守常用的命名规则。
-
- 必须指明命名空间std;
-
- 需要将ofstream对象与文件关联起来。为此,方法之一是使用open()成员函数;
-
- 使用完文件后,应使用成员函数close()将其关闭;
-
- 可结合使用ofstream对象和运算符<<来输出各种类型的数据。
-
接下来用一段代码块展示:
#include<iostream>
#include<fstream>
int main()
{
using namespace std;
char automobile[50]; //或:string automobile;
cin.getline(automobile,sizeof(automobile)); // 使用友元函数:getline(cin,automobile);
int year;
cin>>year;
double a_price;
cin>>a_price;
double d_price;
d_price=0.913*a_price;
ofstream outFile; //创建一个ofstream对象用于接受消息输入,或者ofstream fout(file output)类似于cout(command output)
outFile.open("carinof.txt"); //用方法open()使该对象与一个具体文本文件联系起来
outFile<<fixed;
outFile.precision(2);
outFile.setf(ios base::showpoint); //调整outFile对象写入浮点类型的精度
outFile << "Make and model:" << automobile << endl; //用法和cout一样的
outFile << "Year:" << year << endl;
outFile << "Was asking $"<< a_price << endl;
outFile << "Now asking $"<< d_price << endl;
outFile.close(); //记得结束的时候要关闭文件
return 0;
}
- 如果您查看该程序的可执行文件所在的目录,将看到一个名为carinfo.txt的新文件(根据编译器的配置,该文件也可能位于其他文件夹),其中包含使用outFile生成的输出。
- 注意:方法close()不需要使用文件名作为参数,这是因为outFile已经同特定的文件关联起来。如果您忘记关闭文件,程序正常终止的时候将会自动关闭它。
- 如果在该程序运行之前,文件carinfo.txt并不存在,在这种情况下,方法open()将新建一个名为carinfo.txt的文件。如果在此运行该程序,文件carinfo.txt将存在,此时情况将如何呢?默认情况下,open()将首先截断该文件,即将其长度截短到零——丢弃原有的内容,然后将新的输出加入到该文件中。
3、读取文本文件
-
与控制台的读取相同,C++使用cin来将控制台的信息输入,同样地,C++使用与cin类似的方法,用ifstream对象来读取文本文件中的信息。
-
具体操作如下
-
- 必须包含头文件fstream;
-
- 头文件fstream定义了一个用于处理输入的ifstream类;
-
- 需要声明一个或多个ifstream变量(对象);
-
- 必须指明命名空间std;
-
- 使用完文件后,应使用close()方法将其关闭;
-
- 可结合使用ifstream对象和运算符>>来读取各种类型的数据;
-
- 可以使用ifstream对象和get()方法来读取一个字符,使用ifstream对象和getline()来读取一行字符;
-
- 可以结合使用ifstream和eof()、fail()等方法来判断输入是否成功;
-
- ifstream对象本身被用作测试条件时,如果最后一个读取操作成功,它将被转换为布尔值true,否则被转换为false。
-
接下来用一段代码块展示:
#include<iostream>
#include<fstream>
#include<cstdlib>
const int SIZE=60;
int main()
{
using namespace std;
char filename[SIZE];
ifstream inFile; //object for handing file input
cout << "Enter name of data file:";
cin.getline(filename,SIZE);
inFile.open(filename); //associate inFile with a file
if(!inFile.is_open()) //failed to open file
{
cout<< "Could not open the file" << filename << endl;
cout<< "Program terminating.\n";
exit(EXIT_FAILURE);
}
double value;
double sum=0.0;
int count=0; //number of items read
inFile >> value; //get first value
while(inFile.good()) //while input good and not at EOF
{
++count;
sum+=value;
inFile >> value; //get next value
}
if(inFile.eof())
cout << "End of file reached.\n";
else if(inFile.fail())
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(); //finished with the file
return 0;
}
- Windows文本文件的每行都以回车字符和换行符结尾
- 方法is_open()检查文件是否被成功打开是C++中相对较新的内容。如果编译器不支持它,可以使用较老的方法good()来代替。并且good()也可以用来检查指出最后一次读取输入的操作是否成功。如果最后一次读取数据时遇到EOF,则eofbit将被设置为1,eof()将返回true,而相应的good()返回false;如果最后一次读取操作中发生了类型不匹配的情况,方法fail()将返回true(如果遇到了EOF,方法fail()也返回true);最后,可能出现意外的问题,如果文件受损或者硬件故障,则方法bad()将返回true,但这三种情况都可以用good()代替,good()是没有发生任何错误的时候返回true。
总结
- C++提供了if语句、if-else语句和switch-case语句来管理选项,还提供了帮助决策的运算符。
- cctype字符函数库提供了一组方便的,功能强大的工具,可用于分析字符输入,判断字符的具体类型。
- 对于文件I/O来说,循环和选择语句是很有用的工具;文件I/O与控制台I/O极其相似。声明ifstream和ofstream对象,并将它们同文件关联起来后,便可以像使用cin和cout那样使用这些对象。
参考书籍:《C++ Prime Plus》