﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-飘羽-文章分类-C++学习</title><link>http://www.cppblog.com/xmh79/category/420.html</link><description /><language>zh-cn</language><lastBuildDate>Sun, 06 Jul 2008 09:57:06 GMT</lastBuildDate><pubDate>Sun, 06 Jul 2008 09:57:06 GMT</pubDate><ttl>60</ttl><item><title>简单的VC串口调试开发(ZZ)</title><link>http://www.cppblog.com/xmh79/articles/11950.html</link><dc:creator>飘羽</dc:creator><author>飘羽</author><pubDate>Sat, 02 Sep 2006 02:13:00 GMT</pubDate><guid>http://www.cppblog.com/xmh79/articles/11950.html</guid><wfw:comment>http://www.cppblog.com/xmh79/comments/11950.html</wfw:comment><comments>http://www.cppblog.com/xmh79/articles/11950.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/xmh79/comments/commentRss/11950.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xmh79/services/trackbacks/11950.html</trackback:ping><description><![CDATA[
		<font color="#000000">        在众多网友的支持下，串口调试助手从2001年5月21日发布至今，短短一个月，在全国各地累计下载量近5000人次，在近200多个电子邮件中，20多人提供了使用测试意见，更有50多位朋友提出要串口调试助手的源代码，为了答谢谢朋友们的支持，公开推出我最初用VC控件MSComm编写串口通信程序的源代码，并写出详细的编程过程，姑且叫串口调试助手源程序V1.0或VC串口通讯源程序吧，我相信，如果你用VC编程，那么有了这个代码，就可以轻而易举地完成串口编程任务了。（也许本文过于详细，高手就不用看） </font>
		<p>
				<font color="#000000">开始吧： </font>
		</p>
		<p>
				<font color="#000000">1.建立项目：打开VC＋＋6.0，建立一个基于对话框的MFC应用程序SCommTest（与我源代码一致，等会你会方便一点）； </font>
		</p>
		<p>
				<font color="#000000">2.在项目中插入MSComm控件   选择Project菜单下Add To Project子菜单中的 Components and Controls…选项，在弹出的对话框中双击Registered ActiveX Controls项（稍等一会，这个过程较慢），则所有注册过的ActiveX控件出现在列表框中。 选择Microsoft Communications Control, version 6.0，，单击Insert按钮将它插入到我们的Project中来，接受缺省的选项。（如果你在控件列表中看不到Microsoft Communications Control, version 6.0，那可能是你在安装VC6时没有把ActiveX一项选上，重新安装VC6，选上ActiveX就可以了）， </font>
		</p>
		<p>
				<font color="#000000">这时在ClassView视窗中就可以看到CMSComm类了，（注意：此类在ClassWizard中看不到，重构clw文件也一样），并且在控件工具栏Controls中出现了电话图标（如图1所示），现在要做的是用鼠标将此图标拖到对话框中，程序运行后，这个图标是看不到的。 </font>
		</p>
		<p>
				<font color="#000000">
				</font> </p>
		<p>
				<font color="#000000">3.利用ClassWizard定义CMSComm类控制对象  打开ClassWizard－&gt;Member Viariables选项卡，选择CSCommTestDlg类，为IDC_MSCOMM1添加控制变量：m_ctrlComm，这时你可以看一看，在对话框头文件中自动加入了//{{AFX_INCLUDES()  #include "mscomm.h"  //}}AFX_INCLUDES （这时运行程序，如果有错，那就再从头开始）。 </font>
		</p>
		<p>
				<font color="#000000">4.在对话框中添加控件  向主对话框中添加两个编辑框，一个用于接收显示数据ID为IDC_EDIT_RXDATA，另一个用于输入发送数据，ID为IDC_EDIT_TXDATA，再添加一个按钮，功能是按一次就把发送编辑框中的内容发送一次，将其ID设为IDC_BUTTON_MANUALSEND。别忘记了将接收编辑框的Properties－&gt;Styles中把Miltiline和Vertical Scroll属性选上，发送编辑框若你想输入多行文字，也可选上Miltiline。 </font>
		</p>
		<p>
				<font color="#000000">再打开ClassWizard－&gt;Member Viariables选项卡，选择CSCommTestDlg类， 为IDC_EDIT_RXDATA添加CString变量m_strRXData， 为IDC_EDIT_TXDATA添加CString变量m_strTXData。说明： m_strRXData和m_strTXData分别用来放入接收和发送的字符数据。 </font>
		</p>
		<p>
				<font color="#000000">       休息一会吧？我们天天与电脑打交道，要注意保重，我现在在单杠上做引体向上可以来40次，可我都32了，佩服吗？。。。。。。好了，再接着来，下面是关键了： </font>
		</p>
		<p>
				<font color="#000000">5.添加串口事件消息处理函数OnComm() 打开ClassWizard－&gt;Message Maps，选择类CSCommTestDlg，选择IDC_MSCOMM1，双击消息OnComm，将弹出的对话框中将函数名改为OnComm，（好记而已）OK。 </font>
		</p>
		<p>
				<font color="#000000"> 这个函数是用来处理串口消息事件的，如每当串口接收到数据，就会产生一个串口接收数据缓冲区中有字符的消息事件，我们刚才添加的函数就会执行，我们在OnComm()函数加入相应的处理代码就能实现自已想要的功能了。请你在函数中加入如下代码： </font>
		</p>
		<p>
				<font color="#000000">void CSCommTestDlg::OnComm() <br />{<br />    // TODO: Add your control notification handler code here<br />    VARIANT variant_inp;<br />    COleSafeArray safearray_inp;<br />    LONG len,k;<br />    BYTE rxdata[2048]; //设置BYTE数组 An 8-bit integerthat is not signed.<br />    CString strtemp;<br />    if(m_ctrlComm.GetCommEvent()==2) //事件值为2表示接收缓冲区内有字符<br />    {             ////////以下你可以根据自己的通信协议加入处理代码<br />        variant_inp=m_ctrlComm.GetInput(); //读缓冲区<br />        safearray_inp=variant_inp; //VARIANT型变量转换为ColeSafeArray型变量<br />        len=safearray_inp.GetOneDimSize(); //得到有效数据长度<br />        for(k=0;k&lt;len;k++)<br />            safearray_inp.GetElement(&amp;k,rxdata+k);//转换为BYTE型数组<br />        for(k=0;k&lt;len;k++) //将数组转换为Cstring型变量<br />        {<br />            BYTE bt=*(char*)(rxdata+k); //字符型<br />            strtemp.Format("%c",bt); //将字符送入临时变量strtemp存放<br />            m_strRXData+=strtemp; //加入接收编辑框对应字符串 <br />        }<br />    }<br />    UpdateData(FALSE); //更新编辑框内容<br />} </font>
		</p>
		<p>
				<br />
				<font color="#000000">到目前为止还不能在接收编辑框中看到数据，因为我们还没有打开串口，但运行程序不应该有任何错误，不然，你肯定哪儿没看仔细，因为我是打开VC6对照着做一步写一行的，运行试试。没错吧？那么做下一步： </font>
		</p>
		<p>
				<font color="#000000">6.打开串口和设置串口参数  你可以在你需要的时候打开串口，例如在程序中做一个开始按钮，在该按钮的处理函数中打开串口。现在我们在主对话框的CSCommTestDlg::OnInitDialog()打开串口，加入如下代码： </font>
		</p>
		<p>
				<font color="#000000">// TODO: Add extra initialization here<br />if(m_ctrlComm.GetPortOpen())<br />m_ctrlComm.SetPortOpen(FALSE); </font>
		</p>
		<p>
				<font color="#000000">m_ctrlComm.SetCommPort(1); //选择com1<br />if( !m_ctrlComm.GetPortOpen())<br />m_ctrlComm.SetPortOpen(TRUE);//打开串口<br />else<br />AfxMessageBox("cannot open serial port"); </font>
		</p>
		<p>
				<font color="#000000">m_ctrlComm.SetSettings("9600,n,8,1"); //波特率9600，无校验，8个数据位，1个停止位 </font>
		</p>
		<p>
				<font color="#000000">m_ctrlComm.SetInputMode(1); //1：表示以二进制方式检取数据<br />m_ctrlComm.SetRThreshold(1); <br />//参数1表示每当串口接收缓冲区中有多于或等于1个字符时将引发一个接收数据的OnComm事件<br />m_ctrlComm.SetInputLen(0); //设置当前接收区数据长度为0<br />m_ctrlComm.GetInput();//先预读缓冲区以清除残留数据 </font>
		</p>
		<p>
				<br />
				<font color="#000000">现在你可以试试程序了，将串口线接好后（不会接？去看看我写的串口接线基本方法），打开串口调试助手，并将串口设在com2，选上自动发送，也可以等会手动发送。再执行你编写的程序，接收框里应该有数据显示了。 </font>
		</p>
		<p>
				<font color="#000000">7.发送数据  先为发送按钮添加一个单击消息即BN_CLICKED处理函数，打开ClassWizard－&gt;Message Maps，选择类CSCommTestDlg，选择IDC_BUTTON_MANUALSEND，双击BN_CLICKED添加OnButtonManualsend()函数，并在函数中添加如下代码： </font>
		</p>
		<p>
				<font color="#000000">void CSCommTestDlg::OnButtonManualsend() <br />{<br />// TODO: Add your control notification handler code here<br />UpdateData(TRUE); //读取编辑框内容<br />m_ctrlComm.SetOutput(COleVariant(m_strTXData));//发送数据<br />} </font>
		</p>
		<p>
				<font color="#000000">运行程序，在发送编辑框中随意输入点什么，单击发送按钮，啊！看看，在另一端的串口调试助手（或别的调试工具）接收框里出现了什么。 </font>
		</p>
		<p>
				<font color="#000000">如果你真是初次涉猎串口编程，又一次成功，那该说声谢谢我了，因为我第一次做串口程序时可费劲了，那时网上的资料也不好找。开开玩笑，谢谢你的支持，有什么好东西别忘了给我寄一份。 </font>
		</p>
		<p>
				<font color="#000000">最后说明一下，由于用到VC控件，在没有安装VC的计算机上运行时要从VC中把mscomm32.ocx、msvcrt.dll、mfc42.dll拷到Windows目录下的System子目录中（win2000为System32）并再进行注册设置，请参考 <br /></font>
				<a name="8.发送十六进制字符">
						<b>
								<font color="#000000" size="3">8.发送十六进制字符</font>
						</b>
				</a>
		</p>
		<p style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 3px" align="left">
				<font color="#000000" size="3">    在主对话框中加入一个复选接钮，ID为IDC_CHECK_HEXSEND Caption: 十六进制发送，再利用ClassWizard为其添加控制变量：m_ctrlHexSend；</font>
		</p>
		<p style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 3px" align="left">
				<font color="#000000" size="3">    在ClassView中为SCommTestDlg类添加以下两个PUBLIC成员函数，并输入相应代码;</font>
		</p>
		<p style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 3px" align="left">
				<font color="#000000" size="3">　</font>
		</p>
		<blockquote>
				<blockquote>
						<p style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 3px" align="left">
								<font color="#000000" size="3">//由于这个转换函数的格式限制，在发送框中的十六制字符应该每两个字符之间插入一个空隔<br />//如：A1 23 45 0B 00 29<br /></font>
								<font color="#000000" size="3">//CByteArray是一个动态字节数组，可参看MSDN帮助<br />int CSCommTestDlg::String2Hex(CString str, CByteArray &amp;senddata)<br />{<br />int hexdata,lowhexdata;<br />int hexdatalen=0;<br />int len=str.GetLength();<br />senddata.SetSize(len/2);<br />for(int i=0;i&lt;len;)<br />{<br />char lstr,hstr=str[i];<br />if(hstr==' ')<br />{<br />i++;<br />continue;<br />}<br />i++;<br />if(i&gt;=len)<br />break;<br />lstr=str[i];<br />hexdata=ConvertHexChar(hstr);<br />lowhexdata=ConvertHexChar(lstr);<br />if((hexdata==16)||(lowhexdata==16))<br />break;<br />else <br />hexdata=hexdata*16+lowhexdata;<br />i++;<br />senddata[hexdatalen]=(char)hexdata;<br />hexdatalen++;<br />}<br />senddata.SetSize(hexdatalen);<br />return hexdatalen;<br />}<br /><br /></font>
								<font color="#000000" size="3">//这是一个将字符转换为相应的十六进制值的函数<br />//好多C语言书上都可以找到<br />//功能：若是在0-F之间的字符，则转换为相应的十六进制字符，否则返回-1<br />char CSCommTestDlg::ConvertHexChar(char ch) <br />{<br />if((ch&gt;='0')&amp;&amp;(ch&lt;='9'))<br />return ch-0x30;<br />else if((ch&gt;='A')&amp;&amp;(ch&lt;='F'))<br />return ch-'A'+10;<br />else if((ch&gt;='a')&amp;&amp;(ch&lt;='f'))<br />return ch-'a'+10;<br />else return (-1);<br />}</font>
						</p>
						<p style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 3px" align="left">
								<font color="#000000" size="3">　</font>
						</p>
				</blockquote>
		</blockquote>
		<p style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 3px" align="left">
				<font color="#000000" size="3">  再将CSCommTestDlg::OnButtonManualsend()修改成以下形式：</font>
		</p>
		<blockquote>
				<p style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 3px" align="left">
						<font color="#000000" size="3">void CSCommTestDlg::OnButtonManualsend() <br />{<br />// TODO: Add your control notification handler code here<br />UpdateData(TRUE); //读取编辑框内容<br />if(m_ctrlHexSend.GetCheck())<br />{<br />CByteArray hexdata;<br />int len=String2Hex(m_strTXData,hexdata); //此处返回的len可以用于计算发送了多少个十六进制数<br />m_ctrlComm.SetOutput(COleVariant(hexdata)); //发送十六进制数据<br />}<br />else <br />m_ctrlComm.SetOutput(COleVariant(m_strTXData));//发送ASCII字符数据<br /><br />}<br /></font>
				</p>
		</blockquote>
		<p style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 3px" align="left">
				<font color="#000000" size="3">现在，你先将串口线接好并打开串口调试助手V2.1，选上以十六制显示，设置好相应串口，然后运行我们这个程序，在发送框中输入00 01 02 03 A1 CC等十六进制字符，并选上以十六进制发送，单击手动发送，在串口调试助手的接收框中应该可以看到00 01 02 03 A1 CC了。</font>
		</p>
		<p style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 3px" align="left">
				<font color="#000000" size="3">　</font>
		</p>
		<p style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 3px" align="left">
				<font color="#000000" size="3">
						<img height="65" src="http://www.stupai.com/tech/UploadPic/2006-7/2006714131055654.gif" width="56" border="0" />
				</font>
				<a name="9.在接收框中以十六进制显示">
						<b>
								<font color="#000000" size="3">9.在接收框中以十六进制显示</font>
						</b>
				</a>
		</p>
		<p style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 3px" align="left">
				<font color="#000000" size="3">    这就容易多了：  在主对话框中加入一个复选接钮，IDC_CHECK_HEXDISPLAY Caption: 十六进制显示，再利用ClassWizard为其添加控制变量：m_ctrlHexDiaplay。 然后修改CSCommTestDlg::OnComm()函数：</font>
		</p>
		<blockquote>
				<p style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 3px" align="left">
						<font color="#000000" size="3">void CSCommTestDlg::OnComm() <br />{<br />// TODO: Add your control notification handler code here<br />VARIANT variant_inp;<br />COleSafeArray safearray_inp;<br />LONG len,k;<br />BYTE rxdata[2048]; //设置BYTE数组 An 8-bit integerthat is not signed.<br />CString strtemp;<br />if(m_ctrlComm.GetCommEvent()==2) //事件值为2表示接收缓冲区内有字符<br />{<br />variant_inp=m_ctrlComm.GetInput(); //读缓冲区<br />safearray_inp=variant_inp; //VARIANT型变量转换为ColeSafeArray型变量<br />len=safearray_inp.GetOneDimSize(); //得到有效数据长度<br />for(k=0;k&lt;len;k++)<br />safearray_inp.GetElement(&amp;k,rxdata+k);//转换为BYTE型数组<br />for(k=0;k&lt;len;k++) //将数组转换为Cstring型变量<br />{<br />BYTE bt=*(char*)(rxdata+k); //字符型<br />if(m_ctrlHexDisplay.GetCheck())<br />strtemp.Format("%02X ",bt); //将字符以十六进制方式送入临时变量strtemp存放，注意这里加入一个空隔<br />else <br />strtemp.Format("%c",bt); //将字符送入临时变量strtemp存放<br /><br /></font>
						<font color="#000000" size="3">m_strRXData+=strtemp; //加入接收编辑框对应字符串 <br />}<br />}<br />UpdateData(FALSE); //更新编辑框内容<br />}</font>
				</p>
		</blockquote>
		<p style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 3px" align="left">
				<font color="#000000" size="3">测试：在串口调试助手发送框中输入00 01 02 03 A1 CC等十六进制字符，并选上以十六进制发送，单击手动发送，在本程序运行后选上以十六进制显示，在串口调试助手中单击手动发送或自动发送，则在本程序的接收框中应该可以看到00 01 02 03 A1 CC了。</font>
		</p>
		<p style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 3px" align="left">
				<font color="#000000" size="3">　</font>
		</p>
		<p style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 3px" align="left">
				<font color="#000000" size="3">
						<img height="65" src="http://www.stupai.com/tech/UploadPic/2006-7/2006714131055654.gif" width="56" border="0" />
				</font>
				<a name="10.如何设置自动发送">
						<b>
								<font color="#000000" size="3">10.如何设置自动发送</font>
						</b>
				</a>
		</p>
		<p style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 3px" align="left">
				<font color="#000000" size="3">     最简单的设定自动发送周期是用SetTimer()函数，这在数据采集中很有用，在控制中指令的传送也可能用到定时发送。</font>
		</p>
		<p style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 3px" align="left">
				<font color="#000000" size="3">    方法是：在ClassWizard中选上MessageMap卡，然后在Objects IDs选中CSCommTestDlg类，再在Messages框中选上WM_TIMER消息，单击ADD_FUNCTION加入void CSCommTestDlg::OnTimer(UINT nIDEvent) 函数，这个函数是放入“时间到”后要处理的代码：</font>
		</p>
		<blockquote>
				<p style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 3px" align="left">
						<font color="#000000" size="3">void CSCommTestDlg::OnTimer(UINT nIDEvent) <br />{<br />// TODO: Add your message handler code here and/or call default<br />OnButtonManualsend()；<br /></font>
						<font color="#000000" size="3">CDialog::OnTimer(nIDEvent);<br />}<br /></font>
				</p>
		</blockquote>
		<p style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 3px" align="left">
				<font color="#000000" size="3">再在在主对话框中加入一个复选接钮，ID为IDC_CHECK_AUTOSEND Caption: 自动发送（周期1秒），再利用ClassWizard为其添加BN_CLICK消息处理函数void CSCommTestDlg::OnCheckAutosend():</font>
		</p>
		<blockquote>
				<p style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 3px" align="left">
						<font color="#000000" size="3">void CSCommTestDlg::OnCheckAutosend() <br />{<br />// TODO: Add your control notification handler code here<br />m_bAutoSend=!m_bAutoSend;<br />if(m_bAutoSend)<br />{<br />SetTimer(1,1000,NULL);//时间为1000毫秒<br />}<br />else<br />{<br />KillTimer(1);  //取消定时<br />}<br />}</font>
				</p>
		</blockquote>
		<p style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 3px" align="left">
				<font color="#000000" size="3">其中：m_bAutoSend为BOOL型变量，在CLASSVIEW中为CSCommTestDlg类加入，并在构造函数中初始化：</font>
		</p>
		<p style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 3px" align="left">
				<font color="#000000" size="3">      m_bAutoSen=FALSE;<br />现在可以运行程序测试了。<br /></font>
		</p>
		<p style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 3px" align="left">
				<font color="#000000" size="3">　</font>
		</p>
		<p style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 3px" align="left">
				<font size="3">
						<font color="#000000">
								<img height="65" src="http://www.stupai.com/tech/UploadPic/2006-7/2006714131055654.gif" width="56" border="0" />
								<b>
										<a name="11.什么是VARIANT数据类型？如何使用VARIANT数据类型？">11.什么是VARIANT数据类型？如何使用VARIANT数据类型？</a>
								</b>
						</font>
				</font>
		</p>
		<p style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 3px" align="left">
				<font color="#000000" size="3">     不知如何使用VARIANT数据类型， 有不少朋友对VARIANT这个新的数据类型大感头疼。SetOutput()函数中 需要的VARIANT参数还可以使用COleVariant类的构造函数简单生成，现在GetInput()函数的返回值也成了VARIANT类型，那么如何从返回的值中提取有用的内容。 VARIANT及由之而派生出的COleVariant类主要用于在OLE自动化中传递数据。实际上VARIANT也只不过是一个新定义的结构罢了，它的主要成员包括一个联合体及一个变量。该联合体由各种类型的数据成员构成， 而该变量则用来指明联合体中目前起作用的数据类型。我们所关心的接收到的数据就存储在该联合体的某个数据成员中。 该联合体中包含的数据类型很多，从一些简单的变量到非常复杂的数组和指针。由于通过串口接收到的内容常常是一个字节串，我们将使用其中的某个数组或指针来访问接收到的数据。这里推荐给大家的是指向一个SAFEARRAY（COleSafeArray）类型变量。新的数据类型SAFEARRAY正如其名字一样，是一个“安全数组”，它能根据系统环境自动调整其16位或32 位的定义，并且不会被OLE改变（某些类型如BSTR在16位或32位应用程序间传递时会被OLE翻译从而破坏其中的二进制数据）。大家无须了解SAFEARRAY的具体定义，只要知道它是另外一个结构，其中包含一个 (void *)类型的指针pvData，其指向的内存就是存放有用数据的地方。 简而言之，从GetInput()函数返回的VARIANT类型变量中，找出parray 指针，再从该指针指向的SAFEARRAY变量中找出pvData指针，就可以向访问数组一样取得所接收到的数据了。具体应用请参见void CSCommTestDlg::OnComm()函数。</font>
		</p>
		<p style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 3px" align="left">
				<font color="#000000" size="3">    大概我现在也说不清这个问题，我自己从第一次接触这个东西，到现在还是给别人讲不清。</font>
		</p>
		<p style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 3px" align="left">
				<font color="#000000" size="3">　</font>
		</p>
		<p style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 3px" align="left">
				<font color="#000000" size="3">另：二进制收发设置请参考</font>
				<a style="COLOR: #006666; TEXT-DECORATION: none" href="http://www.gjwtech.com/scomm/scmscomm.htm">
						<font color="#000000" size="3">MSComm控件说明</font>
				</a>
				<font color="#000000" size="3">。</font>
		</p>
<img src ="http://www.cppblog.com/xmh79/aggbug/11950.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xmh79/" target="_blank">飘羽</a> 2006-09-02 10:13 <a href="http://www.cppblog.com/xmh79/articles/11950.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++的iostream标准库介绍</title><link>http://www.cppblog.com/xmh79/articles/1291.html</link><dc:creator>飘羽</dc:creator><author>飘羽</author><pubDate>Thu, 24 Nov 2005 08:23:00 GMT</pubDate><guid>http://www.cppblog.com/xmh79/articles/1291.html</guid><wfw:comment>http://www.cppblog.com/xmh79/comments/1291.html</wfw:comment><comments>http://www.cppblog.com/xmh79/articles/1291.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/xmh79/comments/commentRss/1291.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xmh79/services/trackbacks/1291.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我们从一开始就一直在利用C++的输入输出在做着各种练习，输入输出是由iostream库提供的，所以讨论此标准库是有必要的，它与C语言的stdio库不同，它从一开始就是用多重继承与虚拟继承实现的面向对象的层次结构，作为一个c++的标准库组件提供给程序员使用。 
<P>　　iostream为内置类型类型对象提供了输入输出支持，同时也支持文件的输入输出，类的设计者可以通过对iostream库的扩展，来支持自定义类型的输入输出操作。</P>
<P>　　为什么说要扩展才能提供支持呢？我们来一个示例。</P>
<P class=code clear=both>#include&nbsp;&lt;stdio.h&gt;&nbsp; <BR>#include&nbsp;&lt;<FONT color=maroon>iostream</FONT>&gt;&nbsp; <BR><FONT color=blue>using</FONT>&nbsp;<FONT color=blue>namespace</FONT>&nbsp;std;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp; <BR><FONT color=blue>class</FONT>&nbsp;Test&nbsp; <BR>{&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=blue>public</FONT>:&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Test(<FONT color=blue>int</FONT>&nbsp;a=0,<FONT color=blue>int</FONT>&nbsp;b=0)&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Test::a=a;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Test::b=b;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=blue>int</FONT>&nbsp;a;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=blue>int</FONT>&nbsp;b;&nbsp; <BR>};&nbsp; <BR><FONT color=blue>int</FONT>&nbsp;<FONT color=blue>main</FONT>()&nbsp; <BR>{&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;Test&nbsp;t(100,50);&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;printf("%???",t);<FONT color=green>//不明确的输出格式&nbsp;</FONT> <BR>&nbsp;&nbsp;&nbsp;&nbsp;scanf("%???",t);<FONT color=green>//不明确的输入格式&nbsp;</FONT> <BR>&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=maroon>cout</FONT>&lt;&lt;t&lt;&lt;endl;<FONT color=green>//同样不够明确&nbsp;</FONT> <BR>&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=maroon>cin</FONT>&gt;&gt;t;<FONT color=green>//同样不够明确&nbsp;</FONT> <BR>&nbsp;&nbsp;&nbsp;&nbsp;system("pause");&nbsp; <BR>}</P>
<P>　　由于自定义类的特殊性，在上面的代码中，无论你使用c风格的输入输出，或者是c++的输入输出都不是不明确的一个表示，由于c语言没有运算符重载机制，导致stdio库的不可扩充性，让我们无法让printf()和scanf()支持对自定义类对象的扩充识别，而c++是可以通过运算符重载机制扩充iostream库的，使系统能能够识别自定义类型，从而让输入输出明确的知道他们该干什么，格式是什么。 <BR><BR>　　在上例中我们之所以用printf与cout进行对比目的是为了告诉大家，C与C++处理输入输出的根本不同，我们从c远的输入输出可以很明显看出是函数调用方式，而c++的则是对象模式，<FONT color=#ff0000><B>cout和cin是ostream类和istream类的对象</B></FONT>。 <BR><BR>　　C++中的iostream库主要包含下图所示的几个头文件:</P>
<P align=center><IMG alt="" src="http://www.pconline.com.cn/pcedu/empolder/gj/c/0504/pic/05cppios01.gif" border=0></P>
<P>　　我们所熟悉的输入输出操作分别是由istream(输入流)和ostream(输出流)这两个类提供的，为了允许双向的输入／输出，由istream和ostream派生出了iostream类。 <BR><BR>　　类的继承关系见下图：</P>
<P align=center><IMG alt="" src="http://www.pconline.com.cn/pcedu/empolder/gj/c/0504/pic/05cppios02.gif" border=0><BR></P>
<P align=left><STRONG>iostream库定义了以下三个标准流对象：</STRONG> <BR><BR>　　１.cin，表示标准输入(standard input)的istream类对象。cin使我们可以从设备读如数据。 <BR>　　２.cout，表示标准输出(standard output)的ostream类对象。cout使我们可以向设备输出或者写数据。 <BR>　　３.cerr，表示标准错误(standard error)的osttream类对象。cerr是导出程序错误消息的地方，它只能允许向屏幕设备写数据。 <BR><BR>　　输出主要由重载的左移操作符（&lt;&lt;）来完成，输入主要由重载的右移操作符(&gt;&gt;)完成。 <BR><BR>　　&gt;&gt;a表示将数据放入a对象中。 <BR>　　&lt;&lt;a表示将a对象中存储的数据拿出。 <BR><BR>　　这些标准的流对象都有默认的所对应的设备，见下表：</P>
<P align=center><IMG alt="" src="http://www.pconline.com.cn/pcedu/empolder/gj/c/0504/pic/05cppios03.gif" border=0></P>
<P align=left>　　图中的意思表明cin对象的默认输入设备是键盘，cout对象的默认输出设备是显示器屏幕。 <BR><BR>　　那么原理上Ｃ++有是如何利用cin／cout对象与左移和右移运算符重载来实现输入输出的呢？ <BR><BR>　　下面我们以输出为例，说明其<B><FONT color=#ff0000>实现原理</FONT></B>： <BR><BR>　　cout是ostream类的对象，因为它所指向的是标准设备（显示器屏幕），所以它在iostream头文件中作为全局对象进行定义。 <BR><BR>　　ostream cout(stdout);//其默认指向的C中的标准设备名，作为其构造函数的参数使用。 <BR><BR>　　在iostream.h头文件中，ostream类对应每个基本数据类型都有其友元函数对左移操作符进行了友元函数的重载。 <BR>　　ostream&amp; operator&lt;&lt;(ostream &amp;temp,int source); <BR>　　ostream&amp; operator&lt;&lt;(ostream &amp;temp,char *ps); <BR>　　。。。。等等 <BR><BR>　　一句输出语句：cout&lt;&lt;"www.cndev-lab.com"；，事实上调用的就是ostream&amp; operator&lt;&lt;(ostream &amp;temp,char *ps);这个运算符重载函数，由于返回的是流对象的引用，引用可以作为左值使用，所以当程序中有类似cout&lt;&lt;"www.cndev-lab.com"&lt;&lt;"中国软件开发实验室";这样的语句出现的时候，就能够构成连续输出。</P>
<P align=left>　　由于iostream库不光支持对象的输入输出，同时也支持文件流的输入输出，所以在详细讲解左移与右移运算符重载只前，我们有必要先对文件的输入输出以及输入输出的控制符有所了解。 <BR><BR>　　和文件有关系的输入输出类主要在fstream.h这个头文件中被定义，在这个头文件中主要被定义了三个类，由这三个类控制对文件的各种输入输出操作，他们分别是ifstream、ofstream、fstream，其中fstream类是由iostream类派生而来，他们之间的继承关系见下图所示。</P>
<P align=left><IMG alt="" src="http://www.pconline.com.cn/pcedu/empolder/gj/c/0504/pic/05cppios04.gif" border=0></P>
<P align=left>　　由于文件设备并不像显示器屏幕与键盘那样是标准默认设备，所以它在fstream.h头文件中是没有像cout那样预先定义的全局对象，所以我们必须自己定义一个该类的对象，我们要以文件作为设备向文件输出信息(也就是向文件写数据)，那么就应该使用ofstream类。 <BR><BR>　　ofstream类的默认构造函数原形为： <BR><BR>　　ofstream::ofstream(const char *filename,int mode = ios::out,int openprot = filebuf::openprot); <BR><BR>　　filename：　　要打开的文件名 <BR>　　mode：　　　　要打开文件的方式 <BR>　　prot：　　　　打开文件的属性 <BR><BR>　　其中mode和openprot这两个参数的可选项表见下表： <BR><BR><B>mode属性表</B> <BR><BR>　　ios::app：　　　以追加的方式打开文件 <BR>　　ios::ate：　　　文件打开后定位到文件尾，ios:app就包含有此属性 <BR>　　ios::binary： 　以二进制方式打开文件，缺省的方式是文本方式。两种方式的区别见前文 <BR>　　ios::in：　　　 文件以输入方式打开 <BR>　　ios::out：　　　文件以输出方式打开 <BR>　　ios::trunc：　　如果文件存在，把文件长度设为0 <BR><BR>　　可以用“或”把以上属性连接起来，如ios::out|ios::binary。 <BR><BR><B>openprot属性表:</B> <BR><BR>　　0：普通文件，打开访问 <BR>　　1：只读文件 <BR>　　2：隐含文件 <BR>　　4：系统文件 <BR><BR>　　可以用“或”或者“+”把以上属性连接起来 ，如3或1|2就是以只读和隐含属性打开文件。<BR>示例代码如下： </P>
<P></P>
<P class=code align=left><FONT color=#008000>//程序作者:管宁&nbsp;</FONT> <BR><FONT color=green>//站点:www.cndev-lab.com&nbsp;</FONT> <BR><FONT color=green>//所有稿件均有版权,如要转载,请务必著名出处和作者&nbsp;</FONT> <BR>&nbsp; <BR>#include&nbsp;&lt;<FONT color=maroon>fstream</FONT>&gt;&nbsp; <BR><FONT color=blue>using</FONT>&nbsp;<FONT color=blue>namespace</FONT>&nbsp;std;&nbsp; <BR>&nbsp; <BR><FONT color=blue>int</FONT>&nbsp;<FONT color=blue>main</FONT>()&nbsp;&nbsp; <BR>{&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;ofstream&nbsp;myfile("c:\\1.txt",<FONT color=maroon>ios</FONT>::out|<FONT color=maroon>ios</FONT>::trunc,0);&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;myfile&lt;&lt;"中国软件开发实验室"&lt;&lt;endl&lt;&lt;"网址："&lt;&lt;"www.cndev-lab.com";&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;myfile.close()&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;system("pause");&nbsp; <BR>}</P>
<P align=left>　　文件使用完后可以使用close成员函数关闭文件。 <BR><BR>　　ios::app为追加模式，在使用追加模式的时候同时进行文件状态的判断是一个比较好的习惯。 <BR><BR>　　示例如下：</P>
<P class=code align=left><FONT color=#008000>//程序作者:管宁&nbsp;</FONT> <BR><FONT color=green>//站点:www.cndev-lab.com&nbsp;</FONT> <BR><FONT color=green>//所有稿件均有版权,如要转载,请务必著名出处和作者&nbsp;</FONT> <BR>#include&nbsp;&lt;<FONT color=maroon>iostream</FONT>&gt;&nbsp; <BR>#include&nbsp;&lt;<FONT color=maroon>fstream</FONT>&gt;&nbsp; <BR><FONT color=blue>using</FONT>&nbsp;<FONT color=blue>namespace</FONT>&nbsp;std;&nbsp; <BR><FONT color=blue>int</FONT>&nbsp;<FONT color=blue>main</FONT>()&nbsp;&nbsp; <BR>{&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;ofstream&nbsp;myfile("c:\\1.txt",<FONT color=maroon>ios</FONT>::app,0);&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=blue>if</FONT>(!myfile)<FONT color=green>//或者写成myfile.fail()&nbsp;</FONT> <BR>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=maroon>cout</FONT>&lt;&lt;"文件打开失败，目标文件状态可能为只读！";&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;system("pause");&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exit(1);&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;myfile&lt;&lt;"中国软件开发实验室"&lt;&lt;endl&lt;&lt;"网址："&lt;&lt;"www.cndev-lab.com"&lt;&lt;endl;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;myfile.close();&nbsp; <BR>}</P>
<P align=left>　　在定义ifstream和ofstream类对象的时候，我们也可以不指定文件。以后可以通过成员函数open()显式的把一个文件连接到一个类对象上。 <BR><BR>　　例如：</P>
<P class=code align=left><FONT color=#008000>//程序作者:管宁&nbsp;</FONT> <BR><FONT color=green>//站点:www.cndev-lab.com&nbsp;</FONT> <BR><FONT color=green>//所有稿件均有版权,如要转载,请务必著名出处和作者&nbsp;</FONT> <BR>#include&nbsp;&lt;<FONT color=maroon>iostream</FONT>&gt;&nbsp; <BR>#include&nbsp;&lt;<FONT color=maroon>fstream</FONT>&gt;&nbsp; <BR><FONT color=blue>using</FONT>&nbsp;<FONT color=blue>namespace</FONT>&nbsp;std;&nbsp; <BR><FONT color=blue>int</FONT>&nbsp;<FONT color=blue>main</FONT>()&nbsp;&nbsp; <BR>{&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;ofstream&nbsp;myfile;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;myfile.open("c:\\1.txt",<FONT color=maroon>ios</FONT>::out|<FONT color=maroon>ios</FONT>::app,0);&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=blue>if</FONT>(!myfile)<FONT color=green>//或者写成myfile.fail()&nbsp;</FONT> <BR>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=maroon>cout</FONT>&lt;&lt;"文件创建失败,磁盘不可写或者文件为只读!";&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;system("pause");&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exit(1);&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;myfile&lt;&lt;"中国软件开发实验室"&lt;&lt;endl&lt;&lt;"网址："&lt;&lt;"www.cndev-lab.com"&lt;&lt;endl;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;myfile.close();&nbsp; <BR>}</P>
<P align=left>　　下面我们来看一下是如何利用ifstream类对象，将文件中的数据读取出来，然后再输出到标准设备中的例子。 <BR><BR>　　代码如下：</P>
<P class=code align=left><FONT color=#008000>//程序作者:管宁&nbsp;</FONT> <BR><FONT color=green>//站点:www.cndev-lab.com&nbsp;</FONT> <BR><FONT color=green>//所有稿件均有版权,如要转载,请务必著名出处和作者&nbsp;</FONT> <BR>#include&nbsp;&lt;<FONT color=maroon>iostream</FONT>&gt;&nbsp; <BR>#include&nbsp;&lt;<FONT color=maroon>fstream</FONT>&gt;&nbsp; <BR>#include&nbsp;&lt;<FONT color=maroon>string</FONT>&gt;&nbsp; <BR><FONT color=blue>using</FONT>&nbsp;<FONT color=blue>namespace</FONT>&nbsp;std;&nbsp; <BR><FONT color=blue>int</FONT>&nbsp;<FONT color=blue>main</FONT>()&nbsp;&nbsp; <BR>{&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;ifstream&nbsp;myfile;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;myfile.open("c:\\1.txt",<FONT color=maroon>ios</FONT>::in,0);&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=blue>if</FONT>(!myfile)&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=maroon>cout</FONT>&lt;&lt;"文件读错误";&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;system("pause");&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exit(1);&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=blue>char</FONT>&nbsp;ch;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=maroon>string</FONT>&nbsp;content;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=blue>while</FONT>(myfile.get(ch))&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;content+=ch;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=maroon>cout</FONT>.put(ch);<FONT color=green>//cout&lt;&lt;ch;这么写也是可以的&nbsp;</FONT> <BR>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;myfile.close();&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=maroon>cout</FONT>&lt;&lt;content;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;system("pause");&nbsp; <BR>}</P>
<P align=left>　　上例中，我们利用成员函数get()，逐一的读取文件中的有效字符，再利用put()成员函数，将文件中的数据通过循环逐一输出到标准设备(屏幕)上，get()成员函数会在文件读到默尾的时候返回假值，所以我们可以利用它的这个特性作为while循环的终止条件，我们同时也在上例中引入了C++风格的字符串类型string，在循环读取的时候逐一保存到content中，要使用string类型，必须包含string.h的头文件。<BR>我们在简单介绍过ofstream类和ifstream类后，我们再来看一下fstream类，fstream类是由iostream派生而来，fstream类对象可以同对文件进行读写操作。</P>
<P align=left>　　示例代码如下：</P>
<P class=code align=left><FONT color=#008000>//程序作者:管宁&nbsp;</FONT> <BR><FONT color=green>//站点:www.cndev-lab.com&nbsp;</FONT> <BR><FONT color=green>//所有稿件均有版权,如要转载,请务必著名出处和作者&nbsp;</FONT> <BR>#include&nbsp;&lt;<FONT color=maroon>iostream</FONT>&gt;&nbsp; <BR>#include&nbsp;&lt;<FONT color=maroon>fstream</FONT>&gt;&nbsp; <BR><FONT color=blue>using</FONT>&nbsp;<FONT color=blue>namespace</FONT>&nbsp;std;&nbsp; <BR><FONT color=blue>int</FONT>&nbsp;<FONT color=blue>main</FONT>()&nbsp;&nbsp; <BR>{&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=maroon>fstream</FONT>&nbsp;myfile;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;myfile.open("c:\\1.txt",<FONT color=maroon>ios</FONT>::out|<FONT color=maroon>ios</FONT>::app,0);&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=blue>if</FONT>(!myfile)&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=maroon>cout</FONT>&lt;&lt;"文件写错误,文件属性可能为只读!"&lt;&lt;endl;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;system("pause");&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exit(1);&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;myfile&lt;&lt;"中国软件开发实验室"&lt;&lt;endl&lt;&lt;"网址："&lt;&lt;"www.cndev-lab.com"&lt;&lt;endl;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;myfile.close();&nbsp; <BR>&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;myfile.open("c:\\1.txt",<FONT color=maroon>ios</FONT>::in,0);&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=blue>if</FONT>(!myfile)&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=maroon>cout</FONT>&lt;&lt;"文件读错误,文件可能丢失!"&lt;&lt;endl;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;system("pause");&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exit(1);&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=blue>char</FONT>&nbsp;ch;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=blue>while</FONT>(myfile.get(ch))&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=maroon>cout</FONT>.put(ch);&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;myfile.close();&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;system("pause");&nbsp; <BR>}</P>
<P align=left>　　<FONT style="BACKGROUND-COLOR: #b000b0" color=#ffffff>由于fstream类可以对文件同时进行读写操作，所以对它的对象进行初始话的时候一定要显式的指定mode和openprot参数。</FONT></P>
<P align=left>　　接下来我们来学习一下串流类的基础知识，什么叫<B><FONT color=#ff0000>串流类</FONT></B>？ <BR>　　简单的理解就是能够控制字符串类型对象进行输入输出的类，C++不光可以支持C++风格的字符串流控制，还可以支持C风格的字符串流控制。 <BR><BR>　　我们先看看看C++是如何对C风格的字符串流进行控制的，C中的字符串其实也就是字符数组，字符数组内的数据在内存中的位置的排列是连续的，我们通常用char str[size]或者char *str的方式声明创建C风格字符数组，为了能让字符数组作为设备并提供输入输出操作，C++引入了ostrstream、istrstream、strstream这三个类，要使用他们创建对象就必须包含strstream.h头文件。 <BR>　　istrstream类用于执行C风格的串流的输入操作，也就是以字符串数组作为输入设备。 <BR>　　ostrstream类用于执行C风格的串流的输出操作，也就是一字符串数组作为输出设备。 <BR>　　strstream类同时可以支持C风格的串流的输入输出操作。 <BR><BR>　　istrstream类是从istream（输入流类）和strstreambase（字符串流基类）派生而来，ostrstream是从ostream（输出流类）和strstreambase（字符串流基类）派生而来，strstream则是从iostream(输入输出流类)和和strstreambase（字符串流基类）派生而来。 <BR><BR>　　他们的继承关系如下图所示:</P>
<P align=center><IMG alt="" src="http://www.pconline.com.cn/pcedu/empolder/gj/c/0504/pic/05cppios05.gif" border=0></P>
<P align=left>　　串流同样不是标准设备，不会有预先定义好的全局对象，所以不能直接操作，需要通过构造函数创建对象。 <BR><BR><STRONG>类istrstream的构造函数原形如下：</STRONG> <BR>　　istrstream::istrstream(const char *str,int size); <BR>　　参数1表示字符串数组,而参数2表示数组大小，当size为0时，表示istrstream类对象直接连接到由str所指向的内存空间并以\0结尾的字符串。 <BR><BR>　　下面的示例代码就是利用istrstream类创建类对象，制定流输入设备为字符串数组，通过它向一个字符型对象输入数据。 <BR><BR>　　代码如下： </P>
<P></P>
<P class=code align=left><FONT color=#008000>//程序作者:管宁&nbsp;</FONT> <BR><FONT color=green>//站点:www.cndev-lab.com&nbsp;</FONT> <BR><FONT color=green>//所有稿件均有版权,如要转载,请务必著名出处和作者&nbsp;</FONT> <BR>#include&nbsp;&lt;<FONT color=maroon>iostream</FONT>&gt;&nbsp; <BR>#include&nbsp;&lt;<FONT color=maroon>strstream</FONT>&gt;&nbsp; <BR><FONT color=blue>using</FONT>&nbsp;<FONT color=blue>namespace</FONT>&nbsp;std;&nbsp; <BR><FONT color=blue>int</FONT>&nbsp;<FONT color=blue>main</FONT>()&nbsp;&nbsp; <BR>{&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=blue>char</FONT>&nbsp;*name&nbsp;<FONT color=red>=</FONT>&nbsp;"www.cndev-lab.com";&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=blue>int</FONT>&nbsp;arraysize&nbsp;<FONT color=red>=</FONT>&nbsp;strlen(name)+1;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;istrstream&nbsp;is(name,arraysize);&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=blue>char</FONT>&nbsp;temp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;is&gt;&gt;temp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=maroon>cout</FONT>&lt;&lt;temp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;system("pause");&nbsp; <BR>}</P>
<P align=left>　　类ostrstream用于执行串流的输出，它的构造函数如下所示： <BR><BR>　　ostrstream::ostrstream(char *_Ptr,int streamsize,int Mode = ios::out); <BR><BR>　　第一个参数是字符数组，第二个是说明数组的大小，第三个参数是指打开方式。 <BR><BR>　　我们来一个示例代码：</P>
<P class=code align=left>#include&nbsp;&lt;<FONT color=maroon>iostream</FONT>&gt;&nbsp; <BR>#include&nbsp;&lt;<FONT color=maroon>strstream</FONT>&gt;&nbsp; <BR><FONT color=blue>using</FONT>&nbsp;<FONT color=blue>namespace</FONT>&nbsp;std;&nbsp; <BR><FONT color=blue>int</FONT>&nbsp;<FONT color=blue>main</FONT>()&nbsp;&nbsp; <BR>{&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=blue>int</FONT>&nbsp;arraysize=1;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=blue>char</FONT>&nbsp;*pbuffer=<FONT color=blue>new</FONT>&nbsp;<FONT color=blue>char</FONT>[arraysize];&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;ostrstream&nbsp;ostr(pbuffer,arraysize,<FONT color=maroon>ios</FONT>::out);&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;ostr&lt;&lt;arraysize&lt;&lt;ends;<FONT color=green>//使用ostrstream输出到流对象的时候,要用ends结束字符串&nbsp;</FONT> <BR>&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=maroon>cout</FONT>&lt;&lt;pbuffer;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=blue>delete</FONT>[]&nbsp;pbuffer;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;system("pause");&nbsp; <BR>}</P>
<P align=left>　　上面的代码中，我们创建一个c风格的串流输出对象ostr，我们将arraysize内的数据成功的以字符串的形式输出到了ostr对象所指向的pbuffer指针的堆空间中，pbuffer也正是我们要输出的字符串数组，在结尾要使用<B><FONT color=#ff0000>ends</FONT></B>结束字符串，如果不这么做就有溢出的危险。</P><img src ="http://www.cppblog.com/xmh79/aggbug/1291.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xmh79/" target="_blank">飘羽</a> 2005-11-24 16:23 <a href="http://www.cppblog.com/xmh79/articles/1291.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用C++实现简单的文件I/O操作</title><link>http://www.cppblog.com/xmh79/articles/1289.html</link><dc:creator>飘羽</dc:creator><author>飘羽</author><pubDate>Thu, 24 Nov 2005 06:50:00 GMT</pubDate><guid>http://www.cppblog.com/xmh79/articles/1289.html</guid><wfw:comment>http://www.cppblog.com/xmh79/comments/1289.html</wfw:comment><comments>http://www.cppblog.com/xmh79/articles/1289.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/xmh79/comments/commentRss/1289.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xmh79/services/trackbacks/1289.html</trackback:ping><description><![CDATA[<P>&nbsp;&nbsp;&nbsp; 文件 I/O 在C++中比烤蛋糕简单多了。 在这篇文章里，我会详细解释ASCII和二进制文件的输入输出的每个细节，值得注意的是，所有这些都是用C++完成的。<BR><BR>　　一、ASCII 输出<BR><BR>　　为了使用下面的方法, 你必须包含头文件&lt;fstream.h&gt;(译者注：在标准C++中，已经使用&lt;fstream&gt;取代&lt; fstream.h&gt;，所有的C++标准头文件都是无后缀的。)。这是 &lt;iostream.h&gt;的一个扩展集, 提供有缓冲的文件输入输出操作. 事实上, &lt;iostream.h&gt; 已经被&lt;fstream.h&gt;包含了, 所以你不必包含所有这两个文件, 如果你想显式包含他们，那随便你。我们从文件操作类的设计开始, 我会讲解如何进行ASCII I/O操作。如果你猜是"fstream," 恭喜你答对了！ 但这篇文章介绍的方法,我们分别使用"ifstream"?和 "ofstream" 来作输入输出。<BR><BR>　　如果你用过标准控制台流"cin"?和 "cout," 那现在的事情对你来说很简单。 我们现在开始讲输出部分，首先声明一个类对象。ofstream fout;<BR><BR>　　这就可以了，不过你要打开一个文件的话, 必须像这样调用ofstream::open()。<BR><BR>fout.open("output.txt");<BR><BR>　　你也可以把文件名作为构造参数来打开一个文件.<BR><BR>ofstream fout("output.txt");<BR><BR>　　这是我们使用的方法, 因为这样创建和打开一个文件看起来更简单. 顺便说一句, 如果你要打开的文件不存在，它会为你创建一个, 所以不用担心文件创建的问题. 现在就输出到文件，看起来和"cout"的操作很像。 对不了解控制台输出"cout"的人, 这里有个例子。<BR><BR>int num = 150;<BR>char name[] = "John Doe";<BR>fout &lt;&lt; "Here is a number: " &lt;&lt; num &lt;&lt; "\n";<BR>fout &lt;&lt; "Now here is a string: " &lt;&lt; name &lt;&lt; "\n";<BR><BR>　　现在保存文件，你必须关闭文件，或者回写文件缓冲. 文件关闭之后就不能再操作了, 所以只有在你不再操作这个文件的时候才调用它，它会自动保存文件。 回写缓冲区会在保持文件打开的情况下保存文件, 所以只要有必要就使用它。回写看起来像另一次输出, 然后调用方法关闭。像这样：<BR><BR>fout &lt;&lt; flush; fout.close();<BR><BR>　　 现在你用文本编辑器打开文件，内容看起来是这样：<BR><BR>　　Here is a number: 150 Now here is a string: John Doe<BR><BR>　　很简单吧! 现在继续文件输入, 需要一点技巧, 所以先确认你已经明白了流操作，对 "&lt;&lt;" 和"&gt;&gt;" 比较熟悉了, 因为你接下来还要用到他们。继续…<BR><BR>　　二、ASCII 输入<BR><BR>　　输入和"cin" 流很像. 和刚刚讨论的输出流很像, 但你要考虑几件事情。在我们开始复杂的内容之前, 先看一个文本：<BR><BR>　　12 GameDev 15.45 L This is really awesome!<BR><BR>　　为了打开这个文件，你必须创建一个in-stream对象,?像这样。<BR><BR>ifstream fin("input.txt");<BR><BR>　　现在读入前四行. 你还记得怎么用"&lt;&lt;" 操作符往流里插入变量和符号吧？好,?在 "&lt;&lt;" (插入)?操作符之后，是"&gt;&gt;" (提取) 操作符. 使用方法是一样的. 看这个代码片段.<BR><BR>int number;<BR>float real;<BR>char letter, word[8];<BR>fin &gt;&gt; number; fin &gt;&gt; word; fin &gt;&gt; real; fin &gt;&gt; letter;<BR><BR>　　也可以把这四行读取文件的代码写为更简单的一行。<BR><BR>fin &gt;&gt; number &gt;&gt; word &gt;&gt; real &gt;&gt; letter;<BR><BR>　　它是如何运作的呢? 文件的每个空白之后, "&gt;&gt;" 操作符会停止读取内容, 直到遇到另一个&gt;&gt;操作符. 因为我们读取的每一行都被换行符分割开(是空白字符), "&gt;&gt;" 操作符只把这一行的内容读入变量。这就是这个代码也能正常工作的原因。但是，可别忘了文件的最后一行。<BR><BR>　　This is really awesome!<BR><BR>　　如果你想把整行读入一个char数组, 我们没办法用"&gt;&gt;"?操作符，因为每个单词之间的空格（空白字符）会中止文件的读取。为了验证：<BR><BR>char sentence[101]; fin &gt;&gt; sentence;<BR><BR>　　我们想包含整个句子, "This is really awesome!" 但是因为空白, 现在它只包含了"This". 很明显, 肯定有读取整行的方法, 它就是getline()。这就是我们要做的。<BR><BR>fin.getline(sentence, 100);<BR><BR>　　这是函数参数. 第一个参数显然是用来接受的char数组. 第二个参数是在遇到换行符之前，数组允许接受的最大元素数量. 现在我们得到了想要的结果：“This is really awesome!”。<BR><BR>　　你应该已经知道如何读取和写入ASCII文件了。但我们还不能罢休，因为二进制文件还在等着我们。<BR><BR>　　三、二进制 输入输出<BR><BR>　　二进制文件会复杂一点, 但还是很简单的。首先你要注意我们不再使用插入和提取操作符(译者注：&lt;&lt; 和 &gt;&gt; 操作符). 你可以这么做，但它不会用二进制方式读写。你必须使用read() 和write() 方法读取和写入二进制文件. 创建一个二进制文件, 看下一行。<BR><BR>ofstream fout("file.dat", ios::binary);<BR><BR>　　这会以二进制方式打开文件, 而不是默认的ASCII模式。首先从写入文件开始。函数write() 有两个参数。 第一个是指向对象的char类型的指针, 第二个是对象的大小（译者注：字节数）。 为了说明，看例子。<BR><BR>int number = 30; fout.write((char *)(&amp;number), sizeof(number));<BR><BR>　　第一个参数写做"(char *)(&amp;number)". 这是把一个整型变量转为char *指针。如果你不理解，可以立刻翻阅C++的书籍，如果有必要的话。第二个参数写作"sizeof(number)". sizeof() 返回对象大小的字节数. 就是这样!<BR><BR>　　二进制文件最好的地方是可以在一行把一个结构写入文件。 如果说，你的结构有12个不同的成员。 用ASCII?文件，你不得不每次一条的写入所有成员。 但二进制文件替你做好了。 看这个。<BR><BR>struct OBJECT { int number; char letter; } obj;<BR>obj.number = 15;<BR>obj.letter = ‘M’;<BR>fout.write((char *)(&amp;obj), sizeof(obj));<BR><BR>　　这样就写入了整个结构! 接下来是输入. 输入也很简单，因为read()?函数的参数和 write()是完全一样的, 使用方法也相同。<BR><BR>ifstream fin("file.dat", ios::binary); fin.read((char *)(&amp;obj), sizeof(obj));<BR><BR>　　我不多解释用法, 因为它和write()是完全相同的。二进制文件比ASCII文件简单, 但有个缺点是无法用文本编辑器编辑。 接着, 我解释一下ifstream 和ofstream 对象的其他一些方法作为结束.<BR><BR>　　四、更多方法<BR><BR>　　我已经解释了ASCII文件和二进制文件, 这里是一些没有提及的底层方法。<BR><BR>　　检查文件<BR><BR>　　你已经学会了open() 和close() 方法, 不过这里还有其它你可能用到的方法。<BR><BR>　　方法good() 返回一个布尔值，表示文件打开是否正确。<BR><BR>　　类似的，bad() 返回一个布尔值表示文件打开是否错误。 如果出错，就不要继续进一步的操作了。<BR><BR>　　最后一个检查的方法是fail(), 和bad()有点相似, 但没那么严重。<BR><BR>　　读文件<BR><BR>　　方法get() 每次返回一个字符。<BR><BR>　　方法ignore(int,char) 跳过一定数量的某个字符, 但你必须传给它两个参数。第一个是需要跳过的字符数。 第二个是一个字符, 当遇到的时候就会停止。 例子,<BR><BR>fin.ignore(100, ‘\n’);<BR><BR>　　会跳过100个字符，或者不足100的时候，跳过所有之前的字符，包括 ‘\n’。<BR><BR>　　方法peek() 返回文件中的下一个字符, 但并不实际读取它。所以如果你用peek() 查看下一个字符, 用get() 在peek()之后读取，会得到同一个字符, 然后移动文件计数器。<BR><BR>　　方法putback(char) 输入字符, 一次一个, 到流中。我没有见到过它的使用，但这个函数确实存在。<BR><BR>　　写文件<BR><BR>　　只有一个你可能会关注的方法.?那就是 put(char), 它每次向输出流中写入一个字符。<BR><BR>　　打开文件<BR><BR>　　当我们用这样的语法打开二进制文件:<BR><BR>ofstream fout("file.dat", ios::binary);<BR><BR>　　"ios::binary"是你提供的打开选项的额外标志. 默认的, 文件以ASCII方式打开, 不存在则创建, 存在就覆盖. 这里有些额外的标志用来改变选项。<BR><BR>　　ios::app 添加到文件尾<BR>　　ios::ate 把文件标志放在末尾而非起始。<BR>　　ios::trunc 默认. 截断并覆写文件。<BR>　　ios::nocreate 文件不存在也不创建。<BR>　　ios::noreplace 文件存在则失败。<BR><BR>　　文件状态<BR><BR>　　我用过的唯一一个状态函数是eof(), 它返回是否标志已经到了文件末尾。 我主要用在循环中。 例如, 这个代码断统计小写‘e’ 在文件中出现的次数。<BR><BR>ifstream fin("file.txt");<BR>char ch; int counter;<BR>while (!fin.eof()) {<BR>ch = fin.get();<BR>if (ch == ‘e’) counter++;<BR>}<BR>fin.close();<BR><BR>　　我从未用过这里没有提到的其他方法。 还有很多方法，但是他们很少被使用。参考C++书籍或者文件流的帮助文档来了解其他的方法。<BR><BR>　　结论<BR><BR>　　你应该已经掌握了如何使用ASCII文件和二进制文件。有很多方法可以帮你实现输入输出，尽管很少有人使用他们。我知道很多人不熟悉文件I/O操作，我希望这篇文章对你有所帮助。 每个人都应该知道. 文件I/O还有很多显而易见的方法,?例如包含文件 &lt;stdio.h&gt;. 我更喜欢用流是因为他们更简单。 祝所有读了这篇文章的人好运, 也许以后我还会为你们写些东西。</P><img src ="http://www.cppblog.com/xmh79/aggbug/1289.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xmh79/" target="_blank">飘羽</a> 2005-11-24 14:50 <a href="http://www.cppblog.com/xmh79/articles/1289.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>