目录儿
- 六、分支语句和逻辑操作符
- 6.1 if语句
- 6.1.1 if
- 6.1.2 if-else
- 6.1.3 if-else if-else
- 6.2 逻辑表达式
- 6.2.1 逻辑或`||`
- 6.2.2 逻辑与`&&`
- 6.2.3 逻辑非`!`
- 6.2.4 逻辑操作符的其他表示方式
- 6.3 字符函数库cctype
- 6.4 三目/元操作符
- 6.5 switch语句
- 6.5.1 switch引入枚举常量
- 6.6 break和continue关键字
- 6.7 读取数字的循环
- 6.8 简单文件输入、输出
- 6.8.1 文本I/O和文本文件
- 6.8.2 写出文本文件
- 6.8.3 读取文本文档
六、分支语句和逻辑操作符
C++
提供了两种分支语句,if
和switch
。
6.1 if语句
6.1.1 if
句式:
if(测试条件)
{
语句; // 符合测试条件执行语句
}
例子:输入语句统计字符和空格数量
#include<iostream>
int main() {
using namespace std;
cout << "Enter sentence:\n";
char ch;
int total = 0;
int space = 0;
cin.get(ch);
while (ch != '.')
{
if(ch == ' ') // 若字符为空格
space++; // 累加空格数
total++; // 累加字符数
cin.get(ch);
};
cout << space << " space, " << total << " characters total in sentence." << endl;
system("pause");
return 0;
}
结果
6.1.2 if-else
句式:
if(测试条件){
语句1; // 符合测试条件执行语句1
}else{
语句2; // 不符合测试条件执行语句2
}
6.1.3 if-else if-else
句式:
if(测试条件1){
语句1; // 符合测试条件1执行语句1
}else if(测试条件2){
语句2; // 符合测试条件2执行语句2
}else if(测试条件3){
语句3; // 符合测试条件3执行语句3
}else{
语句4; // 都不符合执行语句4,兜底语句
}
6.2 逻辑表达式
- 与
&&
- 或
||
- 非
!
6.2.1 逻辑或||
- 左右任一表达式为
true
时结果为true
。表达式一 || 表达式二
||
是顺序操作符,从左往右执行,如果左侧表达式为true
,则不会再去判定右侧表达式。
例:#include<iostream> int main() { using namespace std; int i = 2; bool bool_1 = i++ == 2 || i++ == 3; // 左边表达式为 true ,右边表达式不进行判定 cout << "i = " << i << endl; // 输出 i = 3 ,只自增了一次 bool bool_2 = i++ == 3 || i++ == 4; // 左边表达式为 false,判定右边表达式 cout << "i = " << i << endl; // 输出 i = 4 ,自增了两次 system("pause"); return 0; }
6.2.2 逻辑与&&
- 两边表达式都为
true
时结果才为true
。 ||
也是顺序操作符,从左往右执行,如果左侧表达式为false
,则不会再去判定右侧表达式。- 常用作范围取值
if(age > 18 && age < 65){ ... }
注意别这样式儿:
18 < age < 45
编译器不会捕获这种错误,因为这仍然是有效的C++
句式。但是因为<
操作符从左向右工作,因此真正的表达式实则为:(18 < age) < 45
,18 < age
要么为1
要么为0
,都小于45
,因此整个测试结果永远为true
6.2.3 逻辑非!
- 表示对它后面的表达式的值取反。
- 例:
#include<iostream> #include<climits> bool is_int(double); // 自定义函数 int main() { using namespace std; double temp; // double contains int,to prevent consumer input an out-of-range num. cout << "Enter an integer value:"; cin >> temp; while (!is_int(temp)) // 判断是否 int 型 { cout << "out of range!plaese try again:"; cin >> temp; } int num = int(temp); // after judgementation ,conver safely cout << "Congratulation!You enter an integer value " << num << endl; system("pause"); return 0; } // 自定义函数实现 bool is_int(double x) { if (x <= INT_MAX && x >= INT_MIN) // 判定 x 是否在 int 型数据范围内 return true; return false; }
!
的优先级高于所有算术操作符和关系操作符。
6.2.4 逻辑操作符的其他表示方式
并不是所有的键盘都提供了用作逻辑操作符的符号,因此C++标准提供了另一种表示方式,如下表所示。
标识符and
、or
和not
都是C++
保留字,这意味着不能将它们用作变量名等。它们不是关键字,因为它们都是已有语言特性的另一种表示方式。另外,它们并不是C语言中的保留字,但C语言程序可以将它们用作操作符,只要在程序中包含了头文件so646.h
。C++不要求使用头文件。
6.3 字符函数库cctype
C++
从C语言
继承了个与字符相关的、非常方便的函数软件包,它可以简化诸如确定字符是否为大写字母、数字、标点符号等工作,这些函数的原型是在头文件cctype
(老式的风格中为ctype.h
)中定义的。
例如,如果ch
是一个字母,则isalpha(ch)
函数返回一个非零值,否则返回0。同样,如果ch
是标点符号(如逗号或句号),函数ispunct(ch)
将返回true
(这些函数的返回类型为int
,而不是bool
,但通常bool
转换让您能够将它们视为bool
类型)。
使用这些函数比使用AND
和OR
操作符更方便。例如,下面是使用AND
和OR
来测试字符ch
是不是字母字符的代码:
if((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))
使用isalpha(ch)
函数:
if(isalpha(ch))
相比使用函数简洁方便很多。
这个库的主要函数:
例子:
#include<iostream>
#include<cctype>
int main() {
using namespace std;
cout << "Enter a sentence for analysis,any type '@' to terminate input:\n";
char ch;
int spaces = 0;
int alphas = 0;
int digits = 0;
int puncts = 0;
int others = 0;
cin.get(ch);
while (ch != '@')
{
if (isalpha(ch))
alphas++;
else if (isdigit(ch))
digits++;
else if (ispunct(ch))
puncts++;
else if (isspace(ch))
spaces++;
else
others++;
cin.get(ch);
};
cout << alphas << " letters, "
<< digits << " digits, "
<< puncts << " puncts, "
<< spaces << " spaces, "
<< others << " others, "
<< endl;
system("pause");
return 0;
}
6.4 三目/元操作符
C++
有个常被用来代替if else
语句的组合操作符 ?:
,这个组合操作符被称为条件操作符/三元操作符,它是C++
中唯一一个需要3个表达式的操作符。该操作符的通用格式如下:
表达式一?表达式二:表达式三
如果表达式一为true
,则整个条件表达式的值为表达式二的值;否则,整个表达式的值为表达式三的值。
等效于:
if(表达式一)
表达式二
else
表达式三
例:输入俩数,判断大小
#include<iostream>
int main() {
using namespace std;
int a, b, c;
cout << "Enter two integers:";
cin >> a >> b; // 连续读取赋值
cout << "the larger between " << a << " and " << b;
a > b ? c = a : c = b; // a>b c=a; a<b c=b;
cout << " is " << c << endl;
system("pause");
return 0;
}
从可读性来说,三元操作符最适合用于简单关系和简单表达式。当关系和表达式比较复杂时(如多重嵌套分支),使用if else
语句来表达可能更清晰。
6.5 switch语句
在多种选择分支结构中可以用if else
结构来处理,除此之外,C++
还提供了一种适用于大型列表选择的switch
语句,格式如下:
switch(整型表达式){ // 测试条件必须是一个结果为整数值的表达式,如 switch(1){...}
case 条件一:语句一; // 匹配值,需是整数常量,如 case 5:
case 条件一:语句二; // 匹配值,需是整数常量,如 case 4:
case 条件一:语句三; // 匹配值,需是整数常量,如 case 3:
// ... n 个 case
default: 语句四; // 默认匹配,可选,当以上所有 case 的值都和表达式不匹配时,就跳到默认匹配。
}
- 用测试表达式的值由上往下依次匹配每个
case
的整数常量,相等则执行那个case
对应的语句。 default
是默认兜底匹配,是一个可选项,如果省略它,又没有匹配的case
,就直接跳到switch
后面的语句处执行。
在上面的结构中,如匹配到一个case 2
,执行完这个case 2
的语句二
后它还会继续往下去匹配后面的case
,但是通常我们面对的需求是让每个case
都能有效隔离,每一次switch
都只执行一个case
,不想它继续往下穿透,这个时候就要配合break
关键字使用:
switch(整型表达式){ // 测试条件必须是一个结果为整数值的表达式,如 switch(1){...}
case 条件一:语句一;break; // 匹配值,需是整数常量,如 case 5:
case 条件二:语句二;break; // 匹配值,需是整数常量,如 case 4:
case 条件三:语句三;break; // 匹配值,需是整数常量,如 case 3:
// n 个 case
default: 语句四; // 默认匹配,可选,当以上所有 case 的值都和表达式不匹配时,就跳到默认匹配。
- 碰到
break
关键字后switch
就会停止继续往下穿透匹配。 default
匹配项不用加break
关键字,因为已经是switch
结构的末端。
例:
#include<iostream>
void showmenu();
void report();
using namespace std;
int main() {
int choice;
showmenu();
cin >> choice;
while (choice != 4)
{
switch (choice)
{
case 1:cout << "\a\n"; // 响铃
case 2:cout << "警告警告警告警告警告警告警告警告警告警告警告警告\n"; break;
case 3:report(); break;
default:cout << "非法选项,请重新输入!\n";
}
showmenu();
cin >> choice;
}
cout << "Bye!" << endl;
system("pause");
return 0;
}
void showmenu() {
cout << "Please enter 1,2 or 3:\n"
"1) 告警 2)告警提示 3) 打印报告 4离开) \n";
};
void report() {
cout << "报告内容本容.\n";
};
可以看到因为我的case 1
没有加break
,所以当我选择1
的时候它执行了case 1
和case 2
的语句。这种穿透在某些场景很好用,比如输出当前月份前面的所有月份等等。
switch
语句的每个case
都必须是整数(包括char
),所以和if else
语句相比,它不能用于范围取值、浮点测试等场景。- 如果所有选项都是用整数常量来标识的、选项超过两个的,就代码长度和执行速度而言,
switch
语句的效率更高。
6.5.1 switch引入枚举常量
上面的例子我们每个case
值用的都是整数常量,除此之外,有一些需要做映射的场景比如一种颜色对应一个数字,这时候可以使用枚举enum
定义一组颜色常量,并把其作为case
的匹配值,实际上的值是每个枚举常量对应的下标:
#include<iostream>
enum MyEnum // 定义枚举
{
red,
orange,
yellow,
green,
blue,
indigo,
violet
};
int main() {
using namespace std;
cout << "Enter color code(0-6): ";
int code;
cin >> code;
while (code >= red && code <= violet)
{
switch (code)
{
case red:cout << "红色\n"; break; // 下标值 0
case orange:cout << "橙色\n"; break; // 下标值 1
case yellow:cout << "黄色\n"; break; // 下标值 2
case green :cout << "绿色\n"; break; // 下标值 3
case blue:cout << "蓝色\n"; break; // 下标值 4
case indigo:cout << "靛色\n"; break; // 下标值 5
case violet:cout << "紫色\n"; break; // 下标值 6
}
cout << "Enter color code(0-6): ";
cin >> code;
};
cout << "Bye!" << endl;
system("pause");
return 0;
}
6.6 break和continue关键字
break
和continue
关键字都使程序能够跳过部分代码。可以在switch
语句或任何循环中使用break
关键字,使程序跳到switch
或循环后面的语句处执行。continue
关键字主要用于循环结构中中,让程序跳过循环体中余下的代码,并开始新一轮循环。
比较简单不说。。。
6.7 读取数字的循环
假设要编写个将一系列数字读入到数组中的程序,并允许用户在数组填满之前结束输入。一种方法
是利用cin
:
int n;
cin >> n;
但是如果用户输入了一个字母而不是数字,将会发生以下四种情况:
n
的值保持不变。- 不匹配的输入将被留在输入队列中。
cin
对象中的一个错误标记被设置。- 对
cin
方法的调用将返回false
(如果被转换为bool类型)。
cin
方法返回false意味着可以用非数字输入来结束读取数字的循环。- 非数字输入设置错误标记意味着必须重置该标记,程序才能继续读取输入。
clear()
方法重置错误输入标记,同时也重置文件尾(EOF条件)。输入错误和EOF
都将导致cin
返回false
。
案例一:
假设要编写一个程序,来计算平均每天捕获的鱼的重量。这里假设每天最多捕获5条鱼,因此一个包含5个元素的数组将足以存储所有的数据,但也可能没有捕获这么多鱼,所以如果数组被填满或者输入了非数字输入,结束循环。
#include<iostream>
const int MAX = 5;
int main() {
using namespace std;
double fish[MAX];
cout << "Enter the weights of your fish.(enter q to terminate)\n";
cout << "fish #1:";
int i = 0;
while (i < MAX && cin >> fish[i])
{
if (++i < MAX) { // i自增,判断数组是否还有空位
cout << "fish #" << (i + 1) << ":"; // 这里不能再用自增,否则i就自增了两次
}
};
cout << "cin.fail() = " << cin.fail() << ",结束循环" << endl; // 取出错误标记的值看看
// 统计总量
cout << "统计总量:\n";
double total = 0;
for (int j = 0; j < i; j++)
{
total += fish[j];
cout << "fish #" << (j + 1) << " = " << fish[j] << "\n";
}
cout << "total = " << total << endl;
system("pause");
return 0;
}
输入字母q
后,cin >> fish[i]
发生了如下事情:
cin
并没有把字母q
赋值给fish[i]
。- 字母
q
仍被留在输入队列中。 cin
对象中一个错误标记(通过cin.fail()可以拿到
)被设置为1
。- 对
cin
方法的调用将返回false
(如果被转换为bool类型),不满足循环条件,跳出循环。
案例二:
假设程序要求用户提供5个高尔夫得分,以计算平均成绩。如果用户输入非数字输入,程序将拒绝,并要求用户继续输入数字。程序发现用户输入了错误内容时,应采取3个步骤:
- 重置
cin
错误标记以接受新的输入。 - 删除错误输入。
- 提示用户再输入。
请注意,程序必须先重置cin
,然后才能删除错误输入。
#include<iostream>
const int MAX = 5;
int main() {
using namespace std;
double scores[MAX];
cout << "Enter your golf scores(must enter " << MAX << " rounds).\n";
int i = 0;
for ( i = 0; i < MAX; i++)
{
cout << "round #" << (i + 1) << ":"; // 这里不能再用自增,否则i就自增了两次
while (!(cin >> scores[i])) // 如果输入不正确
{
cin.clear(); // 清除错误标志
while (cin.get()!= '\n') // 把非法字符从缓冲区中删除
continue;
cout << "Please enter a number: ";
};
}
cout << "cin.fail() = " << cin.fail() << ",结束循环" << endl;
// 统计总量
cout << "统计总量:\n";
double total = 0;
for (int j = 0; j < i; j++)
{
total += scores[j];
cout << "round #" << (j + 1) << " = " << scores[j] << "\n";
}
cout << "total = " << total << endl;
system("pause");
return 0;
}
核心代码是这块:
while (!(cin >> scores[i])) // 如果输入不正确
{
cin.clear(); // 清除错误标志
while (cin.get()!= '\n') // 把非法字符从缓冲区中删除
continue;
cout << "Please enter a number: ";
};
如果用户输入88
,合法输入,cin >> scores[i]
表达式为true
,则!(cin >> scores[i])
表达式为false
,将值放到数组中,不进入内部循环;
如果用户输入fdfdff
,表达式(cin >> scores[i])
为false
则表达式!(cin >> scores[i])
为true
,此时不会将值放入数组中,进入循环。循环的第一条语句使用clear()
方法重置输入,如果省略这条语句,程序将拒绝继续读取输入。接下来,程序在循环中使用cin.get()
来读取行尾之前的所有字符,从而删除这一行中的错误输入。另一种方法是读取到下一个空白字符,这样将每次删除一个单词,而不是一次删除整行。最后程序告诉用户,应输入一个数字。
6.8 简单文件输入、输出
很多时候直接键盘输入并不是很好的选择,当数据量很大的时候,用得很频繁的时候,让程序直接读取文件获取内容的方式巴适。同时很多时候我们会有保存程序执行结果的需求,把内容直接写出到一个文件夹就非常巴适,而不是打印到控制台。
6.8.1 文本I/O和文本文件
这里再来介绍些文本I/O
的概念。使用cin
进行输入时,程序将输入视为一系列的字节
,其中每个字节都被解释为字符编码
。不管目标数据类型是什么,输入一开始都是字符数据。然后,cin
对象负责将文本转换为其他类型。
假设有如下输入行:
38.5 15.2
如果是赋值给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
通过计算发现,这4个字符对应于数值38.5
,因此将38.5
的二进制编码(浮点格式)复制到变量x
中。
如果是赋值给char
类型数组:
char wordk[50];
cin >> word;
在这种情况下,cin
将不断读取,直到遇到空白字符。也就是说,它读取3
、8
、.
和5
,遇到空格停止,并把其作为输入队列中的下一个字符。然后,cin
将这4个字符的字符编码存储到数组word
中,并在末尾加上一个空字符
。这里不需要进行任何转换。
还有用getline()
函数的情况:
char wordk[50];
cin.getline(word,50);
在这种情况下,cin
将不断读取,直到遇到换行符
。所有字符都将被存储到数组word
中,并在末尾加
上个空字符
。换行符被丢弃,输入队列中的下一个字符是下一行中的第一个字符。这里不需要进行任何
转换。
6.8.2 写出文本文件
文件输出于文本输出(使用cout
对象)类似:
- 必须包含头文件
fstream
。 - 头文件
fstream
定义了一个用于处理输出的ofstream
类。 - 需要声明一个或多个
ofstream
变量(对象),。 - 必须指明名称空间
std
,例如,为引用元素ofstream
,必须使用编译指令using
或前缀std::
。 - 需要将
ofstream
对象与文件关联起来。为此,方法之一是使用open()
方法。 - 使用完文件后,应使用
close()
方法将其关闭。 - 可结合使用
ofstream
对象和操作符>>
来输出各种类型的数据。
注意,虽然头文件
iostream
提供了一个预先定义好的名为cout
的ostream
对象,但您必须声明自己的ofstream
对象,为其命名,并将其同文件关联起来:ofstream fout; // 声明自己的`ofstream`对象 fout.open("test.txt"); // 通过路径关联对应的文本文件"text.txt" double d = 2131.2; fout << d; // 输出到文件
重要的是,声明一个
ofstream
对象并将其同文件关联起来后,便可以像使用cout
那样使用它。所有可
用于cout
的操作和方法(如<<
、endl
和setf()
)都可用于ofstream
对象。
例子:输入汽车信息,分别输出到控制台和文本文件
#include<iostream>
#include<fstream>
int main() {
using namespace std;
char automobile[50];
int year;
double price_1;
double price_2;
// 声明文件输出流对象
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 >> price_1;
price_2 = 0.92 * price_1;
// 输出到控制台
cout << fixed; // 以浮点数形式输出
cout.precision(2); // 保留小数点后两位
cout.setf(ios_base::showpoint); // 保留精度,整数也强制显示小数(小数点后的0)
cout << "Make and model:" << automobile << endl;
cout << "Year:" << year << endl;
cout << "Was asking $" << price_1 << endl;
cout << "Now asking $" << price_2 << endl;
// 输出到文本文件
outFile << fixed;
outFile.precision(2);
outFile.setf(ios_base::showpoint);
outFile << "Make and model:" << automobile << endl;
outFile << "Year:" << year << endl;
outFile << "Was asking $" << price_1 << endl;
outFile << "Now asking $" << price_2 << endl;
// 关闭输出流
outFile.close();
return 0;
}
注意
outFile.open("carinfo.txt");
当文本文档carinfo.txt
不存在的时候,会新建一个名为carinfo.txt
的文本文件。
当文本文档carinfo.txt
已经存在的时候,会先截断该文件,即将其长度截短到0,丢弃原有内容,然后将新的输出加入到该文件中。
6.8.3 读取文本文档
- 必须包含头文件
fstream
。 - 头文件
fstream
定义了个用于处理输入的ifstream
类。 - 需要声明一个或多个
ifstream
变量(对象),并以自己喜欢的方式对其进行命名,条件是遵守常用
的命名规则。 - 必须指明名称空间
std
,例如,为引用元素ifstream
,必须使用编译指令using
或前缀std:
。 - 需要将
ifstream
对象与文件关联起来。为此,方法之一是使用open()
方法。 - 使用完文件后,应使用
close()
方法将其关闭。 - 可结合使用
ifstream
对象和操作符<<
来读取各种类型的数据。 - 可以使用
ifstream
对象和get()
方法来读取一个字符,使用ifstream
对象和getline()
来读取一行
字符。 - 可以结合使用
ifstream
和eof()
、fail()
等方法来判断输入是否成功。 ifstream
对象本身被用作测试条件时,如果最后一个读取操作成功,它将被转换为布尔值true
,否
则被转换为false
.
基础用法:
ifstream inFile; // 声明输入流对象
inFile("test.txt"); // 关联文档
if(inFile.is_open()){ // 确实是否打开文件成功
exit(EXIT_FAILURE);
}
is_open()
方法在文件被成功打开的时候返回true
,否则返回false
。如果编译器不支持,可以用老的方法good()
来替代,但是后者没有前者严格。
exit()
函数和用于操作系统通信的参数EXIT_FAILURE
都定义在cstdlib
头文件中
例子:
#include<iostream>
#include<fstream>
int main() {
using namespace std;
ifstream inFile;
inFile.open("carinfo.txt"); // 读取上面程序保存的文本文件
char temp[60];
inFile.getline(temp, 60);
while (inFile.good()) // 没发生错误,非eof
{
cout << temp << endl;
inFile.getline(temp, 60);
}
if (inFile.eof()) // 如果读到了文件结束符
{
cout << "Read Over!" << endl;
}
else
{
cout << "Unknow Error!" << endl;
}
inFile.close();
return 0;
}
参考资料:《C++ Primer Plus》