第6章-cpp分支语句和逻辑运算符/文本文件IO(ofstream/ifstream)

C++控制流与文件I/O详解
本文深入探讨C++中的控制流语句,包括条件语句、循环语句及逻辑运算符的使用,同时详述了字符函数库cctype的函数与应用。此外,文章还介绍了C++的基本文件输入输出操作,涵盖了文件打开、关闭、读取和写入的详细过程。

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

字符函数库cctype

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

函数名返回值
isalnum()

如果参数是字母数字,即字母或数字,该函数返回true

isalpha()

如果参数是字母,该函数返回true

iscntrl()

如果参数是控制字符,该函数返回true

isdigit()

如果参数是数字(0~9),该函数返回true

isgraph()

如果参数是除空格之外的打印字符,该函数返回true

islower()

如果参数是小写字母,该函数返回true

isprint()

如果参数是打印字符(包括空格),该函数返回true

ispunct()

如果参数是标点符号,该函数返回true

isspace()

如果参数是标准空白字符,如空格、进纸、换行符、回车、水平制表符或者垂直制表符,该函数返回true

isupper()

如果参数是大写字母,该函数返回true

isxdigit()

如果参数是十六进制数字,即0~9、a~f或A~F,该函数返回true

tolower()

如果参数是大写字符,则返回其小写,否则返回该参数

toupper()

如果参数是小写字符,则返回其大写,否则返回该参数

将枚举量用作标签

#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

数字填满了,就循环结束的程序

#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;    // get rid of bad input
			cout << "Please enter a number: ";
		}
	}
	double total = 0.0;
	for (i = 0; i < Max; i++)
		total += golf[i];

	cout << total / Max << " = average score "
		<< Max << " rounds\n";
	return 0;
}
Please enter your golf scores.
You must enter 5 rounds.
round #1: |88
round #2: |87
round #3: |must i?
Please enter a number: |103
round #4: |94
round #5: |86
91.6 = average score 5 rounds

●关于cin.clear()标志

void clear(iostate flags = goodbit);
函数clear()清除与当前流相关联的标志。默认标志是goodbit,clear()清除所有标志,否则只有指定的标志被清除。
C++为标准输入和输出定义了一些格式标志, 它可以通过flags(), setf(), 和unsetf()三个函数来控制。
例如, cout.setf(ios::left); 对所有指向cout的输出进行左对齐调整。

程序中有段错误处理的关键代码:

while (!(cin >> golf[i])) {
    cin.clear();     // reset input
    while (cin.get() != '\n')
        continue;    // get rid of bad input
    cout << "Please enter a number: ";
}

如果用户输入88,则cin表达式将为true,因此将一个值放到数组中;而表达式!(cin >> golf [i])为false,因此结束内部循环。然而,如果用户输入must i?,则cin表达式将为false,因此不会将任何值放到数组中;而表达式!(cin >> golf [i])将为true,因此进入内部的while循环。该循环的第一条语句使用clear()方法重置输入,如果省略这条语句,cin==false时程序将拒绝继续读取输入。接下来,程序在while循环中使用cin.get()来读取行尾之前的所有输入,从而删除这一行中的错误输入。另一种方法是读取到下一个空白字符,这样将每次删除一个单词,而不是一次删除整行。最后,程序告诉用户,应输入一个数字。

简单的文件输入/输出:

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

假设有如下示例输入行:

38.5 19.2

➀首先,来看使用char数据类型的情况:

char ch;
cin >> ch;

输入行中的第一个字符被赋给ch。在这里,第一个字符是数字3,其字符编码(二进制)被存储在变量ch中。输入和目标变量都是字符,因此不需要进行转换。注意,这里存储的不是数值3,而是字符3的编码。执行上述输入语句后,输入队列中的下一个字符为字符8,下一个输入操作将对其进行处理。

➁接下来看看int类型:

int n;
cin >> n;

在这种情况下,cin将不断读取,直到遇到非数字字符。如果输入的是38.5,也就是说,它将读取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中,并在末尾加上一个空字符'\0'。这里不需要进行任何转换。

➄最后,来看一下另一种使用char数组来存储输入的情况:

char word[50];
cin.getline(word, 50);

在这种情况下,cin将不断读取,直到遇到换行符(示例输入行少于50个字符)。所有字符都将被存储到数组word中,并在末尾加上一个空字符。换行符被丢弃,输入队列中的下一个字符是下一行中的第一个字符。这里不需要进行任何转换。

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

本章讨论的文件I/O相当于控制台I/O,因此仅适用于文本文件。要创建文本文件,用于提供输入,可使用文本编译器,如DOS中的EDIT、Windows中的“记事本”和UNIX/Linux系统中的vi或emacs。

写入到文本文件中

使用文件输出的主要步骤如下。

1.包含头文件<fstream>

2.创建一个ofstream对象

3.将该ofstream对象同一个文件关联起来

4.就像使用cout那样使用该ofstream对象 

#include <fstream>  // for file I/O
#include <iostream>
using namespace std;
int main() {
  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;
}
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

该程序的最后一部分与cout部分相同,只是将cout替换为outFile而已。可以看到在可执行文件目录生成了一个名为carinfo.txt文件(根据编译器的配置,该文件也可能位于其他文件夹):

outFile可使用cout可使用的任何方法。还可以使用各种格式化方法,如setf( )和precision( )。这些方法只影响调用它们的对象。

该程序运行之前,文件carinfo.txt并不存在。在这种情况下,方法open( )将新建一个名为carinfo.txt的文件。如果在此运行该程序,文件carinfo.txt将存在,此时情况将如何呢?默认情况下,open()将首先截断该文件,即将其长度截短到零——丢其原有的内容,然后将新的输出加入到该文件中。

读取文本文件

#include <cstdlib>  // support for exit()
#include <fstream>  // file I/O support
#include <iostream>
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()) {  // failed to open file
    cout << "Could not open the file " << filename << endl;
    cout << "Program terminating.\n";
    // cin.get();    // keep window open
    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;
}

首先在可执行目录下新建一个名为scores.txt的文件,内容如下:

Enter name of data file: |scores.txt
End of file reached.
Items read: 12
Sum: 204.5
Average: 17.0417

注:如果在scores.txt文件最后一行不输入[Enter]一个换行符,“17.5”这个数不会被统计和计算,因为没有最后的换行符时读到“17.5”就到文件结尾了inFile.good()==EOF。如果首行“18”后面有N个空格,输出也是如上;如果“18”后面空格用“#”号代替,输出结果如下:

Enter name of data file: |scores.txt
Input terminated by data mismatch.
Items read: 1
Sum: 18
Average: 18

所以,对于ifstream>>value空格和回车换行符是分隔符,读取时会直接忽略掉

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

由于eof()只能判断是否到达EOF,而fail()可用于检查EOF和类型不匹配,因此上述代码首先判断是否到达EOF。这样,如果执行到了else if测试,便可排除EOF,因此,如果fail()返回true,便可断定导致循环终止的原因是类型不匹配。

其实,表达式inFile >> value的结果为inFile,而在需要一个bool值的情况下,inFile的结果为inFile.good( ),即true或false。

因此,可以对代码进行精简,将两条输入语句用一条用作循环测试的输入语句代替:

while (inFile >> value) {
    ...
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

itzyjr

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值