十、檔案處理

檔案是眾多的字元所組成的集合體,部分語言會將它視為一種資料串流(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檔

常用的檔案處理函數:

函數

說明

open(str,mode)

以mode模式開啟名為str的檔案

close( )

關閉檔案

is_open( )

檢查檔案是否為開啟狀態, 若是則傳回true,否則傳回flase

write(str,size)

將str陣列中size個字元寫入到檔案中

read(str,size)

從檔案中讀取資料至檔案結 尾為止設定給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可以代入

寫一程式讓使用者查尋學生成績

#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);
}

最后更新于

这有帮助吗?