学习一门语言,最重要的是实践。自己决定学习C++的初衷,并不是奢望自己成为C++的编程高手,而是希望C++成为一个自己手中熟练好用的语言工具,利用C++自己可以将头脑中闪过的小火花转变成现实,说白了就是自己开始对C++语言的定位就是用来开发一些有意思的小工具。这几天想动手写一个生成明文口令的小程序,想着用C++来写应该不难,但是前前后后却用了快一周的时间,中间当然也有自己拖沓的原因。编写的过程并不顺利,一个小程序磕磕绊绊,尤其是自己决定用OPP来写,开头确实费了一些周折;C++的文件操作上也是来回犯错,开头为了一个fstream无法创建文件的问题几乎都快发疯了。幸好最后把程序写出来了,虽然还存在一些问题。在这个过程中,自己认识到如果想把C++语言作为一门上手的工具来用,就必须掌握C++提供的“武器库”,尤其是针对不同问题或者需求的工具,比如读写文件使用fstream,字符串表达直接使用C++提供的可变长字符串对象string等。这些工具熟练了,自己使用C++的程度也会越来越熟练。今天抽出时间,将自己在写这个程序的时候,感觉有用的一些“兵器”简单梳理一下。
一、标准库I/O
其实标准库I/O包括对命令窗口和文件和其他设备的读写,但是这里自己想说的只是iostream库下的标准输入输出对象。这里与C是不同的,C语言中的格式化输入输出是利用函数printf/scanf实现的,C++的风格是全部使用对象来实现。这里我们常用的是以下四个对象:
1. cin:标准输入对象,从窗口读入到程序;
2. cout:标准输出对象,从程序输出到窗口等设备;
3. cerr/clog:cerr是错误输出对象,clog是日志输出对象;
一般我们常用也是就是cin和cout,cerr一般会用作错误提示。cin/cout/cerr/clog都称为流对象,“流”就是要从某种IO设备上读入或写出的字符序列,人们使用“流”这一术语试图说明字符序列是时间的函数,是随着时间顺序生成或消耗的。系统负责将这些对象与执行程序的窗口绑定起来,来确定输入、输出的来源和目标。
与输入输出对象同样重要的是输入输出操作符,它们一起构成了输入输出表达式。这里只需要注意输入操作符的左侧必须是istream对象,操作结果是从istream对象读入数据复制到右操作数上,返回左侧的istream对象;输出操作符左侧必须是ostream对象,将右侧操作数的输出到ostream对象绑定的对象上,返回左侧的ostream对象,重要的是可以连续输入输出:
cin >> str1 >> str2;
cout << str1 << endl << str2 << endl; //endl输出换行符,同时刷新输入输出缓冲区
第三个重要的是流表达式作为循环条件,比如while (cin >> a);
流表达式作为条件时,返回的是流状态,上面的cin状态为:若成功输入数据写入到a,且还可以继续写入数据,即返回真;如果遇到非法字符或EOF,则返回假。同理可以知道cout作为条件时的结果,即只要还可以继续cin或cout,就是成功,否则失败。Windows下的EOF是ctrl+z,Unix系统下是ctrl+D/C。
二、变量基本类型与标准库
每种语言都有共同的部分,即基本元素。比如C++内建了C的数据类型和函数,C++的内建数据类型有:
1. 整型:整数-->short/int/long | 字符-->char/wchar_t | 布尔-->bool;
2. 浮点型:float\double\long double
一般使用,整型使用unsigned int就够了,浮点型一般使用double也足够运行了。
C++提供了丰富实用的标准库,使得我们可以操纵一些复合数据类型,比如字符串不需要像C里一样使用字符数组,而是直接提供了string类,数组和指针也被vector类模板和迭代器所替代。可以说,C++编程比C更加抽象,但是好用。
对于string类型,需要知道是可变长字符串,定义与初始化,以及提供的一些string函数,如:
1. s.empty()
2. s.size()
3. s[n]
4. s1 + s2
5. s1 = s2
6. v1 == v2
7. !=, <, <=, >, >=
这里印象最深的是字符串中间的复制赋值,拼接非常简便,就想python里一样,当s1 = s2时,首先清空ss1,然后分配足以容纳s2的空间,最后将2的内容复制到s1中。这里需要注意对于string和vector等标准库容器来说,元素类型必须是string::size_type、vector::size_type,C++会自动为我们分配足以使用的数据类型,使得我们不需要考虑硬件和IDE的不同实现。
vector作为一种类模板,必须在定义时指明元素类型,其提供的操作函数基本和string一样,可以看到C++使得大多数对象的借口具有一致性:
1. v.empty()
2. v.size()
3. v.push_back(t)
4. v[n]
5. v1 = v2
6. v1 == v2
7. !=, <,>,<=,>=
vector可以使用下标引用元素,但是下标不能引用不存在的元素,即下标不能添加元素,添加元素只能使用push_back()方法。vector物理上表现为一串连续存储的单元,我们一般推荐直接定义空vector,因为vector具有自动增长机制,即可以按照需要分配空间,由于分配时采用额外分配,所以效率反而比链表存储要高。遍历vector以及其他容器,均推荐使用迭代器,可以理解成指针,定义时指明类型,看懂以下语句就可以基本使用迭代器了:
1. vector::iterator iter = vec.begin();
2. while (iter != vec.end())
{
*****
iter ++;
}
最后我们来看看针对istream对象的getline函数,具有两个参数:istream和string,将从istream对象中读取数据写入到string对象。由于istream派生了iostream\fstream,因此文件也可以作为第一个参数,也就可以实现从文件中的读取。
对于字符串读入写出来说,输入操作符cin会忽略开头的空白字符,读取直到再次遇到空白字符结束,所谓的空白字符为空格、换行、制表符,因此cin可以用来读取单词;getline函数不会忽略开头的换行符,每读到一个换行符即向string写入一次,但是读取的内容不包括换行符。这样说难免抽象,我们具体看个例子吧!
-
//Getline函数测试
-
#include <iostream>
-
#include <string>
-
#include <fstream>
-
-
using namespace std;
-
-
int main()
-
{
-
fstream tfile;
-
string str;
-
tfile.open("Getline-Test-tmp.txt", ios_base::out);
-
cout << "Please input string..." << endl;
-
cout << "Input Test without endl..." << endl;
-
tfile << "这是一个关于Getline的测试函数" << endl;
-
while (cin >> str) //cin读取规则:忽略开头的空白字符,到遇到的第一个空白字符为止,不会读入空白字符
-
{
-
tfile << str;
-
str.clear();
-
}
-
cout << "Input Test..." << endl;
-
tfile.close();
-
tfile.clear();
-
-
fstream file;
-
file.open("GT.txt", ios_base::out);
-
tfile.open("Getline-Test-tmp.txt", ios_base::in);
-
while (getline(tfile, str)) //getline读取每一行,且不读入源的换行符,必须人为添加endl
-
{
-
//cout << "str is : "<< str <<endl;
-
cout << "str is : "<< str << endl;
-
file << str ;
-
str.clear();
-
}
-
tfile.close();
-
tfile.clear();
-
file.close();
-
file.clear();
-
//system("del Getline-Test-tmp.txt");
-
cout << "Test Finished..." << endl;
-
system("pause");
-
return 0;
-
-
-
-
}
上述程序执行结果为:
输出的源文件——cin读入时没有读入换行等字符,必须在输入文件时人为添加endl:
最后写入的文件——getline函数遇到换行符结束,却不读入换行符:
三、文件操作
文件IO主要借助文件流对象来实现,虽然有ifstream和ofstream对象,但是为了方便期间,我们还是在程序中统一使用fstream对象,至于读写行为利用其文件模式ios_base::in | ios_base::out | ios_base::app来实现。这里需要注意,虽然理论上fstream对象同时具有in | out模式,但是VS2008下无法利用in | out创建一个可读写的文件,这里应该和不同的编译器实现有关,所以最保险的方法是,创建一个fstream对象,然后:
1. 读文件:
fstream rfile;
file.open("filename", ios_base::in);
2. 写文件: //创建文件只带out模式即可
fstream wfile;
wfile.open("filename", ios_base::out | ios_base::app | ios_base::trunc);
并且,一次文件操作之后,记得要关闭相应的流对象,并且清空该流对象的条件状态:file.close(); file.clear(); 一般一个流对象读写结束的时候会将条件状态置为操作结束的无效状态,必须clear()之后才可以继续使用该文件流对象。
除此之外,很有用的还有文件指针操作,比如返回当前指针:file.tell(),设置读指针位置:file.seekg(off, ios_base::beg || ios_base::end),写文件指针为file.seekp(off, ios_base::beg || ios_base::end);
小结:
自己喜欢将C与C++放在一起说:C/C++,因为一方面C++完全兼容C,C中的低级、基本的数据类型和函数均可以在C++中使用,另一方面C++针对C的低级数据类型进行了抽象,提出了自己的自定义类型——类,以及由此带来的继承与多态,同时还封装了一批抽象数据类型,使得程序员不再需要总是考虑问题的底层,从“开发有效率的程序”转变到“有效率地开发程序”,如何能够更好、更便捷地开发程序,而又不失强大的表现力和底层操作力,才是C/C++所追求的理念。
阅读(468) | 评论(0) | 转发(0) |