编写一个程序,要求用户输入一串整数和任意数目的空格,这些整数必须位于同一行中,但允许出现在该行中的任何位置。当用户按下键盘上的“Enter”键时,数据输入结束。程序自动对所有的整数进行求和并打印出结果。
C 语言版本:
#include <stdio.h> #include <stdlib.h> void main() { int i; int sum = 0; char ch; printf("请输入一串整数和任意数目的空格: "); while( scanf("%d", &i) == 1 ) { sum += i; while( (ch=getchar()) == ' ' ) //getchar()只能接受一个字符 ;// 屏蔽空格 if( ch == ' ' ) { break; } ungetc( ch, stdin ); // 将变量ch 中存放的字符退回给stdin输入流。 } printf("结果是: %d", sum); printf(" "); system("pause"); }
C++ 语言版本:
#include <iostream> using namespace std; int main() { int sum = 0; int i; cout << "请输入一串整数和任意数目的空格:"; while( cin >> i ) { sum += i; while( cin.peek() == ' ' ) { cin.get(); } if( cin.peek() == ' ' ) { break; } } cout << "结果是: " << sum << endl; return 0; }
C++ 版本分析:这个程序使用了流对象 cin。这个对象的类型是istream,它知道如何从用户终端读取数据。
cin >> i; cin 输入操作符又称为提取操作符,它一次从输入流对象cin 提取一个整数。 当用户进行键盘输入时,对应的字符将输入到操作系统的键盘缓冲区中。 这样,当用户点击键盘上的“Enter”键时,操作系统把键盘缓冲区的内容传输到cin 流的内部缓冲区,“>>”操作符随后从这个缓冲区提取需要的信息。我们还发觉,如果用户不进行键盘的输入,程序将会阻塞。这是阻塞性(blocking)IO的例子。
“>>”最初定义为右移操作符,它在C++ 中进行了重载,当它按照这里所示的方式使用时,它就用于从输入流对象提取信息。 另外,这个操作符对所有内建的数据类型都进行了重载,所以它可以从输入流对象提取出int, float, double 型数据,也可以提取字符串等数据。
在while( cin >> i ) 中,表达式 cin >> i 返回输入流对象本身,也就是cin。但是,如果到达了文件尾或者提取操作符遇到一个非法值,这个返回值将是 false。 我们小举一个例子:如果我们试图把一个浮点数读取到一个 int 型变量中,当提取操作符遇到小数点时,它将使输入流对象置于一个错误的状态中,导致 cin 的值为 false。
注意,在while( cin >> i ) 中,当用户在键盘上点击“enter”键的时候,在这一句并不会结束。
另外,观察两个程序,我们发现:C 和 C++事实上在声明变量的事实上有点不同,C++允许我们在程序的任意位置声明变量。这大大的提高了大型C++ 程序的可读性,因为这样子我们就可以在实际需要使用变量的时候才来声明他们。
1.cin、cout对象的多个方法
cin 是 istream类的对象,那么cin 同样具有一些方法,这些方法可以增强对于输入操作的控制,下边我们将通过简单的例子介绍 cin 的一些方法。
例子一:cin.ignore() & cin.getline()
#include <iostream> using namespace std; int main() { char buf[20]; cin.ignore(7);// 函数作用是跳过输入流中n个字符,或在遇到指定的终止字符时提前结束(此时跳过包括终止字符在内的若干字符)。 cin.getline( buf, 10 ); cout << buf << endl; return 0; }
例子二:cin.get() & cin.peek()
#include <iostream> using namespace std; int main() { char p; cout << "请输入一段文本: "; while( cin.peek() != ' ' )//cin.peek()的返回值是一个char型的字符,其返回值是指针指向的当前字符,但它只是观测指针停留在当前位置并不后移。 { p = cin.get(); cout << p; } cout << endl; return 0; }
例子三:cin.gcount() & cin.read()
#include <iostream> using namespace std; int main() { const int SIZE = 50; char buf[SIZE]; cout << "请输入一段文本: "; cin.read( buf, 20 ); cout << "字符串收集到的字符数为: " << cin.gcount() << endl; cout << "输入的文本信息是: "; cout.write( buf, 20 ); cout << endl; return 0; }
前边我们已经介绍了几种常用的 cin 对象的方法,下边再介绍几种关于 cout 对象的方法。同样我们通过实例在让大家快速认识!
实例一:cout.precision()
#include <iostream> #include <math.h> using namespace std; int main() { double result = sqrt(3.0); cout << "对 3 开方保留小数点后0 ~ 9 位,结果如下: " << endl; for( int i=0; i <= 9; i++ ) { cout.precision(i); cout << result << endl; } cout << "当前的输出精度为: " << cout.precision() << endl; return 0; }
实例二:cout.width()
#include <iostream> using namespace std; int main() { int width = 4; char str[20]; cout << "请输入一段文本: "; cin.width(5); while( cin >> str ) { cout.width(width++); cout << str << endl; cin.width(5); } return 0; }
结果:
cout.width(4)就是输出的字符串宽度为4,不足的会用空格补足。比方说你要输出“12”,但是在输出之前用了这句话就会输出“ 12”。
cin.width(5):实际只能提取4个字符,str最后一个是空字符。
2.文件 I/O
编写一个文件复制程序,功能实现将一个文件复制到另一个文件。 例如:fileCopy sourceFile destFile
C语言版实例分析:fileCopy.c
#include <stdio.h> #include <stdlib.h> int main( int argc, char* argv[] ) { FILE *in, *out; int ch; // char if( argc != 3 ) { fprintf( stderr, "输入形式: copyFile 源文件名 目标文件名 " ); exit( EXIT_FAILURE ); } if( ( in = fopen( argv[1], "rb") ) == NULL ) { fprintf( stderr, "打不开文件: %s ", argv[1] ); exit( EXIT_FAILURE ); } if( ( out = fopen( argv[2], "wb") ) == NULL ) { fprintf( stderr, "打不开文件: %s ", argv[2] ); fclose( in ); // 记得擦屁股 exit( EXIT_FAILURE ); } while( (ch = getc(in)) != EOF ) // EOF == end of file,其实是宏定义,值为-1,文件结尾一般以-1为返回值标志着结束。 返回EOF的条件:要么是文件结束,要么是遇到错误。 { if( putc( ch, out ) == EOF ) { break; } } if( ferror( in ) ) { printf("读取文件 %s 失败! ", argv[1] ); } if( ferror( out )) { printf("写入文件 %s 失败! ", argv[2] ); } printf("成功复制1个文件! "); fclose( in ); fclose( out ); return 0; }
argc 与 argv[] :在程序中,main 函数有两个参数,整型变量 argc 和字符指针数组 argv[]。
argc 的含义是程序的参数数量,包含本身。 argv[] 的每个指针指向命令行的一个字符串,所以argv[0] 指向字符串"copyFile.exe"。argv[1] 指向字符串sourceFile,argv[2] 指向字符串destFile。
in 和 out 是我们声明的两个文件指针,它们的类型都是 FILE*,分别作为两个 I/O 流对象使用。
if( argc != 3 ) 是为了确保程序参数个数的正确性。
通过 fopen() 函数我们以二进制的形式按可读/可写方式打开两个文件并返回两个文件指针给 in 和 out。
为了确保文件成功打开,我们还对 fopen()的返回值进行了检查,如果未成功打开,我们就向标准错误流 stderr 发送一条消息。
getc() 函数一次从输入流(stdin) 读取一个字符,putc() 函数把这个字符写入到输出流(stdout)。
当getc() 遇到文件结束标志的时候,函数就返回 EOF。
EOF 是一个宏,在stdio.h中定义,其值为一个负整数,通常是 -1。
EOF 事实上有两个含义:MSDN,即要么是文件结束,要么是遇到错误。
注意细节,getc() 的返回值是 int 类型哦,所以我们声明时应该是 int ch。而不是char ch。
3.C++的文件操作
#include <fstream>//文件流操作 #include <iostream> using namespace std; int main() { ifstream in;//ifstream为1个读取类 in.open( "test.txt" ); if( !in ) { cerr << "打开文件失败" << endl;//cerr:输出到标准错误的ostream对象,常用于程序错误信息; return 0; } char x; while( in >> x ) { cout << x;//cout:写到标准输出的ostream对象; } cout << endl; in.close(); return 0; }
从刚刚的例题我们得到的信息是C++ 由于有类的封装,很多东西都变得更加“仔细”了! 上边的例题我们用到的是文件的读取类 ifstream。 接着我们结合例题来说说文件的写入要用到的类 ofstream。
#include <fstream> #include <iostream> using namespace std; int main() { ofstream out;//写入类 out.open( "test.txt" ); if( !out ) { cerr << "打开文件失败!" << endl; return 0; } for( int i=0; i < 10; i++ ) { out << i; } out << endl; out.close(); return 0; }
在前边两个例子中我们出现:
ifstream in;
in.open( "test.txt" );
和
ofstream out;
out.open( "test.txt" );
它们都是用一个open 函数来完成打开文件的功能。当然,这不是唯一的方法,我们还可以这样实现。
ifstream in( "test.txt" );
和
ofstream out( "test.txt" );
以上代码在创建一个ifstream 和ofstream 类的对象时,将文件的名字传递给它们的构造函数.暂时我们可以这么理解构造函数:就是对象默认使用的函数(方法)。
那么这两种方法有什么区别吗?结论是没有区别! 事实上它还可以接受不止一个参数! 下边我们给出一个接受两个参数的实例:
ifstream in( char* filename, int open_mode)
其中,filename 表示文件的名称,它是一个字符串; open_mode 表示打开模式,其值用来定义以怎样的方式打开文件(跟open的参数一样哈)。
下面给出几种常见的打开模式:
ios::in -- 打开一个可读取文件
ios::out -- 打开一个可写入文件
ios::binary -- 以二进制的形式打开一个文件。
ios::app -- 写入的所有数据将被追加到文件的末尾
ios::trunk -- 删除文件原来已存在的内容
ios::nocreate -- 如果要打开的文件并不存在,那么以此参数调用open 函数将无法进行。
ios::noreplece -- 如果要打开的文件已存在,试图用open 函数打开时将返回一个错误。
例子:
#include <fstream> #include <iostream> using namespace std; int main() { ofstream out( "test.txt", ios::app ); if( !out ) { cerr << "文件打开失败!" << endl; return 0; } for( int i=10; i > 0; i-- ) { out << i; } out << endl; out.close(); return 0; }
如果我需要的不只是一种打开模式,要多种并存怎么办呢? 我们只需要使用 OR 操作符:“|”
#include <fstream> #include <iostream> using namespace std; int main() { fstream fp("test.txt", ios::in | ios::out ); if( !fp ) { cerr << "打开文件失败!" << endl; return 0; } fp << "IloveFishc.com!"; static char str[100]; fp.seekg(ios::beg); // ios::beg使得文件指针指向文件头, ios::end 则是文件尾。 fp >> str; cout << str << endl; fp.close(); return 0; }
4.输出输入小结
题目1:这个程序将向用户提出一个“Y/N”问题,然后把用户输入的值赋值给answer变量。 要求: 针对用户输入‘Y’或‘y'和‘N’或‘n’进行过滤; 发掘程序可能存在的任何问题,想想为什么?。 实践开始!
#include <iostream> int main() { char answer; std::cout << "可以格式化您的硬盘吗?[Y/N]"; std::cin >> answer; std::cout << " "; switch( answer ) { case 'Y': case 'y': std::cout << "我想还是别了, 数据丢了妈妈会骂的~" << " "; break; case 'N': case 'n': std::cout << "您的选择是正确的, 硬盘不能随便给人格式化!" << " "; break; default: std::cout << "请输入[Y/N]回答问题。" << " "; break; } //以下代码是为实现运行完后不会立马结束关闭 std::cout << "输入任意键结束程序!" << " "; std::cin.get(); std::cin.ignore(100, ' ');//忽略100个字符,直到遇到回车 return 0; }
题目2:编写一个“温度单位转换程序”,提示用户以【xx.x C】或【xx.x F】的格式输入。要求: 如果用户输入的是34.2 C 程序将自动转换为 90.32 F 并输出!
#include <iostream> int main() { const unsigned short ADD_SUBTRACT = 32; const double RATIO = 9.0 / 5.0; float tempIn, tempOut; char typeIn, typeOut; std::cout << "请以【xx.x C】或【xx.x F】的形式输入温度: "; std::cin >> tempIn >> typeIn; std::cin.ignore(100, ' ');//为了不让回车影响,直到遇到回车字符前都忽略掉 std::cout << " "; switch( typeIn ) { case 'C': case 'c': tempOut = (tempIn * RATIO) + ADD_SUBTRACT; typeOut = 'F'; typeIn = 'C'; break; case 'F': case 'f': tempOut = (tempIn - ADD_SUBTRACT) / RATIO; typeOut = 'C'; typeIn = 'F'; break; default: typeOut = 'E'; break; } if( typeOut != 'E' ) { std::cout << tempIn << typeIn << " = " << tempOut << typeOut << " "; } else { std::cout << "请按照给出格式输入!" << " "; } //以下代码是为实现运行完后不会立马结束关闭,即实现等待输入 std::cout << "请输入任意字符结束!" << " ";//上述std::cin.ignore(100, ' ')语句已经清除掉键盘缓冲区,后续没输入,不需要重新清除 std::cin.get(); return 0; }
对输入数据进行合法性检查:“不要相信任何程序的输入,尤其是由用户输入的东西”,这是计算机安防工作的基本原则。往好处说,非法输入会让程序变得毫无用处; 往坏处说,非法输入会导致程序甚至是整个系统崩溃。 因此,知道如何对输入数据进行合法性检查与知道如何接受输入数据同样重要。 这是我们这系列要研究的一个重要课题!
对输入数据进行合法性检查的具体做法取决于具体的变量类型和它们的取值范围。比较常见的检查项目包括:
(1)检查某个数值是不是一个正数(例如商品的价格、老妈的年龄、老婆的体重);
(2)检查某个数值是不是落在给定的范围(例如女朋友的年龄不能小于0或者大于120吧?);
(3)检查某个字符是不是一个特定的值(例如前边的Y或者N)
前边的程序中我们已经见过了一些用来进行合法性检查的代码,例如question1和question2程序中使用switch()来判断用户是否输入了程序预期的值。 但是呢,还有一些潜伏的问题我们没有发掘出来:例如cin调用失败(无法把一个值赋值给一个变量)时,程序仍会像cin调用成功一样继续自欺欺人。解决这些问题的办法之一就是对cin 调用的结果不要做任何假设。
cin 对象有几个专门用来报告其工作情况的成员函数,它们将返回一个真/假值来表明cin 的状态。
(1)eof():如果到达文件(或输入)末尾,返回true;
(2)fail():如果cin 无法工作,返回true;
(3)bad():如果cin 因为比较严重的原因(例如内存不足)而无法工作,返回true;
(4)good():如果以上情况都没发生,返回true。
对question2 的合法性检查你觉得还有什么需要补充的吗?
#include <iostream> int main() { const unsigned short ADD_SUBTRACT = 32; const double RATIO = 9.0 / 5.0; float tempIn, tempOut; char typeIn, typeOut; std::cout << "请以【xx.x】的形式输入温度: "; while( !(std::cin >> tempIn) ) { std::cin.clear(); std::cin.ignore(100, ' '); std::cout << "请以【xx.x】的形式输入温度: "; } do{ std::cout << "请以【xx.x C】或【xx.x F】的形式输入温度: "; std::cin >> typeIn; }while( (typeIn != 'C') && (typeIn != 'c') && (typeIn != 'F') && (typeIn != 'f') ); std::cout << " "; switch( typeIn ) { case 'C': case 'c': tempOut = (tempIn * RATIO) + ADD_SUBTRACT; typeOut = 'F'; typeIn = 'C'; break; case 'F': case 'f': tempOut = (tempIn - ADD_SUBTRACT) / RATIO; typeOut = 'C'; typeIn = 'F'; break; default: typeOut = 'E'; break; } if( typeOut != 'E' ) { std::cout << tempIn << typeIn << " = " << tempOut << typeOut << " "; } else { std::cout << "请按照给出格式输入!" << " "; } std::cout << "请输入任意字符结束!" << " "; std::cin.get(); return 0; }