首先,我們要明白的是,VC是通過ODBC來訪問Excel表格的,也就是說,VC將Excel表格,當(dāng)作數(shù)據(jù)庫來處理。當(dāng)然了,也可以通過讀以tab鍵隔開的文件來處理這樣的文件,但是,我還是更加愿意用讀取數(shù)據(jù)庫的方式來訪問Excel表格。
第二,既然是數(shù)據(jù)庫,那么,就需要建立一個與該庫對應(yīng)的dsn,這個,而且,在建立dsn之前,首先要確定,已經(jīng)安裝了Excel的驅(qū)動。
第三,要訪問數(shù)據(jù)庫中的表格,就要先打開該表格,如此,就需要一個與之對應(yīng)的RecordSet。如此,有如下代碼:
void CRWExcel::ReadFromExcel()
{
CDatabase database;
CString sSql;
CString sItem1, sItem2;
CString sDriver;
CString sDsn;
CString sFile = "Demo.xls";// 將被讀取的Excel文件名
// 檢索是否安裝有Excel驅(qū)動 "Microsoft Excel Driver (*.xls)"
sDriver = GetExcelDriver();
if (sDriver.IsEmpty())
{
// 沒有發(fā)現(xiàn)Excel驅(qū)動
AfxMessageBox("沒有安裝Excel驅(qū)動!");
return;
}
// 創(chuàng)建進(jìn)行存取的字符串
sDsn.Format("ODBC;DRIVER={%s};DSN='''';DBQ=%s", sDriver, sFile);
TRY
{
// 打開數(shù)據(jù)庫,建立與這個Excel對應(yīng)的Database
database.Open(NULL, false, false, sDsn);
CRecordset recset(&database);
// 設(shè)置讀取的查詢語句.demo.xls并非文件名,需要在excel中進(jìn)行//設(shè)置,具體文章最后有講
sSql = "SELECT Age, Name FROM DEMO.XLS";
// 執(zhí)行查詢語句,打開表格
recset.Open(CRecordset::forwardOnly, sSql, CRecordset::readOnly);
// 獲取查詢結(jié)果
while (!recset.IsEOF())
{
//讀取Excel內(nèi)部數(shù)值
recset.GetFieldValue("Name ", sItem1);
recset.GetFieldValue("Age", sItem2);
// 移到下一行
recset.MoveNext();
}
// 關(guān)閉數(shù)據(jù)庫
database.Close();
}
CATCH(CDBException, e)
{
// 數(shù)據(jù)庫操作產(chǎn)生異常時...
AfxMessageBox("數(shù)據(jù)庫錯誤: " + e->m_strError);
}
END_CATCH;
}
需要注意的是,我們對我們的Excel表格需要進(jìn)行一些處理,需要先選定我們要讀取的數(shù)據(jù),之后,選擇插入>>名字>>之后,在輸入框中輸入我們在select語句中用到的表名。第二,需要設(shè)置列名,為我們選定部分的最前面的一行的數(shù)據(jù)。
最近一個項(xiàng)目需要把報表的表格導(dǎo)入excel,在網(wǎng)上找了一些方法,比較研究了一下,記在這里,備忘。
表格例子如下:
<table id="tableExcel" width="100%" border="1" cellspacing="0" cellpadding="0">
<tr>
<td colspan="5" align="center">html 表格導(dǎo)出道Excel</td>
</tr>
<tr>
<td>列標(biāo)題1</td>
<td>列標(biāo)題2</td>
<td>類標(biāo)題3</td>
<td>列標(biāo)題4</td>
<td>列標(biāo)題5</td>
</tr>
<tr>
<td>aaa</td>
<td>bbb</td>
<td>ccc</td>
<td>ddd</td>
<td>eee</td>
</tr>
<tr>
<td>AAA</td>
<td>BBB</td>
<td>CCC</td>
<td>DDD</td>
<td>EEE</td>
</tr>
<tr>
<td>FFF</td>
<td>GGG</td>
<td>HHH</td>
<td>III</td>
<td>JJJ</td>
</tr>
</table>
1、js的方法
A、將整個表格拷貝到EXCEL中
function method1(tableid) {
var curTbl = document.getElementById(tableid);
var oXL = new ActiveXObject("Excel.Application");
var oWB = oXL.Workbooks.Add();
var oSheet = oWB.ActiveSheet;
var sel = document.body.createTextRange();
sel.moveToElementText(curTbl);
sel.select();
sel.execCommand("Copy");
oSheet.Paste();
oXL.Visible = true;
}
B、讀取表格中每個單元到EXCEL中:
function method2(tableid)
{
var curTbl = document.getElementById(tableid);
var oXL = new ActiveXObject("Excel.Application");
var oWB = oXL.Workbooks.Add();
var oSheet = oWB.ActiveSheet;
var Lenr = curTbl.rows.length;
for (i = 0; i < Lenr; i++)
{
var Lenc = curTbl.rows(i).cells.length;
for (j = 0; j < Lenc; j++)
{
oSheet.Cells(i + 1, j + 1).value = curTbl.rows(i).cells(j).innerText;
}
}
oXL.Visible = true;
}
c、把表格輸出到另一個頁面,然后存成cvs格式
function getXlsFromTbl(inTblId, inWindow)
{
try {
var allStr = "";
var curStr = "";
if (inTblId != null && inTblId != "" && inTblId != "null") {
curStr = getTblData(inTblId, inWindow);
}
if (curStr != null) {
allStr += curStr;
}
else {
alert("你要導(dǎo)出的表不存在");
return;
}
var fileName = getExcelFileName();
doFileExport(fileName, allStr);
}
catch(e) {
alert("導(dǎo)出發(fā)生異常:" + e.name + "->" + e.description + "!");
}
}
function getTblData(inTbl, inWindow) {
var rows = 0;
var tblDocument = document;
if (!!inWindow && inWindow != "") {
if (!document.all(inWindow)) {
return null;
}
else {
tblDocument = eval(inWindow).document;
}
}
var curTbl = tblDocument.getElementById(inTbl);
var outStr = "";
if (curTbl != null) {
for (var j = 0; j < curTbl.rows.length; j++) {
for (var i = 0; i < curTbl.rows[j].cells.length; i++) {
if (i == 0 && rows > 0) {
outStr += " \t";
rows -= 1;
}
outStr += curTbl.rows[j].cells[i].innerText + "\t";
if (curTbl.rows[j].cells[i].colSpan > 1) {
for (var k = 0; k < curTbl.rows[j].cells[i].colSpan - 1; k++) {
outStr += " \t";
}
}
if (i == 0) {
if (rows == 0 && curTbl.rows[j].cells[i].rowSpan > 1) {
rows = curTbl.rows[j].cells[i].rowSpan - 1;
}
}
}
outStr += "\r\n";
}
}
else {
outStr = null;
alert(inTbl + "不存在 !");
}
return outStr;
}
function getExcelFileName() {
var d = new Date();
var curYear = d.getYear();
var curMonth = "" + (d.getMonth() + 1);
var curDate = "" + d.getDate();
var curHour = "" + d.getHours();
var curMinute = "" + d.getMinutes();
var curSecond = "" + d.getSeconds();
if (curMonth.length == 1) {
curMonth = "0" + curMonth;
}
if (curDate.length == 1) {
curDate = "0" + curDate;
}
if (curHour.length == 1) {
curHour = "0" + curHour;
}
if (curMinute.length == 1) {
curMinute = "0" + curMinute;
}
if (curSecond.length == 1) {
curSecond = "0" + curSecond;
}
var fileName = "table" + "_" + curYear + curMonth + curDate + "_"
+ curHour + curMinute + curSecond + ".csv";
return fileName;
}
function doFileExport(inName, inStr) {
var xlsWin = null;
if (!!document.all("glbHideFrm")) {
xlsWin = glbHideFrm;
}
else {
var width = 6;
var height = 4;
var openPara = "left=" + (window.screen.width / 2 - width / 2)
+ ",top=" + (window.screen.height / 2 - height / 2)
+ ",scrollbars=no,width=" + width + ",height=" + height;
xlsWin = window.open("", "_blank", openPara);
}
xlsWin.document.write(inStr);
xlsWin.document.close();
xlsWin.document.execCommand('Saveas', true, inName);
xlsWin.close();
}
總結(jié):比較上面3種方法,感覺第一種方法比較完美一些,因?yàn)檫@種方法比較完整的輸出表格的格式。但,第一和第二種方法都用了ActiveX 對象,對客戶端的安全有要求,而且最大的問題還有一個,就是excel 對象無法關(guān)閉。第3中方法雖然沒有用ActiveX 對象,但是用了彈出窗口輸出, 如果禁止了彈出窗口則無法使用。
對于execl 對象無法關(guān)閉的問題,下面的方法是一個權(quán)宜方法:
function Cleanup() {
window.clearInterval(idTmr);
CollectGarbage();
}
調(diào)用方法:
idTmr = window.setInterval("Cleanup();",1);
2、Asp.net(c#)中的方法
這種方法其實(shí)類似上面的js的第3中方法(也可以在其他的web腳本來實(shí)現(xiàn),比如asp中vbscript,或者php),把表格用文件流的方式
輸出為excel。實(shí)例代碼如下:
public void OutPutExcel(string title)
{
Response.Clear();
Response.Buffer = true;
Response.Charset = "utf-8";
Response.AddHeader("Content-Disposition", "attachment;filename=" + HttpUtility.UrlEncode(title + ".xls"));
Response.ContentEncoding = System.Text.Encoding.GetEncoding("utf-8");
Response.ContentType = "application/ms-excel";
Page.EnableViewState = false;
System.IO.StringWriter oStringWriter = new System.IO.StringWriter();
System.Web.UI.HtmlTextWriter oHtmlTextWriter = new System.Web.UI.HtmlTextWriter(oStringWriter);
this.Page.RenderControl(oHtmlTextWriter);
string temp = oStringWriter.ToString();
Response.Write(temp);
Response.End();
}
這種方法的從本質(zhì)上說并非標(biāo)準(zhǔn)的excel格式,不過把html格式的文件另存為excel的格式,然后用excel打開罷了。
3、利用ExceL Application或者M(jìn)SOWC 或者ado.net
這種方法都是利用服務(wù)器的組件來時實(shí)現(xiàn),要求服務(wù)端要安裝excel,具體的代碼可以看下面的鏈接:
http://www.cnblogs.com/pucumt/archive/2006/09/13/503120.html
http://support.microsoft.com/default.aspx?scid=kb;zh-cn;306023#top
我不提倡用這種方法,因?yàn)樾枰加梅?wù)器的資源。
簡單明了比《Javascript之文件操作 (IE)》實(shí)用!
<script>
/*
object.OpenTextFile(filename[, iomode[, create[, format]]])
參數(shù)
object
必選項(xiàng)。object 應(yīng)為 FileSystemObject 的名稱。
filename
必選項(xiàng)。指明要打開文件的字符串表達(dá)式。
iomode
可選項(xiàng)。可以是三個常數(shù)之一:ForReading 、 ForWriting 或 ForAppending 。
create
可選項(xiàng)。Boolean 值,指明當(dāng)指定的 filename 不存在時是否創(chuàng)建新文件。如果創(chuàng)建新文件則值為 True ,如果不創(chuàng)建則為 False 。如果忽略,則不創(chuàng)建新文件。
format
可選項(xiàng)。使用三態(tài)值中的一個來指明打開文件的格式。如果忽略,那么文件將以 ASCII 格式打開。
設(shè)置
iomode 參數(shù)可以是下列設(shè)置中的任一種:
常數(shù) 值 描述
ForReading 1 以只讀方式打開文件。不能寫這個文件。
ForWriting 2 以寫方式打開文件
ForAppending 8 打開文件并從文件末尾開始寫。
format 參數(shù)可以是下列設(shè)置中的任一種:
值 描述
TristateTrue 以 Unicode 格式打開文件。
TristateFalse 以 ASCII 格式打開文件。
TristateUseDefault 使用系統(tǒng)默認(rèn)值打開文件。
*/
//讀文件
function readFile(filename){
var fso = new ActiveXObject("Scripting.FileSystemObject");
var f = fso.OpenTextFile(filename,1);
var s = "";
while (!f.AtEndOfStream)
s += f.ReadLine()+"\n";
f.Close();
return s;
}
//寫文件
function writeFile(filename,filecontent){
var fso, f, s ;
fso = new ActiveXObject("Scripting.FileSystemObject");
f = fso.OpenTextFile(filename,8,true);
f.WriteLine(filecontent);
f.Close();
alert('ok');
}
</script>
<html>
<input type="text" id="in" name="in" />
<input type="button" value="Write!" onclick="writeFile('c:/12.txt',document.getElementById('in').value);"/><br><br>
<input type="button" value="Read!" onclick="document.getElementById('show').value=readFile('c:/12.txt');"/><br>
<textarea id="show" name="show" cols="50" rows="8" >
</textarea>
</html>
概述
在程序中經(jīng)常要用到設(shè)置或者其他少量數(shù)據(jù)的存盤,以便程序在下一次執(zhí)行的時候可以使用,比如說保存本次程序執(zhí)行時窗口的位置、大小、一些用戶設(shè)置的數(shù)據(jù)等等,在 Dos 下編程的時候,我們一般自己產(chǎn)生一個文件,由自己把這些數(shù)據(jù)寫到文件中,然后在下一次執(zhí)行的時候再讀出來使用。在 Win32 編程中當(dāng)然你也可以這樣干,但 Windows 已經(jīng)為我們提供了兩種方便的辦法,那就是使用注冊表或者 ini 文件(Profile)來保存少量數(shù)據(jù)。本文中先介紹一下 .ini 文件的使用。
ini 文件是文本文件,中間的數(shù)據(jù)格式一般為:
[Section1 Name]
KeyName1=value1
KeyName2=value2
...
[Section2 Name]
KeyName1=value1
KeyName2=value2
ini 文件可以分為幾個 Section,每個 Section 的名稱用 [] 括起來,在一個 Section 中,可以有很多的 Key,每一個 Key 可以有一個值并占用一行,格式是 Key=value,Win32 對 ini 文件操作的 api 中,有一部分是對 win.ini 操作的,有一部分是對用戶自定義的 ini 文件操作的。Win.in 和 system.ini 是Windows的兩個非常重要的初始化文件,Windows將用戶所作的選擇以及各種變化的系統(tǒng)信息記錄在這兩個文件中。System.ini 描述了系統(tǒng)硬件的當(dāng)前狀態(tài),Win.ini 文件則包含了Windows 系統(tǒng)運(yùn)行環(huán)境的當(dāng)前配置。由于 Win.ini 文件的重要性和常用性,Win32 中有專門對 Win.ini 進(jìn)行操作的 api,它們是:
GetProfileInt - 從 Win.ini 文件的某個 Section 取得一個 key 的整數(shù)值,它的原形是:
GetProfileInt(
LPCTSTR lpAppName, // 指向包含 Section 名稱的字符串地址
LPCTSTR lpKeyName, // 指向包含 Key 名稱的字符串地址
INT nDefault // 如果 Key 值沒有找到,則返回缺省的值是多少
);
如果 Key 值沒有找到的話,返回值是 nDefault 指定的缺省值,如果 Key 中的值是負(fù)數(shù),則返回 0,如果 Key 指定的是數(shù)字和字符串的混合,則返回數(shù)字部分的值,比如說 x=1234abcd,則返回 1234
GetProfileString - 從 Win.ini 文件的某個 Section 取得一個 key 的字符串,它的原形是:
GetProfileString(
LPCTSTR lpAppName, // 指向包含 Section 名稱的字符串地址
LPCTSTR lpKeyName, // 指向包含 Key 名稱的字符串地址
LPCTSTR lpDefault, // 如果 Key 值沒有找到,則返回缺省的字符串的地址
LPTSTR lpReturnedString, // 返回字符串的緩沖區(qū)地址
DWORD nSize // 緩沖區(qū)的長度
);
返回的字符串在緩沖區(qū)內(nèi),返回的 eax 值是返回的字符串的長度(不包括尾部的0)
GetProfileSection - 從 Win.ini 文件中讀出整個 Section 的內(nèi)容,它的原形是:
GetProfileSection(
LPCTSTR lpAppName, // 指向包含 Section 名稱的字符串地址
LPTSTR lpReturnedString, // 返回數(shù)據(jù)的緩沖區(qū)地址
DWORD nSize // 返回數(shù)據(jù)的緩沖區(qū)長度
);
WriteProfileSection - 將一個整個 Section 的值 寫入 Win.ini 文件的指定 Section 中,它的原形是:
WriteProfileSection(
LPCTSTR lpAppName, // 指向包含 Section 名稱的字符串地址
LPCTSTR lpString // 要寫入的數(shù)據(jù)的地址
);
如果 Win.ini 沒有指定的 Section,API 會新建立一個并寫入數(shù)據(jù),如果已經(jīng)存在,則先刪除原來 Seciton 中所有的 Key 值然后寫入新的。
WriteProfileString - 將一個 Key 值寫入 Win.ini 文件的指定 Section 中,它的原形是:
WriteProfileString(
LPCTSTR lpAppName, // 指向包含 Section 名稱的字符串地址
LPCTSTR lpKeyName, // 指向包含 Key 名稱的字符串地址
LPCTSTR lpString // 要寫的字符串地址
);
如果 Win.ini 沒有指定的 Section,API 會新建 Section,如果沒有指定的 Key 則新建一個 Key 并寫入數(shù)據(jù),如果已經(jīng)存在,則用字符串代替原來的值。
以上的 Api 是對 Win.ini 操作的,當(dāng)然對于我們來說,用的更多的是在程序運(yùn)行的目錄中建立自己的 ini 文件,如果需要對自己的 ini 文件操作,就要用到另一組 Api,這一組 api 和上面的很象,只要把上面一組的 Profile 換成 PrivateProfile(私有的)就可以了,參數(shù)中也相應(yīng)的多了一個 ini 文件名的參數(shù)。例如 GetPrivateProfileInt、GetPrivateProfileSection、WritePrivateProfileString 等等, 下面分別介紹:
GetPrivateProfileInt - 從 ini 文件的某個 Section 取得一個 key 的整數(shù)值,它的原形是:
GetPrivateProfileInt(
LPCTSTR lpAppName, // 指向包含 Section 名稱的字符串地址
LPCTSTR lpKeyName, // 指向包含 Key 名稱的字符串地址
INT nDefault // 如果 Key 值沒有找到,則返回缺省的值是多少
LPCTSTR lpFileName // ini 文件的文件名
);
中間參數(shù)和返回值的定義和 GetProfileInt 是一樣的。
GetPrivateProfileString - 從 ini 文件的某個 Section 取得一個 key 的字符串,它的原形是:
GetPrivateProfileString(
LPCTSTR lpAppName, // 指向包含 Section 名稱的字符串地址
LPCTSTR lpKeyName, // 指向包含 Key 名稱的字符串地址
LPCTSTR lpDefault, // 如果 Key 值沒有找到,則返回缺省的字符串的地址
LPTSTR lpReturnedString, // 返回字符串的緩沖區(qū)地址
DWORD nSize // 緩沖區(qū)的長度
LPCTSTR lpFileName // ini 文件的文件名
);
GetPrivateProfileSection - 從 ini 文件中讀出整個 Section 的內(nèi)容,它的原形是:
GetPrivateProfileSection(
LPCTSTR lpAppName, // 指向包含 Section 名稱的字符串地址
LPTSTR lpReturnedString, // 返回數(shù)據(jù)的緩沖區(qū)地址
DWORD nSize // 返回數(shù)據(jù)的緩沖區(qū)長度
LPCTSTR lpFileName // ini 文件的文件名
);
這個 api 可以讀出整個 section 的內(nèi)容,當(dāng)你不知道 section 中有哪些 key 的時候,可以使用這個 api 將整個 section 讀出后再處理。
GetPrivateProfileSectionNames - 從 ini 文件中獲得 Section 的名稱,它的原形是:
GetPrivateProfileSectionNames(
LPTSTR lpszReturnBuffer, // 返回數(shù)據(jù)的緩沖區(qū)地址
DWORD nSize // 返回數(shù)據(jù)的緩沖區(qū)長度
LPCTSTR lpFileName // ini 文件的文件名
);
如果 ini 中有兩個 Section: [sec1] 和 [sec2],則返回的是 'sec1',0,'sec2',0,0 ,當(dāng)你不知道 ini 中有哪些 section 的時候可以用這個 api 來獲取名稱
WritePrivateProfileSection - 將一個整個 Section 的內(nèi)容入 ini 文件的指定 Section 中,它的原形是:
WritePrivateProfileSection(
LPCTSTR lpAppName, // 指向包含 Section 名稱的字符串地址
LPCTSTR lpString // 要寫入的數(shù)據(jù)的地址
LPCTSTR lpFileName // ini 文件的文件名
);
WritePrivateProfileString - 將一個 Key 值寫入 ini 文件的指定 Section 中,它的原形是:
WritePrivateProfileString(
LPCTSTR lpAppName, // 指向包含 Section 名稱的字符串地址
LPCTSTR lpKeyName, // 指向包含 Key 名稱的字符串地址
LPCTSTR lpString // 要寫的字符串地址
LPCTSTR lpFileName // ini 文件的文件名
);
如果 ini 中沒有指定的 Section,API 會新建 Section,如果沒有指定的 Key 則新建一個 Key 并寫入數(shù)據(jù),如果已經(jīng)存在,則用字符串代替原來的值。當(dāng)指定的 ini 也不存在的時候,API 會自動建立一個新的文件,所以使用 ini 的好處是我們不必為了保存少量的數(shù)據(jù)涉及到文件操作,就連查找文件是否存在的操作都不必要。
使用要點(diǎn):
在我們實(shí)際使用的時候,用的最多的是 GetPrivateProfileString 和 WritePrivateProfileString,但在對自定義 ini 文件操作的時候要注意的是,如果 lpFileName 指定的文件沒有路徑的話,Api 會去 Windows 的安裝目錄去找而不會在當(dāng)前目錄找,但是每次用到 ini 函數(shù)要獲取當(dāng)前路徑顯然太麻煩了,這里有一個變通的辦法,你只要在 ini 文件名前面加上 .\ 就可以了,比如說要對本目錄下的 user.ini 操作,那么文件名就是 '.\user.ini' 這樣顯然比較方便。另外,當(dāng)你要把一個 Key 清除的時候,可以使用把 lpString 指向一個空的字符串然后使用 WritePrivateProfileString。當(dāng)你要把一個 section 的全部內(nèi)容清空的時候,也不必把 key 一個個的清除,可以使用把 lpString 指向一個空的字符串然后使用 WritePrivateProfileSection。
得到運(yùn)行程序所在路徑:(其實(shí)得到的是當(dāng)前執(zhí)行程序存放路徑)。
TCHAR szFilePath[MAX_PATH + 1];
GetModuleFileName(NULL, szFilePath, MAX_PATH);
(_tcsrchr(szFilePath, _T('\\')))[1] = 0;
CString strtemp=szFilePath;
函數(shù)說明:
GetModuleFileName:The GetModuleFileName function retrieves the full path and filename for the executable file containing the specified module.
得到程序當(dāng)前工作路徑:(因?yàn)槌绦蛟谶\(yùn)行過程中,會改變工作路徑)
char pBuf[MAX_PATH]; //存放路徑的變量
GetCurrentDirectory(MAX_PATH,pBuf); //獲取程序的當(dāng)前目錄
strcat(pBuf,"\\");
CString strtemp=pBuf;
函數(shù)說明:
GetCurrentDirectory:The GetCurrentDirectory function retrieves the current directory for the current process
|
VC 移動,復(fù)制,刪除文件(SHFileOperation)
總結(jié)一下SHFileOperation的用法,希望對大家有用
//刪除文件或者文件夾 bool DeleteFile(char * lpszPath) { SHFILEOPSTRUCT FileOp={0}; FileOp.fFlags = FOF_ALLOWUNDO | //允許放回回收站 FOF_NOCONFIRMATION; //不出現(xiàn)確認(rèn)對話框 FileOp.pFrom = lpszPath; FileOp.pTo = NULL; //一定要是NULL FileOp.wFunc = FO_DELETE; //刪除操作 return SHFileOperation(&FileOp) == 0; }
//復(fù)制文件或文件夾 bool CopyFile(char *pTo,char *pFrom) { SHFILEOPSTRUCT FileOp={0}; FileOp.fFlags = FOF_NOCONFIRMATION| //不出現(xiàn)確認(rèn)對話框 FOF_NOCONFIRMMKDIR ; //需要時直接創(chuàng)建一個文件夾,不需用戶確定 FileOp.pFrom = pFrom; FileOp.pTo = pTo; FileOp.wFunc = FO_COPY; return SHFileOperation(&FileOp) == 0; }
//移動文件或文件夾 bool MoveFile(char *pTo,char *pFrom) { SHFILEOPSTRUCT FileOp={0}; FileOp.fFlags = FOF_NOCONFIRMATION| //不出現(xiàn)確認(rèn)對話框 FOF_NOCONFIRMMKDIR ; //需要時直接創(chuàng)建一個文件夾,不需用戶確定 FileOp.pFrom = pFrom; FileOp.pTo = pTo; FileOp.wFunc = FO_MOVE; return SHFileOperation(&FileOp) == 0; }
//從命名文件或文件夾 bool ReNameFile(char *pTo,char *pFrom) { SHFILEOPSTRUCT FileOp={0}; FileOp.fFlags = FOF_NOCONFIRMATION; //不出現(xiàn)確認(rèn)對話框 FileOp.pFrom = pFrom; FileOp.pTo = pTo; FileOp.wFunc = FO_RENAME; return SHFileOperation(&FileOp) == 0; }
應(yīng)用舉例: DeleteFile("d:\\PID\0\0"); //刪除一個文件夾 DeleteFile("d:\\PID.dsp\0d:\\PID.dsw\0\0"); //刪除多個文件 CopyFile("d:\0\0","D:\\MyProjects\\臨時程序\0\0"); //把"臨時程序"文件夾放到d盤下面 CopyFile("d:\0\0","D:\\MyProjects\\臨時程序\\PID.dsp\0D:\\MyProjects\\臨時程序\\PID.dsw\0"); //把PID.dsp和PID.dsw倆個文件放到d盤下面 ReNameFile("d:\\NewName","d:\\PID\0\0"); \\把PID文件夾從命名為NewName 注意:,如果你想把"D:\\MyProjects\\臨時程序\0\0"的文件夾復(fù)制到D盤下,并從命名為NewName,應(yīng)該這樣 CopyFile("d:\\NewName\0\0","D:\\MyProjects\\臨時程序\\*.*\0\0"); //把"臨時程序"文件夾復(fù)制到d盤下并從命名為"NewName"
下面這個類方便你復(fù)制多個文件或文件夾,僅作參考 //連接多個路徑的類 class JOINFILEPATH { private: int pos; char* MultipleFilePath; public: JOINFILEPATH() { pos=0; MultipleFilePath=new char[MAX_PATH*10]; memset(MultipleFilePath,0,MAX_PATH*10); } ~JOINFILEPATH() { delete []MultipleFilePath; } void join(char *FilePath) { while(*FilePath!='\0') MultipleFilePath[pos++]=*FilePath++; pos++; } char * GetMultipleFilePath() {return MultipleFilePath;} };
//應(yīng)用舉例: JOINFILEPATH FilePath; FilePath.join("D:\\MyProjects\\臨時程序\\PID\\PID.dsp"); FilePath.join("D:\\MyProjects\\臨時程序\\PID\\PID.dsw"); CopyFile("d:\0\0",FilePath.GetMultipleFilePath());
1 pFrom和pTo最好以\0\0結(jié)尾(把存放路徑的字符串初始化為0),不然有可能會出錯,中間的每一個路徑用\0隔開 2 pFrom所指向的文件或文件夾(可以多個)會被復(fù)制或移動到pTo所指向的文件夾下面(假如文件夾不存在會詢問是否創(chuàng)建,當(dāng)然你也可以選擇直接創(chuàng)建)
參數(shù)詳解:
Typedef struct _ShFILEOPSTRUCT { HWND hWnd; //消息發(fā)送的窗口句柄; UINT wFunc; //操作類型 LPCSTR pFrom; //源文件及路徑 LPCSTR pTo; //目標(biāo)文件及路徑 FILEOP_FLAGS fFlags; //操作與確認(rèn)標(biāo)志 BOOL fAnyOperationsAborted; //操作選擇位 LPVOID hNameMappings; //文件映射 LPCSTR lpszProgressTitle; //文件操作進(jìn)度窗口標(biāo)題 }SHFILEOPSTRUCT, FAR * LPSHFILEOPSTRUCT;
在這個結(jié)構(gòu)中,hWnd是指向發(fā)送消息的窗口句柄,pFrom與pTo是進(jìn)行文件操作的源文件名和目標(biāo)文件名,它包含文件的路徑,對應(yīng)單個文件的路徑字符串,或?qū)τ诙鄠€文件,必須以NULL作為字符串的結(jié)尾或文件路徑名之間的間隔,否則在程序運(yùn)行的時候會發(fā)生錯誤。另外,pFrom和pTo都支持通配符*和?,這大大方便了開發(fā)人員的使用。例如,源文件或目錄有兩個,則應(yīng)是:char pFrom[]="d:\\Test1\0d:\\Text.txt\0",它表示對要D:盤Test目錄下的所有文件和D:盤上的Text.txt文件進(jìn)行操作。字符串中的"\\"是C語言中的'\'的轉(zhuǎn)義符,'\0'則是NULL。wFunc 是結(jié)構(gòu)中的一個非常重要的成員,它代表著函數(shù)將要進(jìn)行的操作類型,它的取值為如下:
FO_COPY: 拷貝文件pFrom到pTo 的指定位置。
FO_RENAME: 將pFrom的文件名更名為pTo的文件名。
FO_MOVE: 將pFrom的文件移動到pTo的地方。
FO_DELETE: 刪除pFrom指定的文件。
使用該函數(shù)進(jìn)行文件拷貝、移動或刪除時,如果需要的時間很長,則程序會自動在進(jìn)行的過程中出現(xiàn)一個無模式的對話框(Windows操作系統(tǒng)提供的文件操作對話框),用來顯示執(zhí)行的進(jìn)度和執(zhí)行的時間,以及正在拷貝、移動或刪除的文件名,此時結(jié)構(gòu)中的成員lpszProgressTitle顯示此對話框的標(biāo)題。fFlags是在進(jìn)行文件操作時的過程和狀態(tài)控制標(biāo)識。它主要有如下一些標(biāo)識,也可以是其組合:
FOF_FILESONLY:執(zhí)行通配符,只執(zhí)行文件;
FOF_ALLOWUNDO:保存UNDO信息,以便在回收站中恢復(fù)文件;
FOF_NOCONFIRMATION:在出現(xiàn)目標(biāo)文件已存在的時候,如果不設(shè)置此項(xiàng),則它會出現(xiàn)確認(rèn)是否覆蓋的對話框,設(shè)置此項(xiàng)則自動確認(rèn),進(jìn)行覆蓋,不出現(xiàn)對話框。
FOF_NOERRORUI:設(shè)置此項(xiàng)后,當(dāng)文件處理過程中出現(xiàn)錯誤時,不出現(xiàn)錯誤提示,否則會進(jìn)行錯誤提示。
FOF_RENAMEONCOLLISION:當(dāng)已存在文件名時,對其進(jìn)行更換文提示。
FOF_SILENT:不顯示進(jìn)度對話框。
FOF_WANTMAPPINGHANDLE:要求SHFileOperation()函數(shù)返回正處于操作狀態(tài)的實(shí)際文件列表,文件列表名柄保存在hNameMappings成員中。
SHFILEOPSTRUCT結(jié)構(gòu)還包含一個SHNAMEMAPPING結(jié)構(gòu)的數(shù)組,此數(shù)組保存由SHELL計算的每個處于操作狀態(tài)的文件的新舊路徑。
在使用該函數(shù)刪除文件時必須設(shè)置SHFILEOPSTRUCT結(jié)構(gòu)中的神秘FOF_ALLOWUNDO標(biāo)志,這樣才能將待刪除的文件拷到Recycle Bin,從而使用戶可以撤銷刪除操作。需要注意的是,如果pFrom設(shè)置為某個文件名,用FO_DELETE標(biāo)志刪除這個文件并不會將它移到Recycle Bin,甚至設(shè)置FOF_ALLOWUNDO標(biāo)志也不行,在這里你必須使用全路徑名,這樣SHFileOperation才會將刪除的文件移到Recycle Bin。
轉(zhuǎn)自http://blog.csdn.net/jhb92/archive/2007/04/13/1563452.aspx
Shell的文件操作函數(shù) SHFileOperation 功能: 1.復(fù)制一個或多個文件 2.刪除一個或多個 3.重命名文件 4.移動一個或多個文件
有一樣的Win32API功能函數(shù)是: CopyFile(),DeleteFile(),MoveFile() MoveFile可以對文件重命名! Win32 API 的層次比SHFileOperation低
SHFileOperation 的重要參數(shù) 1.wFunc //對pFrom pTo要執(zhí)行的操作 2.fFlags //影響對wFunx的操作 3.hNameMappings //有系統(tǒng)填充,和你也可以填充 4.lpszProgressTitle
pFrom pTo 在結(jié)尾是兩個'\0\0' 通常用一個'\0',這樣會失敗的!! 當(dāng)FOF_MULTIDESTFILES szPFrom[lstrlen(szPFrom)+1]=0
szPFrom:必須先確定他所指定的文件存在! 可以是單個文件名,*.*,或包含統(tǒng)配符的文件名 注意必須是文件名,不是文件所在的文件夾名 szSource:可以是一個目錄,如果不是目錄,但又有 多個文件,那么必須和szPFrom的每一個文件對應(yīng),還要指定 FOF_MULTIDETFILES標(biāo)志
Source and Target 多個文件---> 一個文件夾 許多單獨(dú)的文件---->一個文件夾 單獨(dú)文件--->單獨(dú)文件 許多單獨(dú)的文件---->許多單獨(dú)的文件
單獨(dú)文件:知道名字的文件 多個文件:帶有統(tǒng)配符的文件 注意到source中沒有對文件夾的操作!!
!!!! SHFileOperation能操作網(wǎng)絡(luò)上的文件 如果你想將本地文件復(fù)制到192.168.1.99 那么只要在192.168.1.99上共享123目錄 然后將pTo設(shè)置為\\192.168.1.99\123 就可以了 但不要設(shè)置為\\192.168.1.99
對hNameMappings操作是Undocumented!! 如果沒有指定hNameMappings 那么hNameMappings一直是NULL 只有當(dāng)某種操作(copy,move,rename)引起了文件名沖突了,hNameMappings才不是NULL!!! 當(dāng)?shù)谝淮蝐opy某些文件到空目錄中時hNameMappings一定是NULL 所以hNameMappings只是內(nèi)存中的一塊地區(qū)用來讓Explorer.exe保存被重命名的文件,以避免文件名沖突! 上面知道了如何才能使hNameMappings有效 現(xiàn)在如何使用hNameMappings,及其所指的結(jié)構(gòu)大小?并取得這個內(nèi)存塊的內(nèi)容呢? hNameMappings 是簡單LPVOID無法使用loop 要使用hNameMappings,必須定義一個結(jié)構(gòu)體 struct HANDLETOMAPPINGS { UINT uNumberOfMappings; // number of mappings in array LPSHNAMEMAPPING lpSHNameMapping; // pointer to array of mappings }; 但是可以寫一個Enumerate function to enumerate lpSHNameMapping指向的內(nèi)存塊,并且是讓W(xué)indow自己調(diào)用我的,不是我主動調(diào)用,象Loop
相關(guān)聯(lián)接: Q154123:File Name Mapping with Windows NT 4.0 Shell Q133326:SHFILEOPSTRUCT pFrom and pTo Fields Incorrect Q142066:PRB: SHGetNameMappingPtr() and SHGetNameMappingCount() Manipulating Files with the SHFileOperation Function in Visual Basic 4.0
FOF_SILENT //不產(chǎn)生正在復(fù)制的對話框 FOF_NOCONFIRMMKDIR//如果目的目錄不存在,就默認(rèn)創(chuàng)建 FOF_NOCONFIRMATION //不出現(xiàn)確認(rèn)文件替換對話框(Confirmation Dialog)(默認(rèn)替換原來的文i件) FOF_NOERRORUI//不出現(xiàn)錯誤對話框 最好不要同時使用FOF_NOERRORUI,FOF_NOCONFIRMMKDIR 因?yàn)镕OF_NOCONFIRMMKDIR屏蔽了missing directory Error 但FOF_NOERROR又屏蔽了missing directory Error,那么在同時使用FOF_NOERRORUI,FOF_NOCONFIRMMKDIR 時也阻止了新目錄安靜(沒有用戶確認(rèn)要產(chǎn)生新目錄的對話框)的產(chǎn)生!! 那么如何同時使用FOF_NOERRORUI,FOF_NOCONFIRMMKDIR? 就必須先確認(rèn)pTo所指定的目錄存在即可 BOOL MakeSureDiretoryPathExists(LPCSTR DirPath);
使用它要包含imagehlp.h和imagehlp.lib 如何判斷同時存在FOF_NOERRORUI,FOF_NOCONFIRMMKDIR
FOF_RENAMEONCOLLISION//有重復(fù)文件時自動重命名
能產(chǎn)生對話框的標(biāo)志: FOF_SILENT //progress dialog FOF_RENAMEONCOLLISION //replace dialog FOF_NOCONFIRMATION //confirmation dialog FOF_NOCONFIRMMKDIR //asks for your permission to create a new folder FOF_NOERRORUI //error message
FOF_ALLOWUNDO //將文件放入回收站,否則直接刪除,一般這個最好做默認(rèn)
|
C 標(biāo)準(zhǔn)提供一些函數(shù)用來檢查輸入輸出函數(shù)調(diào)用中的錯誤。
13.6.1 ferror 函數(shù)
在調(diào)用各種輸入輸出函數(shù)(如 putc, getc , fread, fwrite 等)時,日過出現(xiàn)錯誤,除了函數(shù)返回值有所反映外,還可以用 ferror 函數(shù)檢查。它的一般調(diào)用形式為 ferror(fp); 如果 ferror 函數(shù)返回值為0(假),表示未出錯;如果返回一個非零值,表示出錯。應(yīng)該注意,對同一個文件每一次調(diào)用輸入輸出函數(shù),均產(chǎn)生一個新的 ferror 函數(shù)值,因此,應(yīng)當(dāng)在調(diào)用一個輸入輸出函數(shù)后立即檢查 ferror 函數(shù)的值,否則信息會丟失。
在執(zhí)行 fopen 函數(shù)時,ferror 函數(shù)的初始值自動置為0。
13.6.2 clearerr (清除錯誤) 函數(shù)
clearerr 函數(shù)的作用是使文件錯誤標(biāo)志和文件結(jié)束標(biāo)志置為0。假設(shè)在調(diào)用一個輸入輸出函數(shù)時出現(xiàn)錯誤, ferror 函數(shù)值為一個非零值。在調(diào)用 clearerr
(fp)后,ferror(fp)的值變成0。
只要出現(xiàn)錯誤標(biāo)志,就一直保留,直到對同一文件調(diào)用 clearerr 函數(shù)或(重新)rewind 函數(shù),或任何其它一個輸入輸出函數(shù)。
13.7 文件輸入輸出小結(jié)
在本節(jié)中將以上介紹過的輸入輸出函數(shù)作一概括性的小結(jié),以一目了然,便于查閱。下表列出常用的緩沖文件系統(tǒng)函數(shù)。
分類 函數(shù)名 功 能
打開文件 fopen() 打開文件
關(guān)閉文件 fclose() 關(guān)閉文件
文 fseek() 改變文件位置指針的位置
件 rewind() 使文件位置指針重新置于文件開頭
定位 ftell() 返回文件位置指針的當(dāng)前值
文 fgetc(),getc() 從指定文件取得一個字符
件 fputc(),putc() 把字符輸出到指定文件
讀 fgets() 從指定文件讀取字符串
寫 fputs() 把字符串輸出到指定文件
getw() 從指定文件讀取一個字(int)型
putw() 把一個字(int)型輸出到指定文件
fread() 從指定文件中讀取數(shù)據(jù)項(xiàng)
fwrite() 把數(shù)據(jù)項(xiàng)寫到指定文件
fscanf() 從指定文件按格式輸入數(shù)據(jù)
fprintf() 按指定格式將數(shù)據(jù)寫到指定文件中
文 feof() 若到文件末尾,函數(shù)值為“真”(非0)
件 ferror() 若對文件操作出錯,函數(shù)值為“真”(非0)
狀態(tài) clearerr() 使 ferror 和 feof 函數(shù)值置零
文件這一章的內(nèi)容是很重要的,許多可供實(shí)際使用的 C 程序都包含文件處理
.
本章只介紹一些最基本的概念,由于篇幅所限,不可能舉復(fù)雜的例子。
如何進(jìn)行文件操作(程序如下)
#include "stdio.h"
main() /*先新建一個文件夾(file.txt),運(yùn)行該程序后,就有輸入到文件中.*/
{
FILE *fp;
int x,y,x1,y1,z;
printf("please input two integer numbers:");
scanf("%d %d",&x,&y);
if((fp=fopen("file.txt","w"))==NULL) /*打開文件file.txt,準(zhǔn)備往文件中寫入數(shù)據(jù)*/
{
printf("cann't open file");
exit(0);
}
fprintf(fp,"%d %d",x,y); /*將x,y的值寫入文件*/
fclose(fp); /*關(guān)閉文件*/
if((fp=fopen("file.txt","r"))==NULL) /*打開文件file.txt,準(zhǔn)備從文件中讀出數(shù)據(jù)*/
{
printf("cann't open file");
exit(0);
}
fscanf(fp,"%d %d",&x1,&y1); /*將剛才寫入的兩個整數(shù)分別讀到變量x1,y1中*/
fclose(fp); /*關(guān)閉文件*/
z=x1+y1; /*計算兩個數(shù)的和*/
printf("z=%d",z); /*顯示在屏幕上*/
}
file2.c 程序
#include<stdlib.h>
#include<stdio.h>
void main()
{
FILE*in,*out;
char ch,infile[10],outfile[10];
printf("Enter the infile name:\n");
scanf("%s",infile);
printf("Enter the infile name:\n");
scanf("%s",outfile);
if((in=fopen(infile,"r"))==NULL)
{
printf("can not open infile\n");
exit(0);
}
if((out=fopen(outfile,"w"))==NULL)
{
printf("can not open outfile\n");
exit(0);
}
while(! feof(in)) fputc(fgetc(in),out);
fclose(in);
fclose(out);
}
file1(文本文檔里的內(nèi)容)
11 12
文件打開之后,就可以對它進(jìn)行讀寫了。常用的讀寫函數(shù)如下所述。
13.4.1 fputc 函數(shù)和 fgetc 函數(shù)(putc 函數(shù)和 getc 函數(shù))
1. fputc 函數(shù)
把一個字符寫到磁盤文件上去。其一般調(diào)用形式為
fputc (ch,fp);
其中ch 是要輸出的字符,它可以是一個字符常量,也可以是一個字符變量.
fp 是文件指針變量。fputc (ch,fp) 函數(shù)的作用是將字符(ch的值)輸出到所指向的文件中去。fputc 函數(shù)也帶回一個值:如果輸出成功,則返回值就是輸出的字符;如果輸出失敗,則返回一個EOF(即—1)。EOF 是在 stdio.h 文件中定義的符號常量,值為—1。
在第4章介紹過 putchar 函數(shù),其實(shí) putchar 是從 fputc 函數(shù)派生出來的。putchar(c) 是在 stdio.h 文件中用預(yù)處理命令 #define 定義的宏:
#define putchar(c) fputc(c,stdout)
前面已敘述,stdout 是系統(tǒng)定義的文件指針變量,它與終端輸出相聯(lián).
fputc(c,stdout)的作用是將 c 的值輸出到終端.用宏putchar(c)比寫fputc(c,stdout)
簡單一些。從用戶的角度,可以把 putchar(c) 看作函數(shù)而不必嚴(yán)格地稱它為宏。
2.fgetc 函數(shù)
從指定的文件讀入一個字符,該文件必須是以讀或讀寫方式打開的。fgetc 函數(shù)的調(diào)用形式為: ch=fgetc(fp);
fp 為文件型指針變量,ch 為字符變量。fgetc 函數(shù)帶回一個字符,賦給 ch。
如果在執(zhí)行 fgetc 函數(shù)讀字符時遇到文件結(jié)束符,函數(shù)返回一個文件結(jié)束標(biāo)志EOF(即—1)。如果想從一個磁盤文件順序讀入字符并在屏幕上顯示出來,可以用:
ch=fgetc(fp);
while(ch!=EOF)
{
putchar(ch);
ch=fgetc(fp);
}
注意:EOF 不是可輸出字符,因此不能在屏幕上顯示。由于字符的 ASCII 碼不可能出現(xiàn)—1,因此 EOF 定義為—1是合適的。當(dāng)讀入的字符值等于—1(即EOF)時,表示讀入的已不是正常的字符而是文件結(jié)束符。但以上只適用于讀文本文件的情況?,F(xiàn)在 ANSI C 已允許用緩沖文件系統(tǒng)處理二進(jìn)制文件,而讀入某一個字節(jié)中的二進(jìn)制數(shù)據(jù)的值有可能是—1,而這又恰好是EOF的值.
這就出現(xiàn)了需要讀入有用數(shù)據(jù)而卻被處理為“文件結(jié)束”的情況。為了解決這個問題,ANSI C 提供一個 feof 函數(shù)來判斷文件是否真的結(jié)束。feof(fp)用來測試 fp 所指向的文件當(dāng)前狀態(tài)是否“文件結(jié)束”。如果是文件結(jié)束,函數(shù)feof(fp)的值為1(真);否則為0(假)。
如果想順序讀入一個二進(jìn)制文件中的數(shù)據(jù),可以用:
while(! feof(fp))
{
c=fgetc(fp);
……
}
當(dāng)未遇文件結(jié)束,feof(fp)的值為0,! feof(fp) 的值為1,讀入一個字節(jié)的數(shù)據(jù)賦給整型變量c,并接著對其進(jìn)行所需的處理。直到遇文件結(jié)束,feof(fp)值為1,! feof(fp) 值為0,不再執(zhí)行 while 循環(huán).
這種方法也適用于文本文件。
3. fputc 和 fgetc函數(shù)使用舉例
在掌握了以上幾種函數(shù)以后,可以編制一些簡單的使用文件的程序。
例13 .1 從鍵盤輸入一些字符,逐個把它們送到磁盤上去,直到輸入一個“#”為止。程序如下: (本例有錯誤)
#include "stdio.h"
#include "stdlib.h"
void main()
{
FILE * fp;
char ch,filename[10];
scanf("%s",filename);
if((fp=fopen(filename,"W"))==NULL)
{
printf("cannot open file\n");
exit(0);
}
ch=getchar();
ch=getchar();
while(ch!='#')
{
fputc(ch,fp);
putchar(ch);
ch=getchar();
}
putchar(10);
fclose(fp);
}
運(yùn)行結(jié)果是:
輸入:file1.c
cannot open file
(不能打開文件)
文件名由鍵盤輸入,賦給字符數(shù)組 filename。 fopen 函數(shù)中的第一個參數(shù)“文件名” 可以直接寫成字符串常量形式(如file1.c),也可以用字符數(shù)組名,在字符數(shù)組中存放文件名(如本例所用的方法)。本例運(yùn)行時,從鍵盤輸入磁盤文件名 “file1.c” ,然后輸入要寫入該磁盤文件的字符“ computer and c”, '#' 是表示輸入結(jié)束,程序?qū)?“ computer and c” 寫到以命名的磁盤文件中,同時在屏幕上顯示這些字符 ,以便核對。exit 是標(biāo)準(zhǔn) C 的庫函數(shù),作用是使程序終止,用此函數(shù)應(yīng)當(dāng)加入 stdlib 頭文件。
例13.2 將一個磁盤文件中的信息復(fù)制到另一個磁盤文件中。
(能運(yùn)行,不能復(fù)制 )
#include<stdlib.h>
#include<stdio.h>
void main()
{
FILE*in,*out;
char ch,infile[10],outfile[10];
printf("Enter the infile name:\n");
scanf("%s",infile);
printf("Enter the infile name:\n");
scanf("%s",outfile);
if((in=fopen(infile,"r"))==NULL)
{
printf("can not open infile\n");
exit(0);
}
if((out=fopen(outfile,"w"))==NULL)
{
printf("can not open outfile\n");
exit(0);
}
while(! feof(in)) fputc(fgetc(in),out);
fclose(in);
fclose(out);
}
運(yùn)行情況如下:
Enter the infile name:
file1.txt (輸入原有磁盤文件名)
Enter the infile name:
file2.txt (輸入新復(fù)制的磁盤文件名)
程序運(yùn)行結(jié)果是將 file1.txt 文件中的內(nèi)容復(fù)制到 file2.txt 中去。
以上程序是按文本文件方式處理的。也可以用此程序來復(fù)制一個二進(jìn)制文件,只需將兩個 fopen 函數(shù)中的 r 和 w 分別改為 rb 和 wb 即可。
也可以在輸入命令行時把兩個文件名一起輸入。這時要用到 main 函數(shù)的參數(shù)。程序可改為:
#include<stdlib.h>
#include<stdio.h>
void main(int agc,char *argv[])
{
FILE * in,* out;
char ch;
if(argc !=3)
{
printf("You forgot to enter a filename\n");
exit(0);
}
if((in=fopen(argv[1],"r"))==NULL)
{
printf("cannot open infile\n");
exit(0);
}
if((out=fopen(argv[2],"w"))==NULL)
{
printf("cannot open infile\n");
exit(0);
}
while(! feof(in)) fputc(fgetc(in),out);
fclose(in);
fclose(out);
}
假若本程序的原文件名為 1.c 經(jīng)編譯連接后得到的可執(zhí)行文件名為 1.exe ,則在DOS命令工作方式下,可以輸入以下的命令行:C>1 file1.c file2.c 即在輸入可執(zhí)行文件名后,再輸入兩個參數(shù) file1.c 和 file2.c ,分別輸入到 argv[1]和 argv[2]中,argv[0]的內(nèi)容為a,argc 的值等于3(因?yàn)榇嗣钚泄灿?個參數(shù)) 。如果輸入的參數(shù)少于3個,則程序會輸出:“You forgot to enter a filename”
(你忘了輸入一個文件名)。程序執(zhí)行結(jié)果是將 file1.c 中的信息復(fù)制到 file2.c 中??梢杂靡韵旅铗?yàn)證:
C>type file1.c
computer and c
(這是 file1.c 文件中的信息)
C>type file2.c
computer and c
(這是 file2.c 文件中的信息。可見 file1.c已復(fù)制到 file2.c 中了)。
最后說明一點(diǎn),為了書寫方便,系統(tǒng)把 fputc 和 fgetc 定義為宏名putc 和getc:
#define putc(ch,fp) fputc(ch,fp)
#define getc(fp) fgetc(fp)
這是在 stdio.h 中定義的。因此,用 putc 和 fputc 及用 getc 和 fgetc 是一樣的。一般可以把它們作為相同的函數(shù)來對待。
13.4.2 fead 函數(shù)和 fwrite 函數(shù)
用 getc 和 putc 函數(shù)可以用來讀寫文件中的一個字符。但是常常要求一次讀入一組數(shù)據(jù)(例如,一個實(shí)數(shù)或一個結(jié)構(gòu)體變量的值),ANSI C 標(biāo)準(zhǔn)提出設(shè)置兩個函數(shù)(fead 函數(shù)和 fwrite 函數(shù)),用來讀寫一個數(shù)據(jù)塊。它們的一般調(diào)用形式為:
fread(buffer,size,count,fp);
fwrite(buffer,size,count,fp);
其中:buffer:是一個指針.對 fread 來說,它是讀入數(shù)據(jù)的存放地址. 對fwrite 來說,
是要輸出數(shù)據(jù)的地址(以上指的是起始地址)。
size : 要讀寫的字節(jié)數(shù)。
count: 要進(jìn)行讀寫多少個 size 字節(jié)的數(shù)據(jù)項(xiàng)。
fp: 文件型指針。
如果文件以二進(jìn)制形式打開,用 fead 和 fwrite 函數(shù)就可以讀寫任何類型的信息,例如: fead(f,4,2,fp); 其中 f 是一個實(shí)型數(shù)組名。一個實(shí)型變量占4個字節(jié)。這個函數(shù)從所指向的文件讀入2個4個字節(jié)的數(shù)據(jù),存儲到數(shù)組 f 中。
如果有一個如下的結(jié)構(gòu)體類型:
struct student_type
{
char name[10];
int num;
int age;
char addr[30];
}stud[40];
結(jié)構(gòu)體數(shù)組 stud 有40個元素,每一個元素用來存放一個學(xué)生的數(shù)據(jù)(包括姓名、學(xué)號、年齡、地址)。假設(shè)學(xué)生的數(shù)據(jù)已存放在磁盤文件中,可以用下面的 for 語句和 fread 函數(shù)讀入40個學(xué)生的數(shù)據(jù):
for(i=0;i<40;i++)
fread(&stud[i],sizeof(struct student_type),1,fp);
同樣,以下 for 語句和 fwrite 函數(shù)可以將內(nèi)存中的學(xué)生數(shù)據(jù)輸出到磁盤文件中去:
for(i=0;i<40;i++)
fwrite(&stud[i],sizeof(struct student_type),1,fp);
如果 fead 和 fwrite 調(diào)用成功,則函數(shù)返回值為 count 的值,既輸入或輸出數(shù)據(jù)項(xiàng)的完整個數(shù)。
下面寫出一個完整的程序。
例13.3 從鍵盤輸入4個學(xué)生的有關(guān)數(shù)據(jù),然后把它們轉(zhuǎn)存到磁盤文件上去。
#include "stdio.h"
#define SIZE 4
struct student_type
{char name[10];
int age;
int num;
char addr[15];
}stud[SIZE];
void save()
{
FILE * fp;
int i;
if((fp=fopen("stu_list","wb"))==NULL)
{
printf("cannot open file\n");
return;
}
for(i=0;i<SIZE;i++)
if(fwrite(&stud[i],sizeof(struct student_type),1,fp)!=1)
printf("file write error\n");
fclose(fp);
}
void main()
{
int i;
for(i=0;i<SIZE;i++)
scanf("%s%d%d%s",stud[i].name,&stud[i].age,&stud[i].num,stud[i].addr);
save();
}
在main 函數(shù)中,從終端鍵盤輸入4個學(xué)生的數(shù)據(jù),然后調(diào)用 save 函數(shù),將這些數(shù)據(jù)輸出到以 " stu_list "命名的磁盤文件中。fwrite 函數(shù)的作用是將一個
長度為29字節(jié)數(shù)據(jù)塊送到 stu_list 文件中(一個 student_type 類型結(jié)構(gòu)體變量的長度為它的成員長度之和,即10+2+2+15=29)。
運(yùn)行情況如下:
輸入4個學(xué)生的姓名、學(xué)號、年齡和地址:
Zhang 1001 18 room_101
Fun 1002 18 room_102
Tan 1003 18 room_103
Lin 1004 21 room_104
程序運(yùn)行時,屏幕上并無輸出任何信息,只是將從鍵盤輸入的數(shù)據(jù)送到此盤文件上。為了驗(yàn)證在磁盤文件 “ stu_list ”中是否已存在此數(shù)據(jù),可以用以下程序從 stu_list 文件中讀入數(shù)據(jù),然后在屏幕上輸出。
#include "stdio.h"
#define SIZE 4
struct student_type
{
int age;
int num;
char addr[15];
char name[10];
}stud[SIZE];
void main()
{
int i;
FILE * fp;
fp=fopen("stu_list","rb");
for(i=0;i<SIZE;i++)
{
fread(&stud[i],sizeof(struct student_type),1,fp);
printf("%-10s %4d %4d %-15s\n",stud[i].name,&stud[i].age,&stud[i].num,stud[i].addr);
}
fclose(fp);
}
請注意:輸入輸出數(shù)據(jù)的狀況。從鍵盤輸入4個學(xué)生的數(shù)據(jù)是 ASCII 碼,也就是文本文件。在送到計算機(jī)內(nèi)存時,回車和換行符轉(zhuǎn)換成一個換行符。再從內(nèi)存以 “wb”方式(二進(jìn)制寫)輸出到 stu_list 文件,此時不發(fā)生字符轉(zhuǎn)換,按內(nèi)存中存儲形式原樣輸出到磁盤文件上。在上面驗(yàn)證程序中,又用“fread ”函數(shù)從 stu_list 文件向內(nèi)存讀入數(shù)據(jù),注意此時用的是“rb” 方式,即二進(jìn)制方式,數(shù)據(jù)按原樣輸入,也不發(fā)生字符轉(zhuǎn)換。也就是這時候內(nèi)存中的數(shù)據(jù)恢復(fù)到第一個程序向 “ stu_list ” 輸出以前的情況。最后在驗(yàn)證程序中,用printf 函數(shù)輸出到屏幕,printf 是格式輸出函數(shù),輸出 ASCII 碼,在屏幕上顯示字符。換行符又轉(zhuǎn)換為回車加換行符。
如果企圖從 “ stu_list ”文件中以 “r ”方式讀入數(shù)據(jù)就會出錯。
fread 和 fwrire 函數(shù)一般用于二進(jìn)制文件的輸入輸出。因?yàn)樗鼈兪前磾?shù)據(jù)塊的長度來處理輸入輸出的,在字符發(fā)生轉(zhuǎn)換的情況下很可能出現(xiàn)與原設(shè)想的情況不同。 例如,寫成:fread(&stud[i],sizeof(struct student_type),1,stdin); 企圖從終端鍵盤輸入數(shù)據(jù),這在語法上并不存在錯誤,編譯能通過。如果用以下形式輸入數(shù)據(jù): Zhang 1001 18 room_101
……
由于 fread 函數(shù)要求一次輸入29個字節(jié)(而不問這些字節(jié)的內(nèi)容),因此輸入數(shù)據(jù)中的空格也作為輸入數(shù)據(jù)而不作為數(shù)據(jù)間的分隔符了。連空格也存儲到 stud[i] 中了,顯然是不對的。
這個題目要求的是從鍵盤輸入數(shù)據(jù),如果已有的數(shù)據(jù)已經(jīng)以二進(jìn)制形式存儲在一個磁盤文件 stu_dat 中,要求從其中讀入數(shù)據(jù)并輸出到 stu_list 文件中,可以編寫一個 load 函數(shù),從磁盤文件中讀二進(jìn)制數(shù)據(jù)。
#include "stdio.h"
#define SIZE 4
struct student_type
{
int age;
int num;
char addr[15];
char name[10];
}stud[SIZE];
void load()
{
FILE * fp;
int i;
if((fp=fopen("stu_dat","rb"))==NULL)
{
printf("cannot open file\n");
return;
}
for(i=0;i<SIZE;i++)
if(fread(&stud[i],sizeof(struct student_type),1,fp)!=1)
{ if(feof(fp))
{ fclose(fp);
return;
}
printf("file write error\n");
}
fclose(fp);
}
main()
{
load();
save();
}
13.4.3 fprintf (從文件中輸出) 函數(shù)和 fscanf (從文件中讀入) 函數(shù)
fprintf 函數(shù)、fscanf 函數(shù) 與 printf 函數(shù)、scanf 函數(shù)作用相仿,都是格式讀寫函數(shù)。只有一點(diǎn)不同: fprintf 函數(shù)、fscanf 函數(shù)的讀寫對象不是終端而是磁盤文件。它們的一般調(diào)用方式為:
fprintf (文件指針,格式字符串,輸出表列);
fscanf (文件指針,格式字符串,輸入表列);
例如:
fprintf(fp,"%d,%6.2f",a,b);
它的作用是將整型變量 a 和實(shí)型變量 b 的值按 %d 和 %6.2f 的格式輸出到 fp 指向的文件上。如果 i=3,t=4.5 則輸出到磁盤文件上的是以下的字符串:
3, 4.50
同樣,用以下函數(shù)可以從磁盤文件上讀入 ASCII 字符:
fscanf(fp,"%d,%f",&a,&b);
磁盤文件上如果有這樣的字符:3, 4.5 即將磁盤文件中的數(shù)據(jù) 3送給變量 a,4.5 送給變量 b。
用 fprintf 和 fscanf 函數(shù)對磁盤文件讀寫,使用方便,容易理解,但由于在輸入時要將 ASCII 碼轉(zhuǎn)換為二進(jìn)制形式,在輸出時又要將二進(jìn)制形式轉(zhuǎn)換成字符,花費(fèi)時間比較多。因此,在內(nèi)存與磁盤頻繁交換數(shù)據(jù)的情況下,最好不用 fprintf 和 fscanf 函數(shù),而用 fread(從文件中讀) 和 fwrite(往文件中寫) 函數(shù)。
13.4.4 其它讀寫函數(shù)
1. putw 和 getw 函數(shù)
大多數(shù) C 編譯系統(tǒng)都提供另外兩個數(shù):putw 和 getw 函數(shù) ,用來對磁盤文件讀寫一個字(整數(shù))。例如: putw(10,fp); 它的作用是將整數(shù) 10 輸出到 fp指向的文件。而 i=getw(fp); 的作用是從磁盤文件讀一個整數(shù)到內(nèi)存,賦給整型變量 i。
如果所用的 C 編譯系統(tǒng)的庫函數(shù)中不包括 putw 和 getw 函數(shù),可以自己定義這兩個函數(shù)。putw 函數(shù)如下:
putw(int i,FILE * fp)
{
char * s; 圖1: i
s=&i; 00000000 00001010
putc(s[0],fp); s[0] s[1]
putw(s[1],fp);
return(i);
}
當(dāng)調(diào)用 putw 函數(shù)時,如果用 putw(10,fp); 語句, 形參 i 得到實(shí)參傳來的值 10, 在 putw 函數(shù)中將 i 的地址賦予指針變量 s ,而 s 是指向字符變量的指針變量,因此 s 指向 i 的第 1 個字節(jié),s+1 指向 i 的第 2 個字節(jié)。由于 * (s+0)就是 s[0],* (s+1)就是 s[1],因此,s[0]、s[1]分別對應(yīng)的第 1 個字節(jié)和第 2 個字節(jié)。順序輸出s[0]、s[1]就相當(dāng)于輸出了 i 的兩個字節(jié)中的內(nèi)容,見圖1.
getw 函數(shù)如下:
getw(FILE * fp)
{
char * s;
int i;
s=char * &i; /*使 s 指向 i 的起始地址 */
s[0]=getc(fp);
s[1]=getc(fp);
return(i);
}
putw 和 getw 函數(shù)并不是 ANSI C 標(biāo)準(zhǔn)定義的函數(shù)。許多 C 編譯系統(tǒng)都提供這兩個,但有的不以 putw 和 getw 命名此兩函數(shù),而用其它函數(shù)名,用時要注意。
2. 讀寫其它類型數(shù)據(jù)
如果用 ANSI C 提供的 fread 和 fwrite函數(shù),讀寫任何類型數(shù)據(jù)都是十分方便的。如果所用的系統(tǒng)不提供這兩個函數(shù),用戶只好自己定義所需函數(shù)。例如,可以定義一個向磁盤文件寫一個實(shí)數(shù)(用二進(jìn)制方式)函數(shù) putfloat:
putfloat(float num,FILE * fp)
{
char * s;
int count;
s=(char *) #
for(count=0;count<4,count++)
putc(s[count],fp);
}
同樣可以編寫出讀寫任何類型數(shù)據(jù)的函數(shù)。
3. fgets 函數(shù)和 fputs 函數(shù)
fgets 函數(shù)的作用是從指定文件讀入一個字符串。例如:fgets (str,a,fp); a 為要求得到的字符,但只從 fp指向的文件輸入 a-1 個字符,然后在最后加一個‘ \0 ’字符,因此得到的字符串共有 a 個字符,把它們放到字符數(shù)組 str 中.如果
在讀完 a-1個字符之前遇到換行符或 EOF ,讀入即結(jié)束。 fgets 函數(shù)返回值為 str 的首地址。
fputs 函數(shù)的作用是從指定文件輸出一個字符串。例如:fputs("Wolong",fp);
把字符串 “ Wolong ” 輸出到 fp 指向的文件。fputs 函數(shù)中第一個參數(shù)可以是字符串常量、字符數(shù)組名或字符指針。 字符串末尾的‘ \0 ’ 不輸出。若輸出成功,函數(shù)值為 0;失敗時,為 EOF 。
這兩個函數(shù)類似以前介紹過的 gets 和 puts 函數(shù), 只是 fgets 和 fputs 函數(shù)以指定的文件為讀寫對象。
fgets(從文件中獲取字符串)
fputs(往文件中寫字符串)
如果所用的 C 編譯系統(tǒng)的庫函數(shù)中不包括 putw 和 getw 函數(shù),可以自己定義這兩個函數(shù)。putw 函數(shù)如下:
putw(int i,FILE * fp)
{
char * s; 圖1: i
s=&i; 00000000 00001010
putc(s[0],fp); s[0] s[1]
putw(s[1],fp);
return(i);
}
當(dāng)調(diào)用 putw 函數(shù)時,如果用 putw(10,fp); 語句, 形參 i 得到實(shí)參傳來的值 10, 在 putw 函數(shù)中將 i 的地址賦予指針變量 s ,而 s 是指向字符變量的指針變量,因此 s 指向 i 的第 1 個字節(jié),s+1 指向 i 的第 2 個字節(jié)。由于 * (s+0)就是 s[0],* (s+1)就是 s[1],因此,s[0]、s[1]分別對應(yīng)的第 1 個字節(jié)和第 2 個字節(jié)。順序輸出s[0]、s[1]就相當(dāng)于輸出了 i 的兩個字節(jié)中的內(nèi)容,見圖1.
getw 函數(shù)如下:
getw(FILE * fp)
{
char * s;
int i;
s=char * &i; /*使 s 指向 i 的起始地址 */
s[0]=getc(fp);
s[1]=getc(fp);
return(i);
}
如果用 ANSI C 提供的 fread 和 fwrite函數(shù),讀寫任何類型數(shù)據(jù)都是十分方便的。如果所用的系統(tǒng)不提供這兩個函數(shù),用戶只好自己定義所需函數(shù)。例如,可以定義一個向磁盤文件寫一個實(shí)數(shù)(用二進(jìn)制方式)函數(shù) putfloat:
putfloat(float num,FILE * fp)
{
char * s;
int count;
s=(char *) #
for(count=0;count<4,count++)
putc(s[count],fp);
}
同樣可以編寫出讀寫任何類型數(shù)據(jù)的函數(shù)。
例13.1從鍵盤輸入一些字符,逐個把它們送到磁盤上,直到輸入一個"#"為止.程序如下:
#include<stdlib.h>
#include<stdio.h>
void main()
{
FILE*fp;
char ch,filename[10];
scanf("%s",filename);
if((fp=fopen(filename,"w"))==NULL)
{
printf("can not open file\n");
exit(0);/*終止程序*/
}
ch=getchar();/*接收輸入的一個字符*/
ch=getchar();/*這一句可以省略.*/
while(ch!='#')
{
fputc(ch,fp);putchar(ch);
ch=getchar();
}
putchar(10);/*向屏幕輸出一個換行符*/
fclose(fp);
}
運(yùn)行后的結(jié)果:輸入:is a c# 輸出 a c
13.5 文件的定位
文件中有一個位置指針,指向當(dāng)前讀寫的位置。如果順序讀寫一個文件,每次讀寫一個字符,則讀寫完一個字符后,該位置指針自動移動指向下一個字符位置。如果想改變這樣的規(guī)律,強(qiáng)制使位置指針指向其它指定的位置,可以用后面介紹的有關(guān)函數(shù)。
13.5.1 rewind 函數(shù)
rewind 函數(shù)的作用是使位置指針重新返回文件的開頭,此函數(shù)沒有返回值。
例13.4 有一個磁盤文件,第一次將它的內(nèi)容顯示在屏幕上,第二次把它復(fù)制到另一文件上。
#include "stdio.h"
void main()
{
FILE * fp1,* fp2;
fp1=fopen("file1.txt","r");
fp2=fopen("file2.txt","w");
while(! feof(fp1))
putchar(getc(fp1));
rewind(fp1);
while(! feof(fp1))
putc(getc(fp1),fp2);
fclose(fp1);
fclose(fp2);
}
(先在某一個位置上新建立一個文本文件,命名為file1.txt然后運(yùn)行本程序(在vc編譯系統(tǒng)運(yùn)行過))如:“ file1.txt ”里面的內(nèi)容是:
Nu11 pointer assignment
(無效的) ( 指示器)(分配、任務(wù)、作業(yè))
運(yùn)行后的結(jié)果是:
在第一次將內(nèi)容顯示在屏幕上,file1.txt 的位置指針已指到文件末尾,feof 的值為零(真)。執(zhí)行 rewind 函數(shù) ,使文件的位置指針重新定位于文件開頭,并使 feof 函數(shù)的值恢復(fù)為0(假)。
fopen (打開文件) fclose(文件關(guān)閉) rewind(重新) putchar(寫一個字符的函數(shù))feof=end of file (文件末尾) putc (輸出字符)
對流式文件可以進(jìn)行順序讀寫,也可以進(jìn)行隨機(jī)讀寫,關(guān)鍵在于控制文件的位置指針。如果位置指針是按字節(jié)位置順序移動的,就是順序讀寫;如果能將位置指針按需要移動到任意的位置,就可以實(shí)現(xiàn)隨機(jī)讀寫.所謂隨機(jī)讀寫,是指讀寫完上一個字符(字節(jié))后,并不一定要讀寫其后續(xù)的字符(字節(jié)),而可以讀寫文件中任意位置上所需要的字符(字節(jié))。
用 fseek 函數(shù)可以實(shí)現(xiàn)改變文件的位置指針。
fseek 函數(shù)的調(diào)用形式為: fseek (文件類型指針,位移量,起始點(diǎn))
“起始點(diǎn) ”用0、1或 2 代替,0代表 “文件開始”,1 為“當(dāng)前位置”,2 為 “文件末尾”。 ANSI C 標(biāo)準(zhǔn)指定的名字如下表所示:
起始點(diǎn) 名字 用數(shù)字代表
文件開始 SEEK_SET 0
文件當(dāng)前位置 SEEK_ CUR 1
文件末尾 SEEK_END 2
“位移量”指以 “起始點(diǎn)” 為基點(diǎn),向前移動的字節(jié)數(shù)。ANSI C 和大多數(shù)版本要求位移量是(long)長整型數(shù)據(jù)。這樣當(dāng)文件的長度大于64KB時不致出問題。ANSI C 標(biāo)準(zhǔn)規(guī)定在數(shù)字的末尾加一個字母 L,就表示是 long 型。
fseek 函數(shù)一般用于二進(jìn)制文件,因?yàn)槲谋疚募l(fā)生字符轉(zhuǎn)換,計算位置時往往會發(fā)生混亂。
下面是fseek 函數(shù)調(diào)用的幾個例子:
fseek(fp,100L,0);/* 將位置指針移到離文件頭100個字節(jié)處 */
fseek(fp,50L,1); /* 將位置指針移到離當(dāng)前位置50個字節(jié)處 */
fseek(fp,--10L,2);/* 將位置指針從文件末尾處向后退 10 個字節(jié) */
利用 fseek 函數(shù)就可以實(shí)現(xiàn)隨機(jī)讀寫了。
例13.5 在磁盤文件上存有 10 個學(xué)生的數(shù)據(jù)。要求將第 1、3、5、7、9個學(xué)生數(shù)據(jù)輸入計算機(jī),并在屏幕上顯示出來。程序如下:
#include "stdlib.h"
#include "stdio.h"
struct student_type
{
char name[10];
int num;
int age;
char sex;
}stud[10];
void main()
{
int a;
FILE * fp;
if((fp=fopen("stud_dat.txt","rb"))==NULL)
{
printf("can not open file\n");
exit(0);
}
for(a=0;a<10;a+=2)
{
fseek(fp,a * sizeof(struct student_type),0);
fread(&stud[a],sizeof(struct student_type),1,fp);
printf("%s %d %d %c\n",stud[a].name,stud[a].num,stud[a].age,stud[a].sex);
}
fclose(fp);
}
(先新建一個文本文件名為“stud_dat.txt”(可以改其它名字),然后在里面輸入10學(xué)生的數(shù)據(jù)。接著再把本程序在VC編譯系統(tǒng)中運(yùn)行……)。
13.5.3 ftell 函數(shù)
ftell 函數(shù)的作用是得到流式文件中的當(dāng)前位置,用相對文件開頭的位移量來表示。由于文件中的位置指針經(jīng)常移動,人們往往不容易知道其當(dāng)前位置。
用 ftell 函數(shù)可以得到當(dāng)前位置 。如果 ftell 函數(shù)返回值為--1L,表示出錯。例如:
a=ftell(fp);
if(a==--1L)
printf("error\n");
變量 a 存放當(dāng)前位置,如調(diào)用函數(shù)時出錯(如不存在 fp 文件),則輸出“error”。
tell(告訴,吩咐,斷定,知道,)
例13.4 有一個磁盤文件,第一次將它的內(nèi)容顯示在屏幕上,第二次把它復(fù)制
#include "stdio.h"
void main()
{
FILE * fp1,* fp2;
fp1=fopen("file1.txt","r");
fp2=fopen("file2.txt","w");
while(! feof(fp1))
putchar(getc(fp1));
rewind(fp1);
while(! feof(fp1))
putc(getc(fp1),fp2);
fclose(fp1);
fclose(fp2);
}
例13.5 在磁盤文件上存有 10 個學(xué)生的數(shù)據(jù)。要求將第 1、3、5、7、9個學(xué)
#include "stdlib.h"
#include "stdio.h"
struct student_type
{
char name[10];
int num;
int age;
char sex;
}stud[10];
void main()
{
int a;
FILE * fp;
if((fp=fopen("stud_dat.txt","rb"))==NULL)
{
printf("can not open file\n");
exit(0);
}
for(a=0;a<10;a+=2)
{
fseek(fp,a * sizeof(struct student_type),0);
fread(&stud[a],sizeof(struct student_type),1,fp);
printf("%s %d %d %c\n",stud[a].name,stud[a].num,stud[a].age,stud[a].sex);
}
fclose(fp);
}
file1(文本文檔里的內(nèi)容)
Nu11 pointer assignment
(無效的) ( 指示器)(分配、任務(wù)、作業(yè))
stud_dat(文本文檔里的內(nèi)容)
Nu11 pointer assignment
(無效的) ( 指示器)(分配、任務(wù)、作業(yè))
Nu11 pointer assignment
(無效的) ( 指示器)(分配、任務(wù)、作業(yè))
Nu11 pointer assignment
(無效的) ( 指示器)(分配、任務(wù)、作業(yè))
Nu11 pointer assignment
(無效的) ( 指示器)(分配、任務(wù)、作業(yè))
Nu11 pointer assignment
(無效的) ( 指示器)(分配、任務(wù)、作業(yè))
ASCII碼是7位編碼,字符在計算機(jī)中以其ASCII碼方式表示,其長度為1個字節(jié), 有符號字符型數(shù)。編碼范圍是0x00-0x7F(0~127)。ASCII字符集包括英文字母、阿拉伯?dāng)?shù)字和標(biāo)點(diǎn)符號等字符。其中0x00-0x20和0x7F共33個控制字符。
ASCII 十六進(jìn)制 控制字 代碼含義
00 00 NUL 空
01 01 SOH 標(biāo)題開始
02 02 STX 正文開始
03 03 ETX 正文結(jié)束
04 04 EOT 傳輸結(jié)否
05 05 ENQ 詢問
06 06 ACK 確認(rèn)
07 07 BEL 響鈴
08 08 BS 退格
09 09 HT 橫向列表
10 0A LF 換行
11 0B VT 縱向列表
12 0C FF 換頁
13 0D CR 回車
14 0E SO 換檔(Shift-Out)
15 0F SI 換檔(Shift-In)
16 10 DLE 數(shù)據(jù)鏈擴(kuò)展
17 11 DC1 設(shè)備控制1
18 12 DC2 設(shè)備控制2
19 13 DC3 設(shè)備控制3
20 14 DC4 設(shè)備控制4
21 15 NAK 不確認(rèn)
22 16 SYN 同步字符
23 17 ETB 傳輸塊結(jié)否
24 18 CAN 作廢
25 19 EM 介質(zhì)結(jié)束
26 1A SUB 置換
27 1B ESC 擴(kuò)展
28 1C FS 文件分隔符
29 1D GS 組分隔符
30 1E RS 記錄分隔符
31 1F US 單位分隔符
ASCII碼對照表
ASCII碼 |
鍵盤 |
ASCII 碼 |
鍵盤 |
ASCII 碼 |
鍵盤 |
ASCII 碼 |
鍵盤 |
27 |
ESC |
32 |
SPACE |
33 |
! |
34 |
" |
35 |
# |
36 |
$ |
37 |
% |
38 |
& |
39 |
' |
40 |
( |
41 |
) |
42 |
* |
43 |
+ |
44 |
' |
45 |
- |
46 |
. |
47 |
/ |
48 |
0 |
49 |
1 |
50 |
2 |
51 |
3 |
52 |
4 |
53 |
5 |
54 |
6 |
55 |
7 |
56 |
8 |
57 |
9 |
58 |
: |
59 |
; |
60 |
< |
61 |
= |
62 |
> |
63 |
? |
64 |
@ |
65 |
A |
66 |
B |
67 |
C |
68 |
D |
69 |
E |
70 |
F |
71 |
G |
72 |
H |
73 |
I |
74 |
J |
75 |
K |
76 |
L |
77 |
M |
78 |
N |
79 |
O |
80 |
P |
81 |
Q |
82 |
R |
83 |
S |
84 |
T |
85 |
U |
86 |
V |
87 |
W |
88 |
X |
89 |
Y |
90 |
Z |
91 |
[ |
92 |
\ |
93 |
] |
94 |
^ |
95 |
_ |
96 |
` |
97 |
a |
98 |
b |
99 |
c |
100 |
d |
101 |
e |
102 |
f |
103 |
g |
104 |
h |
105 |
i |
106 |
j |
107 |
k |
108 |
l |
109 |
m |
110 |
n |
111 |
o |
112 |
p |
113 |
q |
114 |
r |
115 |
s |
116 |
t |
117 |
u |
118 |
v |
119 |
w |
120 |
x |
121 |
y |
122 |
z |
123 |
{ |
124 |
| |
125 |
} |
126 |
~ |
只支持ASCII碼的系統(tǒng)會忽略每個字節(jié)的最高位,只認(rèn)為低7位是有效位。HZ字符編碼就是早期為了在只支持7位ASCII系統(tǒng)中傳輸中文而設(shè)計的編碼。早期很多郵件系統(tǒng)也只支持ASCII編碼,為了傳輸中文郵件必須使用BASE64或者其他編碼方式。
GB2312字符集編碼GB2312 是漢字字符集和編碼的代號,中文全稱為“信息交換用漢字編碼字符集”,由中華人民共和國國家標(biāo)準(zhǔn)總局發(fā)布,一九八一年五月一日實(shí)施。GB 是“國標(biāo)” 二字的漢語拼音縮寫。
GB2312 字符集 (character set) 只收錄簡化字漢字,以及一般常用字母和符號,主要通行于中國大陸地區(qū)和新加坡等地。GB2312 共收錄有
7445 個字符,其中簡化漢字
6763 個,字母和符號 682 個。
GB2312 將所收錄的字符分為 94 個區(qū),編號為 01 區(qū)至 94 區(qū);每個區(qū)收錄 94 個字符,編號為 01 位至 94 位。GB2312 的每一個字符都由與其唯一對應(yīng)的區(qū)號和位號所確定。例如:漢字“啊”,編號為 16 區(qū) 01 位。
GB2312 字符集的區(qū)位分布表:
區(qū)號 字?jǐn)?shù) 字符類別
01 94 一般符號
02 72 順序號碼
03 94 拉丁字母
04 83 日文假名
05 86 Katakana
06 48 希臘字母
07 66 俄文字母
08 63 漢語拼音符號
09 76 圖形符號
10-15 備用區(qū)
16-55 3755 一級漢字,以拼音為序
56-87 3008 二級漢字,以筆劃為序
88-94 備用區(qū)
這本手冊列出了 GB2312 的全部字符和它們的區(qū)位號。
GB2312 編碼
GB2312 原始編碼 (encoding) 是對所收錄的每個字符都用兩個字節(jié) (byte) 表示。第一字節(jié)為“高字節(jié)”,由字符的區(qū)號值加上 32 而形成;第二字節(jié)為“低字節(jié)”,由字符的位號值加上 32 而形成。例如:漢字“啊”,編號為 16 區(qū) 01 位。它的高字節(jié)為 16 + 32 = 48 (0x30),低字節(jié)為 01 + 32 = 33 (0x21),合并而成的編碼為 0x3021。
在區(qū)位號值上加 32 的原因大慨是為了避開低值字節(jié)區(qū)間。
由于 GB2312 原始編碼與 ASCII 編碼的字節(jié)有重疊,現(xiàn)在通行的 GB2312 編碼是在原始編碼的兩個字節(jié)上各加 128 修改而形成。例如:漢字“啊”,編號為 16 區(qū) 01 位。它的原始編碼為 0x3021,通行編碼為 0xB0A1。
如果不另加說明,GB2312 常指這種修改過的編碼。
GB2312的編碼范圍是0xA1A1-0x7E7E,去掉未定義的區(qū)域之后可以理解為實(shí)際編碼范圍是0xA1A1-0xF7FE。
上面這句有誤,應(yīng)該說GB2312的每一個漢字由兩個字節(jié)構(gòu)成,其中每一個字節(jié)的范圍都在0xA1 ~0xFE,正好每一個字節(jié)都有94個編碼范圍,與區(qū)位碼個數(shù)完全對應(yīng)。
EUC-CN可以理解為GB2312的別名,和GB2312完全相同。
區(qū)位碼更應(yīng)該認(rèn)為是字符集的定義,定義了所收錄的字符和字符位置,而GB2312及EUC-CN是實(shí)際計算機(jī)環(huán)境中支持這種字符集的編碼。HZ和 ISO-2022-CN是對應(yīng)區(qū)位碼字符集的另外兩種編碼,都是用7位編碼空間來支持漢字。區(qū)位碼和GB2312編碼的關(guān)系有點(diǎn)像 Unicode和UTF-8。
GBK字符集編碼
GBK 編碼是GB2312編碼的超集,向下完全兼容GB2312,同時GBK收錄了Unicode基本多文種平面中的所有CJK漢字。同 GB2312一樣,GBK也支持希臘字母、日文假名字母、俄語字母等字符,但不支持韓語中的表音字符(非漢字字符)。GBK還收錄了GB2312不包含的 漢字部首符號、豎排標(biāo)點(diǎn)符號等字符。
GBK的整體編碼范圍是為:高字節(jié)范圍是0×81-0xFE,低字節(jié)范圍是0x40-7E和0x80-0xFE,不包括低字節(jié)是0×7F的組合。
低字節(jié)是0x40-0x7E的GBK字符有一定特殊性,因?yàn)檫@些字符占用了ASCII碼的位置,這樣會給一些系統(tǒng)帶來麻煩。
有些系統(tǒng)中用0x40-0x7E中的字符(如“|”)做特殊符號,在定位這些符號時又沒有判斷這些符號是不是屬于某個 GBK字符的低字節(jié),這樣就會造成錯誤判斷。在支持GB2312的環(huán)境下就不存在這個問題。需要注意的是支持GBK的環(huán)境中小于0x80的某個字節(jié)未必就 是ASCII符號;另外就是最好選用小于0×40的ASCII符號做一些特殊符號,這樣就可以快速定位,且不用擔(dān)心是某個漢字的另一半。Big5編碼中也 存在相應(yīng)問題。
CP936和GBK的有些許差別,絕大多數(shù)情況下可以把CP936當(dāng)作GBK的別名。
GB18030字符集編碼
GB18030編碼向下兼容GBK和GB2312,兼容的含義是不僅字符兼容,而且相同字符的編碼也相同。GB18030收錄了所有Unicode3.1中的字符,包括中國少數(shù)民族字符,GBK不支持的韓文字符等等,也可以說是世界大多民族的文字符號都被收錄在內(nèi)。
GBK和GB2312都是雙字節(jié)等寬編碼,如果算上和ASCII兼容所支持的單字節(jié),也可以理解為是單字節(jié)和雙字節(jié)混合的變長編碼。GB18030編碼是變長編碼,有單字節(jié)、雙字節(jié)和四字節(jié)三種方式。
GB18030 的單字節(jié)編碼范圍是0x00-0x7F,完全等同與ASCII;雙字節(jié)編碼的范圍和GBK相同,高字節(jié)是0x81-0xFE,低字節(jié)的編碼范圍是0x40 -0x7E和0x80-FE;四字節(jié)編碼中第一、三字節(jié)的編碼范圍是0x81-0xFE,二、四字節(jié)是0x30-0x39。
Windows 中CP936代碼頁使用0x80來表示歐元符號,而在GB18030編碼中沒有使用0x80編碼位,用其他位置來表示歐元符號。這可以理解為是 GB18030向下兼容性上的一點(diǎn)小問題;也可以理解為0x80是CP936對GBK的擴(kuò)展,而GB18030只是和GBK兼容良好。
unicode字符集編碼
每一種語言的不同的編碼頁,增加了那些需要支持不同語言的軟件的復(fù)雜度。因而人們制定了一個世界標(biāo)準(zhǔn),叫做unicode。unicode為每個字符 提供 了唯一的特定數(shù)值,不論在什么平臺上、不論在什么軟件中,也不論什么語言。也就是說,它世界上使用的所有字符都列出來,并給每一個字符一個唯一特定數(shù)值。
Unicode的最初目標(biāo),是用1個16位的編碼來為超過65000字符提供映射。但這還不夠,它不能覆蓋全部歷史上的文字,也不能解決傳輸?shù)膯栴} (implantation head-ache's),尤其在那些基于網(wǎng)絡(luò)的應(yīng)用中。已有的軟件必須做大量的工作來程序16位的數(shù)據(jù)。
因 此,Unicode用一些基本的保留字符制定了三套編碼方式。它們分別是UTF-8,UTF-16和UTF-32。正如名字所示,在UTF-8中,字符是 以8位序列來編碼的,用一個或幾個字節(jié)來表示一個字符。這種方式的最大好處,是UTF-8保留了ASCII字符的編碼做為它的一部分,例如,在UTF-8 和ASCII中,“A”的編碼都是0x41.
UTF-16和UTF-32分別是Unicode的16位和32位編碼方式。考慮到最初的目的,通常說的Unicode就是指UTF-16。在討論Unicode時,搞清楚哪種編碼方式非常重要。
UTF-8字符集編碼
Unicode Transformation Format-8bit,允許含BOM,但通常不含BOM。是用以解決國際上字符的一種多字節(jié)編碼,它對英文使用8位(即一個字節(jié)),中文使用24為(三 個字節(jié))來編碼。UTF-8包含全世界所有國家需要用到的字符,是國際編碼,通用性強(qiáng)。UTF-8編碼的文字可以在各國支持UTF8字符集的瀏覽器上顯 示。如,如果是UTF8編碼,則在外國人的英文IE上也能顯示中文,他們無需下載IE的中文語言支持包。
GBK的文字編碼是用雙字節(jié)來表示的,即不論中、英文字符均使用雙字節(jié)來表示,為了區(qū)分中文,將其最高位都設(shè)定成1。GBK包含全部中文字符,是國家編碼,通用性比UTF8差,不過UTF8占用的數(shù)據(jù)庫比GBD大。
GBK、GB2312等與UTF8之間都必須通過Unicode編碼才能相互轉(zhuǎn)換:
GBK、GB2312--Unicode--UTF8
UTF8--Unicode--GBK、GB2312
對于一個網(wǎng)站、論壇來說,如果英文字符較多,則建議使用UTF-8節(jié)省空間。不過現(xiàn)在很多論壇的插件一般只支持GBK。