檔案是眾多的字元所組成的集合體,部分語言會將它視為一種資料串流(Stream)。
與資料串流有關的資訊,是記錄在一個資料型態為資料串流列別的資料串流物件變數,作用為資料串流與檔案的溝通橋樑。
所以檔案處理也能稱作是資料串流處理。資料串流類別分成三種【資料輸入串流類別(ifsteam)】、【資料輸出串流類別(ofsteam)】、【資料輸入/輸出串流類別(fsteam)】。
檔案類型
一、文字檔
文字檔以列為單位,列與列之間為 '\n' 字元;資料中每一個字元,以對應的ASCII碼來儲存。
若用文字檔來存, 存下來的會是"100000"這個字串, 佔7個byte。
二、二進位檔
二進位 檔裡面沒有額外的 '\n' 字元 ; 資料中每一個字元都直接以二進制的格式存取。例如執行檔、圖片、影片等,但二進制是無法使用在文字編輯軟體處理,若強制開啟會遇到亂碼。
檔案依存取權限,可分為一般檔案與唯讀檔 (read-only),檢視方式對文件按下右鍵,是否有勾起屬性中的唯獨。
若用二進位檔來存, 存下來的是0x186A0的的二進位值, 固定佔4個byte. 因為int型態就是占4個byte.
檔案存取
存取方式分為兩種,循序存取、隨機存取。
一、循序存取(sequential access)
意指一組序列(例如存於記憶數組、磁盤軟體或是磁帶中的資料)是以預先安排,有秩序的方式被人存取。循序存取有時只是唯一的存取數據方式,磁帶即屬一例。循序存取亦有可能成為有選擇性的方式,就如我們純粹有意順序處理一組資料元件。
資料寫入檔案時,附加檔案尾端,讀取時,由檔案的開端由前往後一個字元一個字元讀出。常見於文字檔方式。
二、隨機存取(random access)
又稱直接存取(direct access),代表同一時間存取一組序列中的一個隨意元件。反之則稱循序存取,即是需要更多時間去存取一個遠端元件。介分兩者的傳統圖解就似比較一軸古代畫卷(循序︰所有在元件之前的物料必須事先捲開)及一本圖書(隨機︰可以隨時翻至任何一頁)。而更近現代的例子就如比較卡式磁帶。
資料是以一筆紀錄(結構型態)作為單位寫入檔案,用每一筆紀錄長度相同,可利用目前資料紀錄所在位置,算出實際資料位置並取得資料。常見於二進制檔。
有緩衝區與無緩衝區的檔案處理
C語言
檔案處理函數 fopen()
有緩衝區的檔案處理函數
FILE 指標變數; /* 宣告指向檔案結構的指標 */
指標變數 = fopen("欲開啟檔案名稱", "存取模式");
開啟檔案的範例
FILE *fptr; /* 宣告指向檔案的指標fptr */
fptr = fopen("abc.txt","r"); /* 開啟檔案abc.txt以供讀取 */
if(fptr!=NULL) /* 判別檔案是否開啟成功 */
{
/* 檔案開啟成功時,所要執行的程式碼 */
}
else
{
/* 檔案開啟失敗時,所要執行的程式碼 */
}
stdio.h 標頭檔中的檔案處理函數
寫入檔案(1) - 範例
使用輸出轉向來把printf的結果轉到一個檔案中, 其實就算是最簡單的寫檔案方式了. 輸出轉向的工作, 可以由程式碼來完成.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{
FILE *f1; // 用來指向一個輸出的位置
f1 = fopen("test.txt","wt");
fprintf(f1 , "Helle File !! \n");
system("echo Write 'Test.txt' Content Done");
system("PAUSE");
return 0;
}
fopen 寫法
f1 = fopen("test.txt","wt");
第1個參數是檔名
第2個參數是開啟檔案的模式, w代write, t代text
寫入檔案(2) - 範例
此方法執行完,可以自動打開test.txt檔案。
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{
FILE *f1;
f1 = fopen("test.txt","wt");
fprintf(f1 , "Helle File !! \n");
fclose(f1);//關閉file
system("start test.txt");
return 0;
}
FILE *的指標
C函式庫中已經宣告好3個FILE *的指標, 分別是stdin, stdout, stderr.
1.printf("...")事實上是呼叫fprintf( stdout, "....").
2.scanf("...")事實上是呼叫fsanf( stdin, "..." ).
3.fprintf( stderr, "....")所印在螢幕上的東西不會被輸出轉向所影響.
文字檔 input/output - 範例
使用文字檔時的兩個主要函式就是fprintf/fscanf. 它們的用法和printf/scanf幾乎完全一樣, 只差在需要指定它們input/output的來源/目標.
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *input;
FILE *output;
char string[80];
input=fopen("test.txt","rt");
output=fopen("outfile.txt","wt");
if ( input==NULL )
{
fprintf( output, "open file fail!\n");
exit(0);
}
fscanf(input,"%s",string);
fprintf( output, "%s\n",string);
fclose(input);
fclose(output);
system("start outfile.txt");
return 0;
}
文字檔 迴圈(1)- 範例
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *output;
output=fopen("test.txt","wt");
for ( int i=0; i<10; i++ )
{
fprintf( output, "%d\n",i);
}
fclose(output);
system("start test.txt");
return 0;
}
文字檔 迴圈(2)- 範例
每執行一個fflush就會更新一次檔案的內容.
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *output;
output=fopen("test.txt","wt");
for ( int i=0; i<10; i++ )
{
fprintf( output, "%d\n",i);
fflush(output);
}
fclose(output);
system("start test.txt");
return 0;
}
除了fprintf/fscanf, 文字檔還有fputc/fgetc可以用, 用法和putc/getc幾乎一樣
二進位檔 寫入檔案(1)-範例
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *output;
int i=10000;
output=fopen("bin-file.bin","wb");
fwrite( &i, sizeof(int), 1, output );
fclose(output);
// system("start bin-file.bin");
return 0;
}
二進位檔 寫入檔案(2) fread/fwrite-範例
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *input;
int i=10000;
input=fopen("bin-file.bin","rb");
if ( input==NULL )
{
printf("open input file fail!\n");
exit(0);
}
fread( &i, sizeof(int), 1, input );
printf("%d\n",i);
fclose(input);
// system("start bin-file.bin");
return 0;
}
二進位檔 寫入檔案(3) -範例
二進位檔可以很快的讀/寫入一組數列
#include <stdio.h>
#define N 10
void main(void)
{
int data[N];
int get[N];
int i;
FILE *file=fopen("data.bin","wb"); //開啟檔案來寫
for ( i=0; i < N; i++ )
{
data[i]=i;
}
fwrite(data,sizeof(int),N,file);
// fwrite可以一次把數個bytes的資料寫入檔案
fclose(file);
file=fopen("data.bin","rb"); // 開啟檔案來讀
fread(get, sizeof(int),N,file);
// fread可以一次把數個bytes的資料讀入記憶體中
for ( i=0; i < N; i++ )
{
printf("%d ",get[i]);
}
printf("\n");
fclose(file);
}
二進位檔 struct -範例
struct經當會使用在檔案讀寫中. 舉例, 假如現在訂好一個檔案結構用來記錄全班同學的基本資料, 每個同學需要記錄的資料有姓名, 身高, 體重, 年齡. 姓名在20字母以下, 身高體重都用float來記, 年齡用整數來記錄.
#include <stdio.h>
struct Person
{
char name[20];
float height;
float weight;
int age;
};
void main(void)
{
struct Person p={"Peter", 180.0f, 80.0f, 25};
FILE *file=fopen("people.bin","wb");
fwrite(&p, sizeof(struct Person), 1, file);
printf("%s %f %f %d",p.name ,p.height ,p.weight ,p.age);
fclose(file);
}
二進位檔 不使用 struct -範例
#include < stdio.h >
void main(void)
{
char name[20];
float height=180.0f;
float weight=80.0f;
int age=25;
FILE *file=fopen("people.bin","wb");
fwrite(name, sizeof(char), 20, file);
fwrite(&height, sizeof(float), 1, file);
fwrite(&weight, sizeof(float), 1, file);
fwrite(&age, sizeof(int), 1, file);
fclose(file);
}
C++ 語言
檔案的輸出入
負責檔案輸出入的類別有 ifstream (檔案輸入)、ofstream (檔案輸出) 以及 fstream (檔案輸出入)。請注意所謂檔案的 IO (輸入 \ 輸出),是以程 式的角度而言,因此,檔案開啟為輸入狀態時,表示程式將由檔案讀入資(in),而 不是指將資料輸入檔案。檔案開啟為輸出狀態時,表示程式將輸出資料到檔案。為了避免混淆,我們將資料由檔案輸入到程式稱之為讀取,將資料由檔案輸出到程式稱之為寫入。
fstream類別的使用
fstream是一個由C++提供的類別,可以用 於將資料寫入檔案,或讀取檔案資料。欲使用fstream類別執行檔案的 IO時,必須先 宣告一個 fstream類別物件。
fstream file ; //宣 告一個fstream物件
然後利用fstream所提供的open成員函數,開啟一個檔案。傳入open函數的參 數有兩個,分別為 :欲開啟檔案名稱 、開啟該檔案的模式參數
file.open(“Reader.txt”,ios::in) ; //在讀取模式下開啟Reader.txt檔
若傳入的參數超過兩個可用 | 分開
file.open(“Reader.txt”,ios::in | :ios::binary) ;
//在IO以及二進位制模式下開啟Reader.txt檔
常用的檔案處理函數:
檢查檔案是否為開啟狀態, 若是則傳回true,否則傳回flase
從檔案中讀取資料至檔案結 尾為止設定給str陣列,但至多不超過size個 字元
寫入檔案(1)- 範例
#include <iostream>
#include <fstream>
#define size 10
using namespace std;
int main()
{
std::fstream file; //fstream物件
char str[size] = "測試中", str1[size]; //宣告 char
file.open("Reader.txt", ios::out | ios::trunc); //開啟檔案為輸出狀態,若檔案已存在則清除檔案內容重新寫入
file.write(str, size); //將str寫入檔案
file.close(); //關閉檔案
file.open("Reader.txt", ios::in); //開啟檔案為輸出狀態
file.read(str1, size); //從檔案讀取內容給str1
file.close(); //關閉檔案
std::cout << "Reading data from file...\n" << str1 << std::endl;
system("start Reader.txt");
return 0;
}
寫入檔案 將資料輸岀至檔案(2)- 範例
#include <iostream>
#include <fstream>
#include <stdlib.h>
#define size 10
using namespace std;
int main()
{
fstream file;
char str[5] = {"Mary","John","Judy","Joe"}; //宣告字串指標陣列
int id[5] = {100,200,300,400};
file.open("Reader.txt", ios::out); //開啟檔案
if(!file) //檢查檔案是否成功開啟
{
cerr << "Can't open file!\n";
exit(1); //在不正常情形下,中斷程式的執行
}
for(int i = 0; i < 4; i++){ //將資料輸出至檔案
file << id[i] << " " << str[i] << "\n";
}
return 0;
}
寫入檔案 由檔案讀取資料(3)- 範例
#include <iostream>
#include <fstream>
#include <stdlib.h>
#define size 10
using namespace std;
int main()
{
fstream file;
char str[8];
int id;
file.open("Reader.txt", ios::in); //將檔案開啟為輸入狀態
if(!file) //檢查檔案是否成功開啟
{
cerr << "Can't open file!\n";
exit(1); //在不正常情形下,中斷程式的執行
}
cout << setw(4) << setiosflags(ios::right) << "ID"
<< setw(8) << setiosflags(ios::right) << "Name" << endl;
cout << "-------------------\n";
while(file >> id >> str) //讀取記錄,若讀取至檔案結尾則傳回0
cout << setw(4) << setiosflags(ios::right) << id
<< setw(8) << setiosflags(ios::right) << str << endl;
return 0;
}
循序檔的使用
類似DOS-範例
寫作一個類似dos指令type的讀檔程式
#include <stdio.h>
#include <stdlib.h>
void main(int argc, char *argv[])
{
FILE *input;
if ( argc < 2 )
{
printf("missing argument\n");
exit(0);
}
input=fopen(argv[1],"rt");
if ( input==NULL )
{
printf("open %s fail!\n",argv[1]);
exit(0);
}
while( !feof(input) )
{
putchar( fgetc(input) );
}
}
隨機讀取檔的使用
類似copy
寫作一個類似copy的程式
#include <stdio.h>
#include <stdlib.h>
void main(int argc, char *argv[])
{
int filelen;
FILE *input,*output;
if ( argc < 3 )
{
printf("Usage:%s source dest\n",argv[0]);
exit(0);
}
// 開啟檔案
if( (input=fopen(argv[1],"rb"))==NULL )
{
printf("open %s fail!\n",argv[2]);
exit(0);
}
if( (output=fopen(argv[2],"wb"))==NULL )
{
printf("open %s fail!\n",argv[3]);
exit(0);
}
fseek( input, 0, SEEK_END);//先寫位置移到檔尾
filelen = ftell( input );// 再傳回檔案目前的讀寫位置
fseek( input, 0, SEEK_SET);
unsigned char *data=new unsigned char[filelen]; // 配置一塊可以讀整個檔案的空間
fread( data, 1, filelen, input ); // 把整個檔案讀進記憶體
fwrite( data, 1, filelen, output ); // 把整個檔案寫到另一個檔案中
fclose( input );
fclose( output );
}
fseek是隨機讀取用的函式。
fseek( FILE *, location, from_where );
from_where可以代入
SEEK_SET把讀寫位置設定成從檔案開頭開始向後算的第location個位置.(所以location要>=0才有意義)
SEEK_END把讀寫位置設定成從檔案結尾開始向後算的第location個位置.(所以location要<=0才有意義)
SEEK_CUR把讀寫位置設定成從檔案目前讀取位置開始向後算的第location個位置.(所以location0向後移)
ftell會傳回目前檔案的讀寫位置. 所以, 下面的動作可以得知檔案的長度.
寫一程式讓使用者查尋學生成績
#include <iostream.h>
#include <stdio.h>
void main(void)
{
int num;
int data;
FILE *file=fopen("points.bin","rb");
do
{
printf("num=");
scanf("%d",&num);
if ( num>=50 || num<0 )
break;
fseek(file, num*sizeof(int), SEEK_SET);
fread(&data, sizeof(int), 1, file);
printf("Student(%d)=%d\n",num,data);
} while(1);
fclose(file);
}