第六章 分支语句和逻辑运算符
为什么程序要使用climits文件中定义的INT_MAX和INT_MIN来确定取值范围?有没有其他的方法可以实现同样的功能?
为什么C++逻辑运算符的优先级是这样设计的?有没有其他的语言使用不同的优先级规则?
为什么C++标准要提供这种另一种表示逻辑运算符的方式?它有什么优势或劣势吗?
如果一个程序中同时使用了符号和保留字来表示逻辑运算符,会不会造成混乱或错误?
为什么要区分文本文件和其他类型的文件?不同类型的文件有什么区别?
如何打开一个已存在的文件,而不截断其原有内容?
如何检查打开文件是否成功?
如何在同一个程序中同时使用多个ofstream对象?
如何在读取文件时跳过不需要的字符或空白?
6.1 if 语句
设计智能程序的一个关键是使程序具有决策能力。第5章介绍了一种决策方式——循环,在循环中,程序决定是否继续循环。现在,来研究一下C++是如何使用分支语句在可选择的操作中做出决定的。程序应使用哪一种防止吸血鬼的方案(大蒜还是十字架)呢?用户选择了哪个菜单选项呢?用户是否输入了0?C++提供了if和switch语句来进行决策,它们是本章的主要主题。另外,还将介绍条件运算符和逻辑运算符,前者提供了另一种决策方式,而后者允许将两个测试组合在一起。最后,本章将首次介绍文件输入/输出。
6.1.1 ifelse 语句
- if语句:if语句是一种选择结构,可以根据一个条件来决定是否执行一条语句或一个语句块。
if (test-condition)
statement
- if语句的格式:if语句的格式为if (test-condition) statement,其中test-condition是一个可以转换为bool值的表达式,statement是一条语句或一个用花括号括起的语句块。如果testcondition为true,则执行statement;如果为false,则跳过statement。
- if语句的示例:程序清单展示了如何使用if语句来统计输入中的空格数和字符总数。程序使用cin.get()函数来读取字符,然后使用if语句来判断字符是否为空格,并相应地增加计数器。
// if.cpp -- using the if statement
#include <iostream>
int main()
{
using std::cin; // using declarations
using std::cout;
char ch;
int spaces = 0;
int total = 0;
cin.get(ch);
while (ch != '.') // quit at end of sentence
{
if (ch == ' ') // check if ch is a space
++spaces;
++total; // done every time
cin.get(ch);
}
cout << spaces << " spaces, " << total;
cout << " characters total in sentence\n";
return 0;
}
下面是该程序的输出:
The balloonist was an airhead
with lofty goals.
6 spaces, 46 characters total in sentence
- if else语句:if else语句是一种选择结构,可以根据一个条件来决定执行两条语句或语句块中的哪一条。
- if else语句的格式:if else语句的格式为,
if (test-condition)
statement1
else
statement2
- 其中test-condition是一个可以转换为bool值的表达式,statement1和statement2是两条语句或两个用花括号括起的语句块。如果test-condition为true,则执行statement1;如果为false,则执行statement2。
- if else语句的示例:程序清单展示了如何使用if else语句来对输入的文本进行加密编码。程序使用cin.get()函数来读取字符,然后使用if else语句来判断字符是否是换行符,并相应地输出字符。
// ifelse.cpp -- using the if else statement
#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; // done if newline
else
std::cout << ++ch; // done otherwise
std::cin.get(ch);
}
// try ch + 1 instead of ++ch for interesting effect
std::cout << "\nPlease excuse the slight confusion.\n";
// std::cin.get();
// std::cin.get();
return 0;
}
下面是该程序的运行情况:
Type, and I shall repeat.
An ineffable joy suffused me as I beheld
Bo!jofggbcmf!kpz!tvggvtfe!nf!bt!J!cfifme
the wonders of modern computing.
uif!xpoefst!pg!npefso!dpnqvujoh
Please excuse the slight confusion.
6.1.2 格式化ielse 语句
- 格式化if else语句:格式化if else语句是一种使代码更清晰和规范的方法,可以用大括号将多条语句组合成一个语句块,并用缩进和换行来区分不同的分支。
if (ch == 'Z')
{ // if true block
zorro++;
cout << "Another Zorro candidate\n";
}
else
{ // if false block
dull++;
cout << "Not a Zorro candidate\n";
}
- 格式化if else语句的方法:格式化if else语句的方法有多种,其中两种流行的方法是:一种是将大括号与if和else对齐,强调语句的块结构;另一种是将大括号与语句缩进,将语句块与关键字if和else更紧密地结合在一起。这两种方法都可以满足要求,但应该保持一致的风格。
- 格式化if else语句的示例:程序清单展示了如何使用大括号和缩进来格式化if else语句,使代码更易于阅读和理解。程序使用cin.get()函数来读取字符,然后使用if else语句来判断字符是否是换行符,并相应地输出字符。
while (ch != '.')
{
if (ch == '\n')
std::cout << ch; // done if newline
else
std::cout << ++ch; // done otherwise
std::cin.get(ch);
}
6.1.3 if else if else结构
- if else if else结构:if else if else结构是一种选择结构,可以根据多个条件来决定执行哪一条语句或哪一个语句块。
- if else if else结构的格式:if else if else结构的格式为,
if (test-condition1)
statement1
else if (test-condition2)
statement2 ...
else statementN
其中test-condition1,test-condition2等是可以转换为bool值的表达式,statement1,statement2等是语句或语句块。如果test-condition1为true,则执行statement1;如果为false,则继续判断下一个条件,直到找到一个为true的条件或者到达最后的else子句。
- if else if else结构的示例:程序清单展示了如何使用if else if else结构来实现一个小型测验程序。程序使用cin>>操作符来读取用户输入的数字,然后使用if else if else结构来判断数字是否等于、大于或小于目标数字,并相应地输出提示信息。
// ifelseif.cpp -- using 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: ";
else
cout << Fave << " is right!\n";
} while (n != Fave);
return 0;
}
下面是该程序的输出:
Enter a number in the range 1-100 to find my favorite number: 50
Too high -- guess again: 25
Too low -- guess again: 37
Too high -- guess again: 31
Too high -- guess again: 28
Too high -- guess again: 27
27 is right!
6.2 逻辑表达式
经常需要测试多种条件。例如,字符要是小写,其值就必须大于或等于’a’,且小于或等于’z’。如果要求用户使用y或n进行响应,则希望用户无论输入大写(Y和N)或小写都可以。为满足这种需要,C++提供了3种逻辑运算符,来组合或修改已有的表达式。这些运算符分别是逻辑OR(||)、逻辑AND(&&)和逻辑NOT(!)。下面介绍这些运算符。
6.2.1 逻辑OR运算符:||
- 逻辑OR运算符:||:逻辑OR运算符是一种二元运算符,可以将两个表达式组合成一个表达式,如果原来的表达式中的任何一个或全部都为true(或非零),则得到的表达式的值为true;否则,表达式的值为false。
- 逻辑OR运算符的优先级和顺序点:逻辑OR运算符的优先级比关系运算符低,但比赋值运算符高,因此可以在不使用括号的情况下与它们一起使用。逻辑OR运算符是一个顺序点,即它会先计算左边的子表达式,然后根据结果决定是否计算右边的子表达式。如果左边的子表达式为true,则右边的子表达式不会被计算,因为整个表达式已经确定为true。
- 逻辑OR运算符的示例:程序清单展示了如何使用逻辑OR运算符来检查用户输入的字符是否是大写或小写。程序使用cin>>操作符来读取用户输入的字符,然后使用if else if else结构和逻辑OR运算符来判断字符是否等于、大于或小于目标字符,并相应地输出提示信息。
// or.cpp -- using the logical OR operator
#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') // y or Y
cout << "You were warned!\a\a\n";
else if (ch == 'n' || ch == 'N') // n or N
cout << "A wise choice ... bye\n";
else
cout << "That wasn't a y or n! Apparently you "
"can't follow\ninstructions, so "
"I'll trash your disk anyway.\a\a\a\n";
return 0;
}
该程序不会带来任何威胁,下面是其运行情况:
This program may reformat your hard disk
and destroy all your data.
Do you wish to continue? <y/n> N
A wise choice ... bye
6.2.2 逻辑AND运算符:&&
- 逻辑与运算符:&&运算符可以将两个表达式组合成一个表达式,只有当两个表达式都为true时,结果才为true。
// and.cpp -- using the logical AND operator
#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) // 2 quitting criteria
{
naaq[i] = temp;
++i;
if (i < ArSize) // room left in the array,
{
cout << "Next value: ";
cin >> temp; // so get next value
}
}
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中。在核实输入有效后,程序才将这个值赋给数组。
下面是该程序的两次运行情况。一次在输入6个值后结束:
Enter the NAAQs (New Age Awareness Quotients) of
your neighbors. Program terminates when you make
6 entries or enter a negative value.
First value: 28
Next value: 72
Next value: 15
Next value: 6
Next value: 130
Next value: 145
Enter your NAAQ: 50
3 of your neighbors have greater awareness of
the New Age than you do.
- 循环终止条件:程序使用了一个while循环来从键盘读取输入值,并将它们存储到数组中。循环有两个终止条件:一是数组被填满,二是输入值为负。
- 输入输出技巧:程序使用了一个临时变量temp来存储输入值,然后检查其有效性后再赋给数组。程序使用了cin>>操作符来读取基本输入,即忽略空格和换行符。程序使用了顺序点来保证副作用的发生。
6.2.3 用 && 来设置取值范围
// more_and.cpp -- using the logical AND operator
#include <iostream>
const char * qualify[4] = // an array of pointers
{ // to strings
"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;
}
下面是该程序的运行情况:
Enter your age in years: 87
You qualify for the pie-throwing festival.
- 字符指针数组:程序使用了一个const char *类型的数组来存储四个字符串的地址,然后通过索引来访问这些字符串。
- 取值范围测试:程序使用了if else语句和&&运算符来根据用户输入的年龄选择一个取值范围,并根据该范围设置索引值。
- 注意事项:程序在使用取值范围测试时,应确保没有缝隙或重叠,并且不要使用数学符号来表示范围,而要使用两个完整的关系表达式。
6.2.4 逻辑NOT 运算符:!
- 逻辑非运算符:!运算符可以将它后面的表达式的真值取反,即如果表达式为true,则!表达式为false,反之亦然。
- 输入验证技巧:程序使用了一个布尔函数is_int()来检查用户输入的值是否在int类型的取值范围内,如果不是,则使用while循环让用户重新输入。
- 字符指针转换:程序使用了int类型强制转换来将double类型的输入值赋给int类型的变量,这样可以避免数据丢失或截断。
// not.cpp -- using the not operator
#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)) // continue while num is not int-able
{
cout << "Out of range -- please try again: ";
cin >> num;
}
int val = int (num); // type cast
cout << "You've entered the integer " << val << "\nBye\n";
return 0;
}
bool is_int(double x)
{
if (x <= INT_MAX && x >= INT_MIN) // use climits values
return true;
else
return false;
}
下面是该程序在int占32位的系统上的运行情况:
Yo, dude! Enter an integer value: 6234128679
Out of range -- please try again: -8000222333
Out of range -- please try again: 99999
You've entered the integer 99999
Bye
为什么程序要使用climits文件中定义的INT_MAX和INT_MIN来确定取值范围?有没有其他的方法可以实现同样的功能?
我们继续往下走。
6.2.5 逻辑运算符细节
- C++逻辑运算符有三种:逻辑OR(||)、逻辑AND(&&)和逻辑NOT(!)。
- C++逻辑运算符的优先级遵循以下规则:!高于关系运算符和算术运算符,&&高于||,||高于赋值运算符。为什么C++逻辑运算符的优先级是这样设计的?有没有其他的语言使用不同的优先级规则?
我们继续往下走。
- 使用括号可以改变逻辑表达式的求值顺序,也可以增加代码的可读性和可靠性。
- C++保证程序从左向右计算逻辑表达式,并在知道结果后立即停止。这可以避免一些不必要或危险的计算。
6.2.6 其他表示方式
- C++标准提供了另一种表示逻辑运算符的方式,使用保留字and、or和not代替符号&&、||和!。为什么C++标准要提供这种另一种表示逻辑运算符的方式?它有什么优势或劣势吗?
我们继续往下走。
- 这些保留字不是关键字,因为它们只是已有语言特性的另一种表示方式,而不是引入新的语法或语义。如果一个程序中同时使用了符号和保留字来表示逻辑运算符,会不会造成混乱或错误?
我们继续往下走。
- 这些保留字在C语言中不是保留字,但C语言程序可以使用它们,只要包含了头文件iso646.h。C++程序不需要包含这个头文件。
运算符 | 另一种表示方式 |
---|---|
&& | and |
|| | or |
! | not |
6.3 字符函数库 cctype
- C++从C语言继承了一个与字符相关的、非常方便的函数软件包,它可以简化诸如确定字符是否为大写字母、数字、标点符号等工作,这些函数的原型是在头文件cctype中定义的。
- 这些函数都接受一个char类型的参数,返回一个int类型的值,表示该字符是否属于某个特定的类别。如果是,则返回非零值,否则返回零。这些值可以被转换为bool类型,用于逻辑表达式中。
- 使用这些函数比使用AND和OR运算符更方便,也更通用,因为它们不依赖于特定的字符编码方式,而是根据当前的区域设置来判断字符的类别。
// cctypes.cpp -- using the ctype.h library
#include <iostream>
#include <cctype> // prototypes for character functions
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); // get first character
while (ch != '@') // test for sentinel
{
if(isalpha(ch)) // is it an alphabetic character?
chars++;
else if(isspace(ch)) // is it a whitespace character?
whitespace++;
else if(isdigit(ch)) // is it a digit?
digits++;
else if(ispunct(ch)) // is it punctuation?
punct++;
else
others++;
cin.get(ch); // get next character
}
cout << chars << " letters, "
<< whitespace << " whitespace, "
<< digits << " digits, "
<< punct << " punctuations, "
<< others << " others.\n";
return 0;
}
下面是该程序的运行情况。注意,空白字符计数中包括换行符:
Enter text for analysis, and type @ to terminate input.
AdrenalVision International producer Adrienne Vismonger
announced production of their new 3-D film, a remake of
"My Dinner with Andre," scheduled for 2013. "Wait until
you see the the new scene with an enraged Collossipede!"@
177 letters, 33 whitespace, 5 digits, 9 punctuations, 0 others.
- 程序清单演示了一些ctype库函数的用法,它使用了isalpha()、isdigit()、isspace()和ispunct()来统计输入文本中不同类别的字符的个数,并在遇到@字符时结束输入。
- 下表总结了cctype软件包中的函数,它们可以用来测试字符是否为字母、数字、控制字符、空白字符、十六进制数字、小写字母、大写字母或标点符号。
函数名称 | 返回值 | 描述 |
---|---|---|
isalnum() | true | 如果参数是字母数字,即字母或数字,该函数返回true |
isalpha() | true | 如果参数是字母,该函数返回true |
iscntrl() | true | 如果参数是控制字符,该函数返回true |
isdigit() | true | 如果参数是数字(0~9),该函数返回true |
isgraph() | true | 如果参数是除空格之外的打印字符,该函数返回true |
islower() | true | 如果参数是小写字母,该函数返回true |
isprint() | true | 如果参数是打印字符(包括空格),该函数返回true |
ispunct() | true | 如果参数是标点符号,该函数返回true |
isspace() | true | 如果参数是标准空白字符,如空格、进纸、换行符等,该函数返回true |
isupper() | true | 如果参数是大写字母,该函数返回true |
isxdigit() | true | 如果参数是十六进制数字,即0~9、a~f或A~F,该函数返回true |
tolower() | 字符 | 如果参数是大写字符,则返回其小写,否则返回该参数 |
toupper() | 字符 | 如果参数是小写字符,则返回其大写,否则返回该参数 |
6.4 ?:运算符
- C++有一个常被用来代替if else语句的运算符,这个运算符被称为条件运算符(?:),它是C++中唯一一个需要3个操作数的运算符。
- 条件运算符的通用格式是:
expression1 ? expression2 : expression3
,它的含义是:如果expression1为true,则整个条件表达式的值为expression2的值;否则,整个表达式的值为expression3的值。 - 使用条件运算符可以简化代码,也可以将一个条件表达式作为一个值赋给变量或放到一个更大的表达式中。
// condit.cpp -- using the conditional operator
#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; // c = a if a > b, else c = b
cout << " is " << c << endl;
return 0;
}
下
面是该程序的运行情况:
Enter two integers: 25 28
The larger of 25 and 28 is 28
- 程序清单演示了条件运算符的用法,它使用了条件运算符来确定两个值中较大的一个,并输出结果。
- 条件运算符有时也可以嵌套在另一个条件表达式中,但这样做可能会降低代码的可读性和可维护性。
6.5 switch 语句
- switch语句是一种用来从多个选项中选择一个执行的语句,它根据一个整数表达式的值来跳转到相应的case标签处。
- switch语句的通用格式是:
switch (integer-expression)
{
case label1 : statement(s)
case label2 : statement(s)
...
default : statement(s)
}
其中每个标签都必须是一个整数常量表达式,default标签是可选的,用于处理没有匹配的情况。
- switch语句与if else语句的区别是,switch语句只能用于整数值的比较,而if else语句可以用于任何类型的比较;switch语句在跳转到某个case标签后,会继续执行之后的所有语句,除非遇到break语句,而if else语句在执行完一个分支后,会自动跳出条件结构。
- 程序清单演示了switch和break的用法,它使用了一个简单的菜单来让用户选择不同的操作,并在输入5时退出循环。
- switch语句有时也可以使用多个标签来表示同一组语句,例如case ‘a’: case ‘A’: cout << “\a\n”; break;,这样可以简化代码,也可以增加灵活性
6.5.1 将枚举量用作标签
// enum.cpp -- using enum
#include <iostream>
// create named constants for 0 - 6
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 was blue.\n"; break;
case violet : cout << "Her eyes were violet.\n"; break;
case indigo : cout << "Her mood was indigo.\n"; break;
}
cout << "Enter color code (0-6): ";
cin >> code;
}
cout << "Bye\n";
return 0;
}
下面是该程序的输出:
Enter color code (0-6): 3
Her nails were green.
Enter color code (0-6): 5
Her eyes were violet.
Enter color code (0-6): 2
Her shoes were yellow.
Enter color code (0-6): 8
Bye
- 程序清单使用enum定义了一组相关的常量,分别表示不同的颜色,从0到6。
- 程序要求用户输入一个整数作为颜色代码,然后使用switch语句根据用户的输入输出相应的描述。
- 程序使用while循环来重复这个过程,直到用户输入一个不在0到6范围内的数值,然后退出循环。
- 程序演示了如何使用枚举类型来提高代码的可读性和可维护性,也演示了如何将枚举量与int类型进行比较和提升。
6.5.2 switch 和 ifelse
- switch语句和if else语句都是用来从多个选项中选择一个执行的语句,它们各有优劣。
- switch语句的优点是,它可以简化代码,提高执行速度,适合于处理整数常量的比较。
- switch语句的缺点是,它不能处理取值范围、浮点测试或变量的比较,它需要使用break语句来控制程序流程。
- if else语句的优点是,它更通用,可以处理任何类型的比较,也可以嵌套使用来实现多分支的逻辑。
- if else语句的缺点是,它可能导致代码冗长,执行效率低下,适合于处理简单或复杂的条件。
6.6 break 和 continue语句
- break和continue语句都是用来跳过部分代码的语句,它们可以用于switch语句或任何循环中。
- break语句使程序跳出当前的switch或循环,继续执行后面的语句。
- continue语句使程序跳过循环体中余下的代码,开始新一轮的循环。
// jump.cpp -- using continue and break
#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]; // display character
if (line[i] == '.') // quit if it's a period
break;
if (line[i] != ' ') // skip rest of loop
continue;
spaces++;
}
cout << "\n" << spaces << " spaces\n";
cout << "Done.\n";
return 0;
}
下面是该程序的运行情况:
Enter a line of text:
Let's do lunch today. You can pay!
Complete line:
Let's do lunch today. You can pay!
Line through first period:
Let's do lunch today.
3 spaces
Done.
- 程序清单演示了break和continue语句的用法,它使用了一个简单的文本分析程序,根据用户的输入输出不同的结果,并在遇到句点时结束循环。
- C++也有goto语句,但它不推荐使用,因为它会破坏程序的结构和可读性,应该使用结构化控制语句来代替。
6.7 读取数字的循环
- 非数字输入会导致cin返回false,并设置错误标记,需要使用clear()方法重置cin,以便继续读取输入。
- 为了删除错误输入,可以使用cin.get()方法读取并丢弃输入队列中的字符,直到遇到换行符或空白字符。
6.8 简单文件输入/ 输出
有时候,通过键盘输入并非最好的选择。例如,假设您编写了一个股票分析程序,并下载了一个文件,其中包含1000种股票的价格。在这种情况下,让程序直接读取文件,而不是手工输入文件中所有的值,将方便得多。同样,让程序将输出写入到文件将更为方便,这样可得到有关结果的永久性记录。
幸运的是,C++使得将读取键盘输入和在屏幕上显示输出(统称为控制台输入/输出)的技巧用于文件输入/输出(文件I/O)非常简单。第17章将更详细地讨论这些主题,这里只介绍简单的文本文件I/O。
6.8.1 文本I/O和文本文件
- 文本I/O和文本文件:文本I/O的概念,即输入和输出都是以字符数据(文本数据)的形式进行的,然后由cin和cout对象负责将文本数据转换为其他类型或从其他类型转换为文本数据。为什么要区分文本文件和其他类型的文件?不同类型的文件有什么区别?
我们继续往下走。
- cin的处理方式:cin是如何根据目标变量的类型来读取输入的,例如,如果目标变量是char类型,cin只读取一个字符;如果是int类型,cin读取直到遇到非数字字符;如果是double类型,cin读取直到遇到不属于浮点数的字符;如果是char数组,cin读取直到遇到空白字符或换行符。
- 文本文件的创建和查看:如何使用文本编辑器或字处理程序来创建和查看文本文件,以及如何将字处理文件保存为文本格式。也说明了源代码文件就属于文本文件。
6.8.2 写入到文本文件中
- 文件输出的基本概念:文件输出与控制台输出类似,都使用ostream类和<<运算符,但需要包含头文件fstream,创建一个ofstream对象,并将其与一个文件关联起来。
- 文件输出的操作方法:使用open()方法打开一个文件,如果文件不存在则新建一个,如果文件已存在则截断其原有内容。使用close()方法关闭一个文件,释放资源。使用cout可用的任何格式化方法来控制文件输出的样式。
- 程序清单的示例:该程序要求用户输入汽车的信息,然后将信息显示到屏幕上,并写入到carinfo.txt文件中。该程序演示了如何使用ofstream对象来输出各种类型的数据。
// outfile.cpp -- writing to a file
#include <iostream>
#include <fstream> // for file I/O
int main()
{
using namespace std;
char automobile[50];
int year;
double a_price;
double d_price;
ofstream outFile; // create object for output
outFile.open("carinfo.txt"); // associate with a file
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;
// display information on screen with cout
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;
// now do exact same things using outFile instead of cout
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(); // done with file
return 0;
}
该程序的最后一部分与cout部分相同,只是将cout替换为outFile而已。下面是该程序的运行情况:
Enter the make and model of automobile: Flitz Perky
Enter the model year: 2009
Enter the original asking price: 13500
Make and model: Flitz Perky
Year: 2009
Was asking $13500.00
Now asking $12325.50
我们继续往下走。
如何检查打开文件是否成功?
我们继续往下走。
如何在同一个程序中同时使用多个ofstream对象?
我们继续往下走。
6.8.3 读取文本文件
- 文本输入的基本概念:文本输入与控制台输入类似,都使用istream类和>>运算符,但需要包含头文件fstream,创建一个ifstream对象,并将其与一个文件关联起来。
- 文本输入的操作方法:使用open()方法打开一个文件,如果文件不存在则返回错误。使用close()方法关闭一个文件,释放资源。使用cin可用的任何读取方法来获取文件中的数据。
- 程序清单的示例:该程序从用户指定的文件中读取数字,并计算它们的总和和平均值。该程序演示了如何使用ifstream对象来读取各种类型的数据,以及如何检查文件是否被成功打开和读取。
// sumafile.cpp -- functions with an array argument
#include <iostream>
#include <fstream> // file I/O support
#include <cstdlib> // support for exit()
const int SIZE = 60;
int main()
{
using namespace std;
char filename[SIZE];
ifstream inFile; // object for handling 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; // one more item read
sum += value; // calculate running total
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;
}
要运行程序清单中的程序,首先必须创建一个包含数字的文本文件。为此,可以使用文本编辑器(如用于编写源代码的文本编辑器)。假设该文件名为scores.txt,包含的内容如下:
18 19 18.5 13.5 14
16 19.5 20 18 12 18.5
17.5
我们继续往下走。
小思考🤔
为什么程序要使用climits文件中定义的INT_MAX和INT_MIN来确定取值范围?有没有其他的方法可以实现同样的功能?
答:
程序使用climits文件中定义的INT_MAX和INT_MIN来确定取值范围是一种方便而可移植的方法,因为这些常量可以根据不同的系统和编译器自动调整。其他的方法也可以实现同样的功能,例如使用sizeof运算符来计算int类型占用的字节数,然后根据二进制补码表示法来计算最大和最小值。
为什么C++逻辑运算符的优先级是这样设计的?有没有其他的语言使用不同的优先级规则?
答:
C++逻辑运算符的优先级是基于数学上的布尔代数和逻辑推理的规则设计的。例如,!运算符相当于数学上的否定符号(¬),&&运算符相当于数学上的合取符号(∧),||运算符相当于数学上的析取符号(∨)。在数学上,¬、∧和∨的优先级也是按照C++中相同的顺序排列的。其他的语言也可能使用不同的优先级规则,例如Python中,not、and和or的优先级分别对应于C++中的!、||和&&。
为什么C++标准要提供这种另一种表示逻辑运算符的方式?它有什么优势或劣势吗?
答:
C++标准提供这种另一种表示逻辑运算符的方式是为了增加代码的可读性和可移植性。一方面,使用保留字可以让代码更接近自然语言,更容易理解和记忆。另一方面,使用保留字可以避免一些键盘上没有逻辑运算符符号的问题,或者一些字符集编码上的问题。这种方式的劣势可能是与C语言的兼容性较差,或者与其他编程语言的习惯不同。
如果一个程序中同时使用了符号和保留字来表示逻辑运算符,会不会造成混乱或错误?
答:
如果一个程序中同时使用了符号和保留字来表示逻辑运算符,不会造成混乱或错误,因为它们在编译器看来是完全等价的。但是,这样做可能会降低代码的一致性和风格,所以最好在一个程序中只选择一种方式来表示逻辑运算符。
为什么要区分文本文件和其他类型的文件?不同类型的文件有什么区别?
答:
因为不同类型的文件有不同的存储格式和读写方式。文本文件只存储字符编码,每个字节都对应一个字符。其他类型的文件可能存储数值、格式、图像等非文本数据,每个字节都有特定的含义。
如何打开一个已存在的文件,而不截断其原有内容?
答:
可以在open()方法中使用第二个参数来指定打开模式,例如outFile.open(“carinfo.txt”,
ios_base::app)表示以追加模式打开文件,即在原有内容的末尾添加新的内容。
如何检查打开文件是否成功?
答:
可以使用is_open()方法来判断一个ofstream对象是否与一个文件成功关联,例如if
(outFile.is_open())表示如果outFile已经打开了一个文件,则执行后续操作。
如何在同一个程序中同时使用多个ofstream对象?
答:
可以声明多个ofstream对象,并分别将它们与不同的文件关联起来,例如ofstream outFile1, outFile2;
outFile1.open(“carinfo.txt”);
outFile2.open(“carinfo2.txt”);表示创建了两个ofstream对象,并分别打开了两个不同的文件。
如何在读取文件时跳过不需要的字符或空白?
答:
可以使用get()或getline()方法来读取一个字符或一行字符,然后根据需要忽略或处理它们。也可以使用ignore()方法来跳过指定数量的字符或直到遇到指定的分隔符。例如inFile.ignore(10, ‘\n’);表示跳过10个字符或直到遇到换行符。