C++ 基礎 (12) 檔案的操作 FILE
- 課程回顧
結構體基本操作:
- 結構體型別的定義
// struct為關鍵字 Stu為自定義識別符號
// struct Stu才是結構體型別
// 結構體成員不能在定義型別時賦值
struct Stu
{
int age;
char name[50];
int score;
} // 後面有分號
2。結構體的定義和初始化
// 結構體變數初始化和陣列很類似,只有在定義時,才能初始化
// 定義結構體變數時,別忘了struct關鍵字
struct Stu obj = {18,”mike”,58}
3。結構體成員變數的使用
obj.age = 18;
(&obj)->age = 18;
2.結構體指標變數
1) 指標變數指向棧區
struct Stu *p = NULL;
struct Stu obj; //棧區結構體
p = &obj; //指向棧區
p ->age = 18;
(*p).age = 18;
2) 指標變數指向堆區
struct Stu *p = NULL;
// 指向堆區
p = (struct Stu *)malloc(sizeof(struct Stu));
p -> age = 18;
free(p);
p = NULL;
3const修飾的指標變數
//看const修飾是*還是變數
const struct Stu *p (代表指標所指向的記憶體不能變
struct Stu const *p;(代表指標不能變
3.結構體陣列
stuct Stu obj[3] = {
{18,”lily”,03},
{18,”lily”,03},
{18,”lily”,03},
}
struct Stu obj2[3] = {18,”lily”,99,22,”lucy”,-80,33….} // 這樣就比較不清晰了
struct Stu tmp[3]
int I = 0
for (I = 0;i<3;i++)
{
(tmp +i)->age = 18+I;
(*(tmp+i)).age = 18+I; // .的優先順序高,所以要加括號
tmp[i].age = 18+I; //常用
}
4.結構體和函式
1) 同類型結構體變數賦值
struct Stu obj1 = {18,”lily”,99};
struct Stu obj2;
- obj2 = obj1;
2)函式值傳遞
a)
struct Stu p;
// 結構體變數本身傳遞(值傳遞),形參修改不會影響到實參
// 呼叫完畢fun()函式,p的成員還是沒有賦值
fun(p)
void fun(struct stu p) //值傳遞,相當於拷貝了一份,所以並沒有變
{
p.age = 18;
strcpy(p.name,”mike”);
p.score = 59;
}
3 函式地址傳遞
struct Stu p;
fun(&p);
void fun(struct Stu *p)
{
p->age = 18;
strcpy(p->name,”mike”);
p->score = 59;
}
5 結構體套一級指標
struct stu
{
int age;
char *name; // 一級指標
int score;
}; // 後面有分號
1) 棧區結構體
struct Stu s;
s.age =10;
s.name = (char *)malloc( strlen(“mike”) +1);
strcpy(s.name,”mike”);
s.score = 59;
free(s.name);
2堆區結構體
struct Stu *p; //結構體是指標,後面在堆區分配空間
p = (struct Stu *)malloc(sizeof(struct Stu))
p->name = (char *)malloc(strlen(“mike”)+1)
p->age = 18
strcpy(p->name,”mike”)
p->score = 59
free(p->name)
free(p)
列舉
typedef
#define INT int
巨集定義是在預處理階段,前面的替換後面的
typedef是在編譯階段,後面的替換前面的
2 作業講解
3 檔案概述
printf 把記憶體中的10先放到緩衝區,再放到螢幕
為什麼不直接列印到螢幕,而放到緩衝區呢?
為了效率,就是快取 (可能會拿很多次,
如果只拿一次,當然是直接給螢幕比較快,但是可能會拿很多次
FILE 所有平臺的名字都一樣,FILE是一個結構體型別,裡面的成員功能一樣,不同平臺成員的名字不一樣
FILE *fp
看一下stdio.h
為了相容改成typedef 最後轉成FILE
不同平臺成員不一樣
(vs2013 vs2015同一個平臺不同編譯器,裡面成員都不一樣
FILE所有平臺的名字都一樣,FILE是一個結構體型別,裡面的成員功能一樣,不同平臺成員的名字不一樣
FILE *fp
1、fp指標,只用呼叫了fopen(),在堆區分配空間,把地址返回給fp
2、fp指標不是指向檔案,fp指標和檔案關聯,fp內部成員儲存了檔案的狀態
char *p
*fp(不能這樣寫!!不是裡面是檔案,fp只是儲存了檔案的狀態
fd 檔案描述符
ulimit
1,2被佔用了 從3開始
3、操作fp指標 不能直接操作,必須通過檔案庫函式來操作fp指標
4、通過庫函式操作fp指標,對檔案的任何操作,fp內部成員會有相應的變化(作業系統自動完成)
結論:檔案的操作是需要作業系統層來支援的,通過檔案庫函式來操作,不是像普通指標一樣,p,*p這樣直接拿內容。
https://stackoverflow.com/questions/5130375/how-exactly-does-fopen-fclose-work
https://www.cnblogs.com/llguanli/p/6791507.html
4 檔案分類
分為裝置檔案和磁碟檔案
5 檔案操作流程
1、 開啟檔案fopen()
2、 讀寫檔案
a) 按字元讀寫fgetc(),fputc()
b) 按字串(行)讀取檔案fgets(),fputs()
c) 檔案結尾判斷 feof() // end of file
3、 關閉檔案flose()
6 標準檔案裝置指標
fp指向堆區一片空間,空間中的資料和硬碟種的關聯了
檔案指標
stdin,stdout,stderr
7 標準裝置補充
8 檔案的開啟關閉
FILE *fp = NULL;
//1、windows路徑寫法 因為\是轉義字元,所以需要\\
fl = fopen(“D:\\1.txt”,”w”); //windows
fp = fopen(“D:/1.txt”,”w”); //linux,windows
// 當前路徑
fp = fopen(“a.txt”,”w”)
fp = fopen(“./a.txt,”w”)
//絕對路徑
fp = fopen(“/home/edu/a.txt”,”w”);
fopen會在堆區分配空間,返回堆區地址給fp
9 檔案路徑說明
相對路徑:
1、在linux,相對路徑相對於可執行程式
2、VS
a,編譯同時執行程式,相對路徑是相對於.vcxproj(專案檔案)所在的路徑
b, 如果直接執行程式,相對路徑線相對於可執行程式
3、Qt
a, 編譯同時執行程式,相對路徑,相對於debug所在的路徑
b, 如果直接執行程式,相對路徑相對於可執行程式
fopen(“1.txt”,”w”);
char *p = “1.txt”; //指向文字常量區
fopen(p,”w”);
char p[] = “1.txt” //棧區/陣列
fopen(p,”w”); //陣列名字是首元素地址
char *mode = “w”;
fopen(“1.txt”,mode);
10 上午知識複習
1.檔案的型別
文字檔案和二進位制檔案
2.檔案的開啟關閉
1.檔案指標
FILE *fp;
stdin //0 標準輸入
stdout //1 標準輸出
stderr //2 標準出錯
2.檔案的開啟
fopen(‘路徑’,’許可權w/r/a’)
3.檔案的關閉
11 fputc的使用
1 開啟檔案fopen()
2 讀寫檔案
3 關閉檔案f close
12 fputc的使用補充
13 fgetc的使用
1、如果是文字檔案,可以通過-1(EOF) 判斷檔案是否結尾
(!ASCII碼沒有-1
#include <stdio.h>
#include <string.h>
void write_file()
{
//1、開啟檔案
FILE *fp = fopen("4.txt", "w");
if (fp == NULL)
{
perror("write_file fopen");
return;
}
//2、寫檔案
char *p = "abcdef";
int i = 0;
int n = strlen(p);
for (i = 0; i < n; i++)
{
fputc(p[i], fp);
}
//3、關閉檔案
fclose(fp);
}
void read_file()
{
//1、開啟檔案 以讀的方式開啟
FILE *fp = fopen("4.txt", "r");
if (fp == NULL)
{
perror("write_file fopen");
return;
}
//2、讀檔案,每次讀一個字元
char ch;
// while (ch != -1) //EOF 文字檔案結尾預設是-1
while (ch != EOF) // 有這個巨集
{
ch = fgetc(fp);
// printf("ch = %c\n", ch); //以char形式打出
printf("ch = %d\n", ch); //以數字形式打出
}
//3、關閉檔案
fclose(fp);
}
int main(int argc, char const *argv[])
{
write_file();
read_file();
return 0;
}
14 feof的存在意義
1、如果是文字檔案,可以通過-1(EOF)判斷檔案是否結尾
2、如果是二進位制檔案,不能以-1判斷檔案結尾
3、feof()判斷檔案是否結尾,任何檔案都能判斷
15 feof的使用
feof(fp) // 如果到檔案結尾,返回真
1、如果第一次沒有對檔案進行讀操作,直接呼叫此函式,永遠返回真
2、此函式必須,先讀,再呼叫feof() 才有意義
3、呼叫此函式,游標不會自動往後移動
4、必須讀取後,才能判斷是否結束,判斷的是讀取的字元
(這個東西是判斷游標以前的字元
16 feof的使用補充
17 cat命令的實現
將mycat放在cat所在路徑
放到目錄裡 然後用mycat命令就可以了,C語言的cat命令就是這樣實現的
18 課堂答疑
19 vi命令的實現
不能用scanf 因為不能有回車 空格
gets 不能有回車
所以只能用fgets (可以有空格,也可以有回車
// 讀一點寫一點
20 課堂答疑
注意 fgets會儲存’\n’
21 fputs的使用
22 fgets的使用
讀一下,發現後面全都是mike
因為讀到檔案結尾的時候會接觸本次讀取,所以buf內的內容並沒有變,還是最後一行的內容,
用一下memset:
(邊界值的問題,現在也順便講解了memset怎麼用的
迴圈讀取:
解決方法:
(放前面
23 作業