[转]
C++的iostream标准库介绍
0 为什么需要iostream
我们从一开始就一直在利用C++的输入输出在做着各种练习,输入输出是由iostream库提供的,所以讨论此标准库是有必要的,它与C语言的 stdio库不同,它从一开始就是用多重继承与虚拟继承实现的面向对象的层次结构,作为一个c++的标准库组件提供给程序员使用。
iostream为内置类型对象提供了输入输出支持,同时也支持文件的输入输出,类的设计者可以通过对iostream库的扩展,来支持自定义类型的输入输出操作。
为什么说要扩展才能提供支持呢?我们来一个示例。
#include <stdio.h>
#include <iostream>
usingnamespace std;
class Test
{
public:
Test(int a=0,int b=0)
{
Test::a=a;
Test::b=b;
}
int a;
int b;
};
int main()
{
Test t(100,50);
printf("%???",t);//不明确的输出格式
scanf("%???",t);//不明确的输入格式
cout<<t<<endl;//同样不够明确
cin>>t;//同样不够明确
system("pause");
}
由于自定义类的特殊性,在上面的代码中,无论你使用c风格的输入输出,或者
是c++的输入输出都不是不明确的一个表示,由于c语言没有运算符重载机制,导致
stdio库的不可扩充性,让我们无法让printf()和scanf()支持对自定义类对象的
扩充识别,而c++是可以通过运算符重载机制扩充 iostream库的,使系统能能够识
别自定义类型,从而让输入输出明确的知道他们该干什么,格式是什么。
在上例中我们之所以用printf与cout进行对比目的是为了告诉大家,C与C++处理输入输出的根本不同,我们从c远的输入输出可以很明显看出是函数调用方式,而c++的则是对象模式,cout和cin是ostream类和istream类的对象。
1 iostream: istream 和 ostream
C++中的iostream库主要包含下图所示的几个头文件:
IOSstream 库
|
fstream
|
iomainip
|
ios
|
iosfwd
|
iostream
|
istream
|
ostream
|
sstream
|
streambuf
|
strstream
|
我们所熟悉的输入输出操作分别是由istream(输入流)和ostream(输出流)这两个类提供的,为了允许双向的输入/输出,由istream和ostream派生出了iostream类。
类的继承关系见下图:
iostream库定义了以下三个标准流对象:
-
cin,表示标准输入(standard input)的istream类对象。cin使我们可以从设备读入数据。
-
cout,表示标准输出(standard output)的ostream类对象。cout使我们可以向设备输出或者写数据。
-
cerr,表示标准错误(standard error)的osttream类对象。cerr是导出程序错误消息的地方,它只能允许向屏幕设备写数据。
输出主要由重载的左移操作符(<<)来完成,输入主要由重载的右移操作符(>>)完成:
-
>>a表示将数据放入a对象中。
-
<<a表示将a对象中存储的数据拿出。
这些标准的流对象都有默认的所对应的设备,见下表:
C++对象名
|
设备名称
|
C中标准设备名
|
默认含义
|
cin
|
键盘
|
stdin
|
标准输入
|
cout
|
显示器屏幕
|
stdout
|
标准输出
|
cerr
|
显示器屏幕
|
stderr
|
标准错误输出
|
上表中的意思表明cin对象的默认输入设备是键盘,cout对象的默认输出设备是显示器屏幕。
那么原理上C++有是如何利用cin/cout对象与左移和右移运算符重载来实现输入输出的呢?
下面我们以输出为例,说明其实现原理:
-
cout是ostream类的对象,因为它所指向的是标准设备(显示器屏幕),所以它在iostream头文件中作为全局对象进行定义。
-
ostream cout(stdout);//其默认指向的C中的标准设备名,作为其构造函数的参数使用。
-
在iostream.h头文件中,ostream类对应每个基本数据类型都有其友元函数对左移操作符进行了友元函数的重载。
-
ostream& operator<<(ostream &temp,int source);
-
ostream& operator<<(ostream &temp,char *ps);
-
... 等等
一句输出语句:cout<<"http://www.cppblog.com/andxie99";,事实上调用的就是ostream& operator<<(ostream &temp,char *ps);这个运算符重载函数,由于返回的是流对象的引用,引用可以作为左值使用,所以当程序中有类似cout<<"http://www.cppblog.com/andxie99"<<"白纸人生";这样的语句出现的时候,就能够构成连续输出。
由于iostream库不光支持对象的输入输出,同时也支持文件流的输入输出,所以在详细讲解左移与右移运算符重载只前,我们有必要先对文件的输入输出以及输入输出的控制符有所了解。
2 fstream: ifstream 和 ofstream
和文件有关系的输入输出类主要在fstream.h这个头文件中被定义,在这个头文件中主要被定义了三个类,由这三个类控制对文件的各种输入输出操作,他们分别是ifstream、ofstream、fstream,其中fstream类是由iostream类派生而来,他们之间的继承关系见下图所示。
由于文件设备并不像显示器屏幕与键盘那样是标准默认设备,所以它在fstream.h头文件中是没有像cout那样预先定义的全局对象,所以我们必须自己定义一个该类的对象,我们要以文件作为设备向文件输出信息(也就是向文件写数据),那么就应该使用ofstream类。
ofstream类的默认构造函数原形为:
ofstream::ofstream(constchar *filename,int mode = ios::out,int openprot = filebuf::openprot);
-
filename: 要打开的文件名
-
mode: 要打开文件的方式
-
prot: 打开文件的属性
其中mode和openprot这两个参数的可选项表见下表:
mode属性表
|
ios::app
|
以追加的方式打开文件
|
ios::ate
|
文件打开后定位到文件尾,ios:app就包含有此属性
|
ios::binary
|
以二进制方式打开文件,缺省的方式是文本方式。两种方式的区别见前文
|
ios::in
|
文件以输入方式打开
|
ios::out
|
文件以输出方式打开
|
ios::trunc
|
如果文件存在,把文件长度设为0
|
可以用“或”把以上属性连接起来,如ios::out|ios::binary。
openprot属性表
|
属性
|
含义
|
0
|
普通文件,打开访问
|
1
|
只读文件
|
2
|
隐含文件
|
4
|
系统文件
|
可以用“或”或者“+”把以上属性连接起来 ,如3或1|2就是以只读和隐含属性打开文件。
实例代码如下:
#include <fstream>
usingnamespace std;
int main()
{
ofstream myfile("c:\\1.txt",ios::out|ios::trunc,0);
myfile<<"白纸人生"<<endl<<"网址:"<<"www.cppblog.com/andxie99";
myfile.close()
system("pause");
}
文件使用完后可以使用close成员函数关闭文件。
ios::app为追加模式,在使用追加模式的时候同时进行文件状态的判断是一个比较好的习惯。
示例如下:
#include <iostream>
#include <fstream>
usingnamespace std;
int main()
{
ofstream myfile("c:\\1.txt",ios::app,0);
if(!myfile)//或者写成myfile.fail()
{
cout<<"文件打开失败,目标文件状态可能为只读!";
system("pause");
exit(1);
}
myfile<<"白纸人生"<<endl<<"网址:"<<"www.cppblog.com/andxie99"<<endl;
myfile.close();
}
在定义ifstream和ofstream类对象的时候,我们也可以不指定文件。以后可以
通过成员函数open()显式的把一个文件连接到一个类对象上。
例如:
#include <iostream>
#include <fstream>
usingnamespace std;
int main()
{
ofstream myfile;
myfile.open("c:\\1.txt",ios::out|ios::app,0);
if(!myfile)//或者写成myfile.fail()
{
cout<<"文件创建失败,磁盘不可写或者文件为只读!";
system("pause");
exit(1);
}
myfile<<"白纸人生"<<endl<<"网址:"<<"www.cppblog.com/andxie99"<<endl;
myfile.close();
}
下面我们来看一下是如何利用ifstream类对象,将文件中的数据读取出来,然后再输出到标准设备中的例子。
代码如下:
#include <iostream>
#include <fstream>
#include <string>
usingnamespace std;
int main()
{
ifstream myfile;
myfile.open("c:\\1.txt",ios::in,0);
if(!myfile)
{
cout<<"文件读错误";
system("pause");
exit(1);
}
char ch;
string content;
while(myfile.get(ch))
{
content+=ch;
cout.put(ch);//cout<<ch;这么写也是可以的
}
myfile.close();
cout<<content;
system("pause");
}
上例中,我们利用成员函数get(),逐一的读取文件中的有效字符,再利用put()成员函数,将文件中的数据通过循环逐一输出到标准设备(屏幕)上, get()成员函数会在文件读到默尾的时候返回假值,所以我们可以利用它的这个特性作为while循环的终止条件,我们同时也在上例中引入了C++风格的字符串类型string,在循环读取的时候逐一保存到content中,要使用string类型,必须包含string.h的头文件。
我们在简单介绍过ofstream类和ifstream类后,我们再来看一下fstream类,fstream类是由iostream派生而来,fstream类对象可以同对文件进行读写操作。
示例代码如下:
#include <iostream>
#include <fstream>
usingnamespace std;
int main()
{
fstream myfile;
myfile.open("c:\\1.txt",ios::out|ios::app,0);
if(!myfile)
{
cout<<"文件写错误,文件属性可能为只读!"<<endl;
system("pause");
exit(1);
}
myfile<<"白纸人生"<<endl<<"网址:"<<"www.cppblog.com/andxie99"<<endl;
myfile.close();
myfile.open("c:\\1.txt",ios::in,0);
if(!myfile)
{
cout<<"文件读错误,文件可能丢失!"<<endl;
system("pause");
exit(1);
}
char ch;
while(myfile.get(ch))
{
cout.put(ch);
}
myfile.close();
system("pause");
}
由于fstream类可以对文件同时进行读写操作,所以对它的对象进行初始话的时候一定要显式的指定mode和openprot参数。
接下来我们来学习一下串流类的基础知识,什么叫串流类?
3 strstream: ostrstream 和 istrstream
简单的理解就是能够控制字符串类型对象进行输入输出的类,C++不光可以支持C++风格的字符串流控制,还可以支持C风格的字符串流控制。
我们先看看看C++是如何对C风格的字符串流进行控制的,C中的字符串其实也就是字符数组,字符数组内的数据在内存中的位置的排列是连续的,我们通常用 char str[size]或者char *str的方式声明创建C风格字符数组,为了能让字符数组作为设备并提供输入输出操作,C++引入了ostrstream、istrstream、 strstream这三个类,要使用他们创建对象就必须包含strstream.h头文件。
-
istrstream类用于执行C风格的串流的输入操作,也就是以字符串数组作为输入设备。
-
ostrstream类用于执行C风格的串流的输出操作,也就是一字符串数组作为输出设备。
-
strstream类同时可以支持C风格的串流的输入输出操作。
istrstream类是从istream(输入流类)和strstreambase(字符串流基类)派生而来,ostrstream是从 ostream(输出流类)和strstreambase(字符串流基类)派生而来,strstream则是从iostream(输入输出流类)和和 strstreambase(字符串流基类)派生而来。
他们的继承关系如下图所示:
串流同样不是标准设备,不会有预先定义好的全局对象,所以不能直接操作,需要通过构造函数创建对象。
类istrstream的构造函数原形如下:
istrstream::istrstream(constchar *str,int size);
参数1表示字符串数组,而参数2表示数组大小,当size为0时,表示istrstream类对象直接连接到由str所指向的内存空间并以\0结尾的字符串。
下面的示例代码就是利用istrstream类创建类对象,制定流输入设备为字符串数组,通过它向一个字符型对象输入数据。代码如下:
#include <iostream>
#include <strstream>
usingnamespace std;
int main()
{
char *name = "www.cppblog.com/andxie99";
int arraysize = strlen(name)+1;
istrstream is(name,arraysize);
char temp;
is>>temp;
cout<<temp;
system("pause");
}
类ostrstream用于执行串流的输出,它的构造函数如下所示:
ostrstream::ostrstream(char *_Ptr,int streamsize,int Mode = ios::out);
第一个参数是字符数组,第二个是说明数组的大小,第三个参数是指打开方式。
我们来一个示例代码:
#include <iostream>
#include <strstream>
usingnamespace std;
int main()
{
int arraysize=1;
char *pbuffer=newchar[arraysize];
ostrstream ostr(pbuffer,arraysize,ios::out);
ostr<<arraysize<<ends;//使用ostrstream输出到流对象的时候,要用ends结束字符串
cout<<pbuffer;
delete[] pbuffer;
system("pause");
}
上面的代码中,我们创建一个c风格的串流输出对象ostr,我们将arraysize内的数据成功的以字符串的形式输出到了ostr对象所指向的pbuffer指针的堆空间中,pbuffer也正是我们要输出的字符串数组,在结尾要使用ends结束字符串,如果不这么做就有溢出的危险。
4 stringstream
对于stringstream了来说,不用我多说,大家也已经知道它是用于C++风格的字符串的输入输出的。 stringstream的构造函数原形如下:
stringstream::stringstream(string str);
示例代码如下:
#include <iostream>
#include <sstream>
#include <string>
usingnamespace std;
int main()
{
stringstream ostr("ccc");
ostr.put('d');
ostr.put('e');
ostr<<"fg";
string gstr = ostr.str();
cout<<gstr<<endl;
char a;
ostr>>a;
cout<<a
system("pause");
}
除此而外,stringstream类的对象我们还常用它进行string与各种内置类型数据之间的转换。示例代码如下:
#include <iostream>
#include <sstream>
#include <string>
usingnamespace std;
int main()
{
stringstream sstr;
//--------int转string----------- int a=100;
string str;
sstr<<a;
sstr>>str;
cout<<str<<endl;
//--------string转char[]--------
sstr.clear();//如果你想通过使用同一stringstream对象实现多种类型的转换,请注意在每一次转换之后都必须调用clear()成员函数。
string name = "colinguan";
char cname[200];
sstr<<name;
sstr>>cname;
cout<<cname;
system("pause");
}
接下来我们来学习一下输入/输出的状态标志的相关知识.
5 io_state 输入/输出的状态标志
C++中负责的输入/输出的系统包括了关于每一个输入/输出操作的结果的记录信息。这些当前的状态信息被包含在io_state类型的对象中。io_state是一个枚举类型(就像open_mode一样),以下便是它包含的值。
-
goodbit 无错误
-
Eofbit 已到达文件尾
-
failbit 非致命的输入/输出错误,可挽回
-
badbit 致命的输入/输出错误,无法挽回
有两种方法可以获得输入/输出的状态信息。一种方法是通过调用rdstate()函数,它将返回当前状态的错误标记。例如,假如没有任何错误,则rdstate()会返回goodbit.下例示例,表示出了rdstate()的用法:
#include <iostream>
usingnamespace std;
int main()
{
int a;
cin>>a;
cout<<cin.rdstate()<<endl;
if(cin.rdstate() == ios::goodbit)
{
cout<<"输入数据的类型正确,无错误!"<<endl;
}
if(cin.rdstate() == ios_base::failbit)
{
cout<<"输入数据类型错误,非致命错误,可清除输入缓冲区挽回!"<<endl;
}
system("pause");
}
另一种方法则是使用下面任何一个函数来检测相应的输入/输出状态:
bool bad();
bool eof();
bool fail();
bool good();
下例示例,表示出了上面各成员函数的用法:
#include <iostream>
usingnamespace std;
int main()
{
int a;
cin>>a;
cout<<cin.rdstate()<<endl;
if(cin.good())
{
cout<<"输入数据的类型正确,无错误!"<<endl;
}
if(cin.fail())
{
cout<<"输入数据类型错误,非致命错误,可清除输入缓冲区挽回!"<<endl;
}
system("pause");
}
如果错误发生,那么流状态既被标记为错误,你必须清除这些错误状态,以使你的程序能正确适当地继续运行。要清除错误状态,需使用clear()函数。此函数带一个参数,它是你将要设为当前状态的标志值。,只要将ios::goodbit作为实参。
示例代码如下:
#include <iostream>
usingnamespace std;
int main()
{
int a;
cin>>a;
cout<<cin.rdstate()<<endl;
cin.clear(ios::goodbit);
cout<<cin.rdstate()<<endl;
system("pause");
}
通常当我们发现输入有错又需要改正的时候,使用clear()更改标记为正确后,同时也需要使用get()成员函数清除输入缓冲区,以达到重复输入的目的。
示例代码如下:
#include <iostream>
usingnamespace std;
int main()
{
int a;
while(1)
{
cin>>a;
if(!cin)//条件可改写为cin.fail()
{
cout<<"输入有错!请重新输入"<<endl;
cin.clear();
cin.get();
}
else
{
cout<<a;
break;
}
}
system("pause");
}
最后再给出一个对文件流错误标记处理的例子,巩固学习,代码如下:
#include <iostream>
#include <fstream>
usingnamespace std;
int main()
{
ifstream myfile("c:\\1.txt",ios_base::in,0);
if(myfile.fail())
{
cout<<"文件读取失败或指定文件不存在!"<<endl;
}
else
{
char ch;
while(myfile.get(ch))
{
cout<<ch;
}
if(myfile.eof())
{
cout<<"文件内容已经全部读完"<<endl;
}
while(myfile.get(ch))
{
cout<<ch;
}
}
system("pause");
}
posted @
2006-06-29 20:30 Jerry Cat 阅读(356) |
评论 (0) |
编辑 收藏
[转自沐枫兄]用bitset进行2进制输入输出
C/C++内置的输入、输出,对整数进制只支持三种:8进制、10进制、16进制。记得很久以前,有好几次想用到2进制的输入输出,最终只好自已转换。
昨天kaikai突然发了一个POST:
kaikai:
看看能不能把这个程序改得更短
?
#include
<
cstdio
>
int
main()
{
char
*
a,b[
33
];
scanf(
"
%*d
"
);
while
(scanf(
"
%s
"
,b)
==
1
)
{
int
d
=
0
;
for
(a
=
b;
*
a;)d
=
d
*
2
|*
a
++%
2
;
printf(
"
%d.%d.%d.%d\n
"
,d
>>
24
&
255
,d
>>
16
&
255
,d
>>
8
&
255
,d
&
255
);
}
return
0
;
}
一眼看过去,呵呵,输入输出不可省,核心还是二进制的转换问题。于是想起以前曾经做过的事情,学习多了,直觉的可以用bitset来实现。打开MSDN,查找bitset,嘿,果然有定义流输入操作符。
写了一个测试:
#include
<
iostream
>
#include
<
bitset
>
using
namespace
std;
void
main()
{
bitset
<
32
>
a;
cin
>>
a;
cout
<<
a.to_ulong()
<<
endl;
}
呵呵,搞定了,输入2进制,输出10进制,完全正常。以后输入2进制串的时候,就不需要自已转换了。
posted @
2006-06-29 20:22 Jerry Cat 阅读(350) |
评论 (0) |
编辑 收藏
1》误会;2》钉子;3》且慢下手;4》宽大。
1》误会:
早年在美国阿拉斯加地方,有一对年轻人结婚,婚後生育,他的太太因难产而死,遗下一孩子。
他忙生活,又忙於看家,因没有人帮忙看孩子,就训练一只狗,那狗聪明听话,能照顾小孩,咬著奶瓶喂奶给孩子喝,抚养孩子。
有一天,主人出门去了,叫它照顾孩子。
他到了别的乡村,因遇大雪,当日不能回来。
第二天才赶回家,狗立即闻声出来迎接主人。他把房门开一看,到处是血,抬头一望,床上也是血,孩子不见了,狗在身边,满口也是血。
主人发现这种情形,以为狗性发作,把孩子吃掉了,大怒之下,拿起刀来向著狗头一劈,把狗杀死了。
之後,忽然听到孩子的声音,又见他从床下爬了出来,於是抱起孩子;虽然身上有血,但并未受伤。
他很奇怪,不知究竟是怎么一回事,再看看狗身,腿上的肉没有了,旁边有一只狼,口里还咬著狗的肉;
狗救了小主人,却被主人误杀了,这真是天下最令人惊奇的误会。
注:误会的事,是人往往在不了解、无理智、无耐心、缺少思考、未能多方体谅对方,反省自己,感情极为冲动的情况之下所发生。误会一开始,即一直只想到对方的千错万错;因此,会使误会越陷越深,弄到不可收拾的地步,
人对无知的动物小狗发生误会,尚且会有如此可怕严重的後果,这样人与人之间的误会,则其後果更是难以想像。
2》钉子:
有一个男孩有著很坏的脾气,於是他的父亲就给了他一袋钉子;并且告诉他,每当他发脾气的时候就钉一根钉子在後院的围篱上。
第一天,这个男孩钉下了37根钉子。慢慢地每天钉下的数量减少了。
他发现控制自己的脾气要比钉下那些钉子来得容易些。
终於有一天这个男孩再也不会失去耐性乱发脾气,他告诉他的父亲这件事,父亲告诉他,现在开始每当他能控制自己的脾气的时候,就拔出一根钉子。
一天天地过去了,最後男孩告诉他的父亲,他终於把所有钉子都拔出来了。
父亲握著他的手来到後院说:你做得很好,我的好孩子。
但看看那些围篱上的洞,这些围篱将永远不能回复成从前。
你生气的时候说的话将像这些钉子一样留下疤痕。
如果你拿刀子捅别人一刀,不管你说了多少次对不起,那个伤口将永远存在。
话语的伤痛就像真实的伤痛一样令人无法承受。
注:人与人之间常常因为一些彼此无法释怀的坚持,而造成永远的伤害。如果我们都能从自己做起,开始宽容地看待他人,相信你(你)一定能收到许多意想不到的结果……帮别人开启一扇窗,也就是让自己看到更完整的天空
…
…
3》且慢下手:
大多数的同仁都很兴奋,因为单位里调来一位新主管,据说是个能人,专门被派来整顿业务。
可是日一天天过去,新主管却毫无作为,每天彬彬有礼进办公室,便躲在里面难得出门,那些本来紧张得要死的坏份子,现在反而更猖獗了。
「他那里是个能人嘛!根本是个老好人,比以前的主管更容易唬!」
四个月过去,就在真正努力为新主管感到失望时,新主管却发威了……坏份子一律开革,能人则获得晋升。
下手之快,断事之准,与四月表现保守的他,简直像是全然换个人。
年终聚餐时,新主管在酒过三巡之後致词:“相信大家对我新到任期间的表现,和後来的大刀阔斧,一定感到不解,现在听我说个故事,各位就明白了:「我有位朋友,买了栋带著大院的房子,他一搬进去,就将那院子全面整顿
,杂草树一律清除,改种自己新买的花卉,某日原先的屋主往访,进门大吃一惊的问:『那最名贵的牡丹哪里去了?』我这位朋友才发现,他竟然把牡丹当草给铲了。後来他又买了一栋房子,虽然院子更是杂乱,他却是按兵不动
,果然冬天以为是杂树的植物,春天里开了繁花;春天以为是野草的,夏天里成了锦蔟;半年都没有动静的小树,秋天居然红了叶。直到暮秋,它才真正认清哪些是无用的植物,而大力铲除,并使所有珍贵的草木得以保存。
”
说到这儿,主管举起杯来:「让我敬在座的每一位,因为如果这办公室是个花园,你们就都是其间的珍木,珍木不可能一年到头开花结果,只有经过长期的观察才认得出啊!
4》宽大:
这是一个甫自越战归来的士兵的故事。
他从旧金山打电话给他的父母,告诉他们:「爸妈,我回来了,可是我有个不情之请。我想带一个朋友同我一起回家。」
「当然好啊!」他们回答「我们会很高兴见到的。」
不过儿子又继续下去「可是有件事我想先告诉你们,他在越战里受了重伤,少了一条胳臂和一只脚,他现在走投无路,我想请他回来和我们一起生活。」
「儿子,我很遗撼,不过或许我们可以帮他找个安身之处。」
父亲又接著说「儿子,你不知道自己在说些什么。像他这样残障的人会对我们的生活造成很大的负担。我们还有自己的生活要过,不能就让他这样破坏了。我建议你先回家然後忘了他,他会找到自己的一片天空的。」
就在此时,儿子挂上了电话,他的父母再也没有他的消息了。
几天後,这对父母接到了来自旧金山警局的电话,
告诉他们亲爱的儿子已经坠楼身亡了。警方相信这只是单纯的自杀案件。
於是他们伤心欲绝地飞往旧金山,并在警方带领之下到太平间去辨认儿子的遗体。
那的确是他们的儿子没错,但惊讶的是儿子居然,只有一条胳臂和一条腿。
故事中的父母就和我们大多数人一样。要去喜爱面貌姣好或谈吐风趣的人很容易,但是要喜欢那些造成我们不便和不快的人却太难了。我们总是宁愿和那些不如我们健康,美丽或聪明的人保持距离。然而感谢上帝,有些人却不会
对我们如此残酷。他们会无怨无悔地爱我们,不论我们多么糟总是愿意接纳我们。
今晚在你入睡之前,向上帝祷告,请赐给你力量去接纳他人,不论他们是怎么样的人;请帮助我们了解那些不同於我们的人。每个人的心里都藏著一种神奇的东西称为「友情」,你不知道它究竟是如何发生何时发生,但你却知道
它总会带给我们特殊的礼物。
你也会了解友情是上帝给我们最珍贵的赠与!
朋友就像是稀奇的宝物。他们带来欢笑,激励我们成功。
他们倾听我们内心的话,与我们分享每一句赞美。
他们的心房永远为我们而敞开。现在就告诉你的朋友你有多在乎他们。把
这篇文章转寄给所有你认为是朋友的人。
如果这篇文章又回来了,你将知道你拥有了一辈子的朋友。
试想:朋友,您一路看下来之後;一定有很深的感触吧。
那么,在对别人有所决定与判断之前,首先,请想想这是否是一个「误会」,然後,请考虑您是否一定要钉下这个「钉子」,如果可以的话,请「且慢下手」。因为,当您对别人「宽大」之时,即是对您自己宽大
posted @
2006-06-27 22:05 Jerry Cat 阅读(187) |
评论 (0) |
编辑 收藏
以塌实编码为荣 以心浮气躁为耻
以详细注释为荣 以注释残缺为耻
以勤于测试为荣 以懒于测试为耻
以简明文档为荣 以冗余文档为耻
以注重团队为荣 以孤傲自大为耻
以刻苦钻研为荣 以敷衍了事为耻
以善于总结为荣 以不思进取为耻
以质效并进为荣 以单取其一为耻
posted @
2006-06-27 21:41 Jerry Cat 阅读(814) |
评论 (2) |
编辑 收藏
很多人都觉得自己的程序的界面不那么美观, 往往VC默认产生的对话框比较单调, 因此很多人往往找到很多其它的控件对对话框进行美化修饰, 例如给静态控件设置字体, 设置背景颜色等等, 其实这些完全可以由VC自己的WM_CTLCOLOR消息来完成!
WM_CTLCOLOR消息用来完成对EDIT, STATIC, BUTTON等控件设置背景和字体颜色, 其用法如下:
1.首先在自己需要设置界面的对话框上点击右键-->建立类向导-->加入WM_CTLCOLOR消息-->自动生成OnCtlColor()函数, 此函数可以对本对话框的控件的界面外观做修饰, 用法如下:
将类向导产生的函数做如下修改:
HBRUSH CDialogColor::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CDialog::OnCtlColor(pDC,pWnd, nCtlColor);
// TODO: Change any attributes of the DC here
// 设置显示字体
CFont * cFont=new CFont;
cFont->CreateFont(16,0,0,0,FW_SEMIBOLD,FALSE,FALSE,0,
ANSI_CHARSET,OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,
DEFAULT_PITCH&FF_SWISS,"Arial");
// 对特定的控件做修改
switch()
{
case CTLCOLOR_STATIC: //对所有静态文本控件的设置
{
pDC->SetBkMode(TRANSPARENT);
//设置背景为透明
pDC->SetTextColor(RGB(255,255,0)); //设置字体颜色
pWnd->SetFont(cFont); //设置字体
HBRUSH B = CreateSolidBrush(RGB(125,125,255));
//创建画刷
return (HBRUSH) B; //返回画刷句柄
}
case CTLCOLOR_EDIT: //对所有编辑框的设置
{
pDC->SetBkMode(TRANSPARENT);
pDC->SetTextColor(RGB(255,255,0));
pWnd->SetFont(cFont);
HBRUSH B = CreateSolidBrush(RGB(125,125,255));
return (HBRUSH) B;
}
default:
return CDialog::OnCtlColor(pDC,pWnd, nCtlColor);
}
}
注:case的类别有以下几种:
CTLCOLOR_BTN 按钮控件
CTLCOLOR_DLG 对话框
CTLCOLOR_EDIT 编辑框
CTLCOLOR_LISTBOX 列表框
CTLCOLOR_MSGBOX 消息框
CTLCOLOR_SCROLLBAR 滚动条
CTLCOLOR_STATIC 静态文本
2.你可能觉得对所有的控件使用统一的界面设置觉得不自由, 其实VC同样可以对特定的ID的控件进行设置, 方法如下:
switch (pWnd->GetDlgCtrlID())
{
//针对ID为IDC_CTL1, IDC_CTL2和IDC_CTL3的控件进行同样的设置
case IDC_CTL1:
case IDC_CTL2:
case IDC_CTL3:
{
pDC->SetBkMode(TRANSPARENT);
pDC->SetTextColor(RGB(255,255, 0));
pWnd->SetFont(cFont);
HBRUSH B = CreateSolidBrush(RGB(125,125,255));
return (HBRUSH) B;
}
default:
return CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
}
posted @
2006-06-24 22:30 Jerry Cat 阅读(601) |
评论 (0) |
编辑 收藏
[转]GridCtrl使用详解
在单文档中的使用方法
步骤一 初始化
在CView类的.h头文件中包含文件:
#include "Gridctrl.h"
并且手写加入如下的成员函数:
CGridCtrl * m_pGridCtrl;
步骤二 构造与析构
构造函数中:
m_pGridCtrl = NULL;
析构函数中:
if(m_pGridCtrl)
delete m_pGridCtrl;
步骤三 如果需要打印功能的话添加同名打印函数代码
在CView类的OnBeginPrinting()函数中添加如下代码:
if(m_pGridCtrl)
m_pGridCtrl->OnBeginPrinting(pDC,pInfo); //简单吧,这就是类的好处
其它两个打印函数也一样的做法.
步骤四 在OnInitaUpdate()函数中或者你自己添加的要显示Grid的消息函数中如下初始化:
//创建非模式对话框
CDlg *dlg;
dlg=new CDlg();
dlg->Create(IDD_Dlg,this);
//初始化GridCtrl控件
if(m_pGridCtrl!=NULL)
{
delete m_pGridCtrl;
m_pGridCtrl=NULL;
}
if (m_pGridCtrl == NULL)
{
// Create the Gridctrl object
m_pGridCtrl = new CGridCtrl;
if (!m_pGridCtrl) return 0;
// Create the Gridctrl window
CRect rect;
GetClientRect(rect);
m_pGridCtrl->Create(rect, this, 100);
// fill it up with stuff
m_pGridCtrl->SetEditable(false);
m_pGridCtrl->SetTextBkColor(RGB(0xFF, 0xFF, 0xE0)); //黄色背景
m_pGridCtrl->EnableDragAndDrop(false);
try {
m_pGridCtrl->SetRowCount(k); //设置行数为k行
m_pGridCtrl->SetColumnCount(4); //k列
m_pGridCtrl->SetFixedRowCount(1); //标题行为一行
m_pGridCtrl->SetFixedColumnCount(1); //同上
}
catch (CMemoryException* e)
{
e->ReportError();
e->Delete();
return 0;
}
//填充列标题
int row=0;
for(int col=0;col<4;col++)
{
GV_ITEM Item;
Item.mask = GVIF_TEXT|GVIF_FORMAT;
Item.row = row;
Item.col = col;
if(col==0){
Item.nFormat = DT_CENTER|DT_WORDBREAK;
Item.strText.Format(_T("【类别】"),col);
}
else if(col==1){
Item.nFormat = DT_LEFT|DT_WORDBREAK;
Item.strText.Format(_T("第一列"),col);
}
else if(col==2){
Item.nFormat = DT_LEFT|DT_WORDBREAK;
Item.strText.Format(_T("第二列"),col);
}
m_pGridCtrl->SetItem(&Item);
}
// fill rows/cols with text
for (row = 1; row < k; row++)
for (col = 0; col < h; col++)
{
GV_ITEM Item;
Item.mask = GVIF_TEXT|GVIF_FORMAT;
Item.row = row;
Item.col = col;
if (col < 1) { //行标题头
Item.nFormat = DT_CENTER|DT_VCENTER
|DT_SINGLELINE|DT_END_ELLIPSIS
|DT_NOPREFIX;
Item.strText.Format(_T("%d"),row);
}
else if(col==1){ //第一列的值
Item.nFormat = DT_CENTER|DT_VCENTER
|DT_SINGLELINE|DT_END_ELLIPSIS
|DT_NOPREFIX;
str="aa";
Item.strText.Format(_T("%s"),str);
}else if(col==2){ //第二列第值
Item.nFormat = DT_CENTER|DT_VCENTER
|DT_SINGLELINE|DT_END_ELLIPSIS
|DT_NOPREFIX;
CString str;
str="bb";
Item.strText.Format(_T("%s"),str);
}
m_pGridCtrl->SetItem(&Item);
}
m_pGridCtrl->AutoSize();
//--------------设置行列距------------------
for(int a=1;a<m;a++)
m_pGridCtrl->SetRowHeight(a,21); //设置各行高
m_pGridCtrl->SetRowHeight(0,24); //设置0行高
m_pGridCtrl->SetColumnWidth(1,110); //设置2列宽
m_pGridCtrl->SetColumnWidth(2,160); //设置3列宽
m_pGridCtrl->SetColumnWidth(3,100); //设置4列宽
}
上例取自实际工程,稍有修改!
部分注释:
void SetVirtualMode(TRUE)
//设为虚模式
BOOL SetRowCount(int nRows) //设置总的行数。
BOOL SetFixedRowCount(int nFixedRows = 1)//设置固定的行数据
BOOL SetColumnCount(int nCols) //设置列数
BOOL SetFixedColumnCount(int nFixedCols = 1)//设置固定的列数
步骤五: 添加WM_SIZE消息,调整控件的界面占屏幕大小
if(m_pGridCtrl->GetSafeHWnd())
{
CRect rect;
GetClientRect(rect);
m_pGridCtrl->MoveWindow(rect);
}
在对话框中的使用方法
步骤一 创建数据显示表格对话框
在资源管理器中新创建一个对话框,假设为CDlgTestReportBox。 从工具箱中加入Custom Control,就是人头像的那个,将其区域拉伸至要显示数据表格的大小,充满整个对话框。
在CDlgTestReportBox类的头文件中:
#include "GridCtrl.h"
再定义成员变量:
CGridCtrl* m_pGrid;
添加OnShowWindow()消息处理函数如下:
void CDlgTestReportBox::OnShowWindow(BOOL bShow, UINT nStatus)
{
CDialog::OnShowWindow(bShow, nStatus);
// TODO: Add your message handler code here
if(m_pGrid!=NULL)
{
delete m_pGrid;
m_pGrid=NULL;
}
if(m_pGrid==NULL)
{
m_pGrid=new CGridCtrl;
CRect rect;
GetDlgItem(IDC_ReportAera)->GetWindowRect(rect); //得到显示区域
ScreenToClient(&rect);
m_pGrid->Create(rect,this,100);
m_pGrid->SetEditable(false);
m_pGrid->SetTextBkColor(RGB(0xFF, 0xFF, 0xE0)); //黄色背景
try
{
m_pGrid->SetRowCount(10); //初始为10行
m_pGrid->SetColumnCount(11); //初始化为11列
m_pGrid->SetFixedRowCount(1); //表头为一行
m_pGrid->SetFixedColumnCount(1); //表头为一列
}
catch (CMemoryException* e)
{
e->ReportError();
e->Delete();
// return FALSE;
}
for (int row = 0; row < m_pGrid->GetRowCount(); row++)
for (int col = 0; col < m_pGrid->GetColumnCount(); col++)
{
//设置表格显示属性
GV_ITEM Item;
Item.mask = GVIF_TEXT|GVIF_FORMAT;
Item.row = row;
Item.col = col;
if(row==0&&col==0) //第(0,0)格
{
Item.nFormat = DT_CENTER|DT_WORDBREAK;
Item.szText.Format(_T("报表显示"),col);
}
else if (row < 1) //设置0行表头显示
{
Item.nFormat = DT_CENTER|DT_WORDBREAK;
Item.szText.Format(_T(" 项目%d"),col);
}
else if (col < 1) //设置0列表头显示
{
if(row< m_pGrid->GetRowCount()-4)
{
Item.nFormat = DT_CENTER|DT_VCENTER|DT_SINGLELINE|DT_END_ELLIPSIS;
Item.szText.Format(_T("第%d次"),row);
}
}
else
{
Item.nFormat = DT_CENTER|DT_VCENTER|DT_SINGLELINE|DT_END_ELLIPSIS;
Item.szText.Format(_T(""),2);
}
m_pGrid->SetItem(&Item);
}
m_pGrid->Invalidate();
}
//--------------设置行列距------------------
for(int a=0;aGetRowCount();a++)
m_pGrid->SetRowHeight(a,16); //设置各行高
m_pGrid->SetColumnWidth(0,58); //设置0列宽
for(int b=1;bGetColumnCount();b++)
m_pGrid->SetColumnWidth(b,59); //设置各列宽
}
步骤二 嵌入上面的对话框 显示数据
在你需要显示数据的对话框上的头文件中,假设为CDlgTest,加入
#include "GridCtrl.h"
CDlgTestReportBox* m_pTestReportBox;
将数据显示对话框放入你的对话框相应位置上,在CDlgTest::OnInitDialog() 中:
if(!m_pTestReportBox)
{
m_pTestReportBox=new CDlgTestReportBox(this);
}
m_pTestReportBox->Create(IDD_DlgTestReportBox,this);
//定义区域变量
CRect rectDraw;
GetDlgItem(IDC_AeraReport)->GetWindowRect(rectDraw);
ScreenToClient(&rectDraw); //动态测试数据显示区域rectDraw
//将对应的对话框放到指定区域
m_pTestReportBox->MoveWindow(rectDraw);
m_pTestReportBox->ShowWindow(SW_SHOW);
自定义填充数据的函数:CDlgTest::FillGrid() 如下:
CGridCtrl* pGrid=m_pTestReportBox->m_pGrid;
for (int row = pGrid->GetRowCount()-1; row >= pGrid->GetRowCount()-3; row--)
{
for (int col = 1; col <= pGrid->GetColumnCount(); col++)
{
GV_ITEM Item;
Item.mask = GVIF_TEXT|GVIF_FORMAT;
Item.row = row;
Item.col = col;
if(row==pGrid->GetRowCount()-3&&col>0) //平均值
{
if(col==10){
Item.nFormat = DT_CENTER|DT_WORDBREAK;
Item.szText.Format(_T(" %6.2f "),avjch);
}
else{
Item.nFormat = DT_CENTER|DT_WORDBREAK;
Item.szText.Format(_T(" %6.2f "),av[col-1]);
}
}
pGrid->SetItem(&Item); //提交数据
if(row==0||col==0)
{
COLORREF clr = RGB(0, 0, 0);
pGrid->SetItemBkColour(row, col, clr);
pGrid->SetItemFgColour(row, col, RGB(255,0,0));
}
}//循环结束
pGrid->Invalidate();
}
好累啊,忙了一天时间终于写完了!
posted @
2006-06-24 19:50 Jerry Cat 阅读(5382) |
评论 (5) |
编辑 收藏
[转]深入 printf / wprintf / console下的unicode output
1. printf 只能提供ANSI/MB 的输出,不支持输出unicode stream.
例如:
wchar_t test[]
=
L
"
测试1234
"
;
printf(
"
%s
"
,test);
是不会正确输出的
2.wprintf 同样不会提供unicode output,
但是他会把wchar_t的string转为locale的SB/MB字符编码,然后输出
例如:
wchar_t test[]
=
L
"
测试Test
"
;
wprintf(L
"
%s
"
,test);
会输出??1234之类的字符串,或者不输出任何结果
因为wprintf没有办法把L"测试Test"转为默认的ANSI,需要设置locale
setlocale(LC_ALL,
"
chs
"
);
wchar_t test[]
=
L
"
测试Test
"
;
wprintf(L
"
%s
"
,test);
会有正确的输出
综上:
CRT I/O functions do not provide Unicode output.
3. Window console自从NT4就是一个真正的unicode console
不过输出unicode string,只有使用Windows API, WriteConsoleW
例如:
wchar_t test[]
=
L
"
测试1234
"
;
DWORD ws;
WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE),test,wcslen(test),
&
ws,NULL);
可以正确的输出而不需要设置locale,因为是真正的unicode的输出,跟codepage无关
4. 如何实现跨平台的console output
不要使用wchar_t和wprintf,因为这些都依赖于编译器.
ICU是IBM的一个成熟的跨平台支持unicode的libary,推荐使用
以下是ICU的uprintf实现
void
uprintf(
const
UnicodeString
&
str) {
char
*
buf
=
0
;
int32_t len
=
str.length();
int32_t bufLen
=
len
+
16
;
int32_t actualLen;
buf
=
new
char
[bufLen
+
1
];
actualLen
=
str.extract(
0
, len, buf
/*
, bufLen
*/
);
//
Default codepage conversion
buf[actualLen]
=
0
;
printf(
"
%s
"
, buf);
delete buf;
}
它也是先把Unicode string转化为本地的codepage,然后printf,虽然也不是unicode output,但是跨平台,大多数情况会工作得很好。
posted @
2006-06-22 02:11 Jerry Cat 阅读(441) |
评论 (0) |
编辑 收藏
/********************************************\
| 欢迎转载, 但请保留作者姓名和原文链接, 祝您进步并共勉! |
\********************************************/
读VC++内幕之体悟 - 04
作者: Jerry Cat
时间: 2006/06/17
链接: http://www.cppblog.com/jerysun0818/archive/2006/06/17/8669.html
4
. ActiveX控件最突出的特点是其属性和方法。属性有符号化的名字,这些名字对应着内部的整数索引。客户可通过一个整数索引存取相应的属性值。ActiveX控件的方法与函数很类似。
posted @
2006-06-17 19:48 Jerry Cat 阅读(517) |
评论 (0) |
编辑 收藏
[转]我的生财之道 - 计算机编程相关
本人长期高价代做计算机系作业、课程设计及毕业设计。作业100元,课程设计1000元,毕业设计5000元。明码标价童叟无欺,保证质量保证原创。上海交大(你们幸福着哩,沾陈进叫兽的光哦)学生可享受九折优惠; 湖南长沙国防科技大(你们狗日的真幸运,抄FreeBSD 5.3搞了个叫凄淋的伪操作系统说国防,军人的保家卫国全靠它哩)八五折优惠。技术交流及技术咨询不在上述之列。
posted @
2006-06-15 19:14 Jerry Cat 阅读(310) |
评论 (0) |
编辑 收藏
[转]c++中的引用新解
c++中的引用又称为别名.
1. 引用必须被初始化,即必须指向一个对象.
int nTemp = 10;
int &refTemp = nTemp;
2.初始化之后就不能再指向其他对象.如果用一个引用给另一个引用赋值,那么被改变的是被引用的对象而不是引用本身.
int nOther = 20;
refTemp = nOther; //此时nTemp中的值为20,但是&refTemp和&nTemp是相同的.
3.用const修饰的引用可以用不同类型的对象初始化(只要能从一种类型转换到另外一种类型),也可以是不可寻址的值.能够这样的原因是因为:编译器做了手脚.
double dTemp = 1024;
const int &refTemp = dTemp; //&dTemp和&refTemp是不同的.
编译之后成为:
double dTemp = 1024;
int nTemp = dTemp;
const int &refTemp = nTemp;
4.不允许非const引用指向需要临时对象的对象或值.
posted @
2006-06-15 07:02 Jerry Cat 阅读(310) |
评论 (0) |
编辑 收藏