C 文件基本操作
1. 文件基本寫入
要想實現一個文件的基本寫入需要用到下面三個函數(基本流程):
步驟 | 函數 | 說明 | 備註 |
---|---|---|---|
第一步 | fopen() | 打開文件 | fopen(“文件路徑+文件名”, “模式(wb/ab/rb)”) 返回FILE* fp指針 |
第二步 | fwrite() | 寫入文件 | fwrite(“寫入的內容”, 1, “內容的大小(字節)”, “FILE* 指針對象”); |
第三步 | fclose() | 關閉文件 | fclose(“FILE* 指針對象”); 對文件進行寫入/讀取之後都需要關閉文件 |
- fopen()函數中常用的三種模式: wb/ab/rb
- wb模式:寫入文件,寫入的時候每次都會清空文件原有的內容
- ab模式:寫入文件,每次寫入內容都是追加到原文件的內容之後(不會清空原文件內容)
- rb模式:讀取文件
示列:
wb模式寫入
#include<stdio.h>
#include<stdlib.h>
// 文件寫入:使用wb模式(清空內容之後再寫入)
void file_fwrite_wb();
int main() {
file_fwrite_wb();
return 0;
}
// 文件寫入:使用wb模式(清空內容之後再寫入)
void file_fwrite_wb() {
// 文件路徑
const char fileName[] = "F:/C++/file/a.txt";
// 打開文件
FILE* fp = fopen(fileName, "wb");
if (fp == NULL) {
printf("文件打開失敗,文件路徑不存在\n ");
return;
}
// 要寫入的字符
char buf[] = "hello,world";
// 返回寫入的字節數
int res = fwrite(buf, 1, 11, fp);
// 關閉文件
fclose(fp);
}
ab模式只需要把wb替換成ab就可以了,其它代碼一樣
// 打開文件
FILE* fp = fopen(fileName, "ab");
以字符串的方式寫入int類型數據
#include<stdio.h>
#include<stdlib.h>
// 以字符串的方式寫入int類型數據
void file_fwrite_str();
int main() {
file_fwrite_str();
return 0;
}
// 以字符串的方式寫入int類型數據
void file_fwrite_str() {
// 文件路徑
const char fileName[] = "F:/C++/file/a.txt";
// 打開文件
FILE* fp = fopen(fileName, "wb");
if (fp == NULL) {
printf("文件打開失敗,文件不存在\n ");
return;
}
// 要寫入的字符
int buf[3] = { 0xA1, 0xB1, 0xC1 };
for (int i = 0; i < 3; i++)
{
char text[12];
// 加了一個,號分割數組的存儲
sprintf(text, "%d,", buf[i]);
fwrite(text, 1, strlen(text), fp);
}
// 關閉文件
fclose(fp);
}
2. 文件讀取
2.1 讀取全部內容
#include<stdio.h>
#include<stdlib.h>
// 讀取文件內容(讀取全部內容)
void read_all_content();
int main() {
read_all_content();
return 0;
}
// 讀取文件內容(讀取全部內容)
void read_all_content() {
// 文件路徑
const char fileName[] = "F:/C++/file/a.txt";
// 打開文件
FILE* fp = fopen(fileName, "rb");
if (fp == NULL) {
printf("文件打開失敗,文件不存在\n ");
return;
}
// 第一種方式讀取(直接讀取):
char buf[12];
// 我的文件內容爲: 161,177,193,
int n = fread(buf, 1, 12, fp);
// 可調試+斷點,打開局部變量窗口查看buf的值
fclose(fp);
}
2.2 順序讀取
#include<stdio.h>
#include<stdlib.h>
// 順序讀取
void read_buf_content();
int main() {
read_buf_content();
return 0;
}
// 順序讀取
void read_buf_content() {
// 文件路徑
const char fileName[] = "F:/C++/file/a.txt";
// 打開文件
FILE* fp = fopen(fileName, "rb");
if (fp == NULL) {
printf("文件打開失敗,文件路徑不存在\n ");
return;
}
// 我的文件內容爲: 161,177,193, 一共12個字節
// 所以我創建一個字節緩衝區(4個字節),下面分3次讀取
char buf[4];
// 順序讀取(文件流):
// 如果文件很大,則無法一次性讀完,可以採用順序讀取,每次讀取一定長度,直到讀取完
while (! feof (fp))
{
// 每次讀取4個字節
int n = fread(buf, 1, 4, fp);
if (n > 0) { // n 讀取到字節數
printf("read %d bytes \n", n);
// 下一次讀取會覆蓋上次讀取(buf緩衝區)的內容,斷點+調試
}
}
// 注意: feof()函數是檢測文件是否已經讀取到末尾
fclose(fp);
}
3. 隨機訪問
隨機訪問:比如MP3、MP4等文件拖動快進時就是隨機讀取( fseek()函數)) | |
功能 | 示列 |
調到第100個字節位置 | fseek(fp, 100, SEEK_SET); |
調到倒數100個字節的位置 | fseek(fp, 100, SEEK_END); |
調到當前位置往前100個字節 | fseek(fp, -100, SEEK_CUR); |
調到當前位置的往後100個字節 | fseek(fp, 100, SEEK_CUR); |
#include<stdio.h>
#include<stdlib.h>
// 隨機訪問讀取
void random_read_content();
int main() {
random_read_content();
return 0;
}
// 隨機訪問讀取
void random_read_content() {
// 文件路徑+文件名
const char fileName[] = "F:/C++/file/a.txt";
FILE* fp = fopen(fileName, "rb");
if (fp == NULL) {
printf("文件打開失敗,文件路徑不存在\n ");
return;
}
// 我的文件內容爲: 161,177,193, 一共12個字節
// 讀取文件內容的buf緩衝區
unsigned char buf[4];
// 從第8個字節開始讀取,讀取四個
fseek(fp, 8, SEEK_SET);
// n 返回的字節數
int n = fread(buf, 1, 4, fp);
fclose(fp);
}
4. 文本形式存儲與讀寫
4.1 以文本形式存儲(按行存儲,就是在結尾加一個\n)
#include<stdio.h>
#include<stdlib.h>
// 以文本形式存儲(按行存儲,就是在結尾加一個\n)
void file_fwrite_text();
int main() {
file_fwrite_text();
return 0;
}
// 以文本形式存儲(按行存儲,就是在結尾加一個\n)
void file_fwrite_text() {
// 文件路徑+文件名
const char fileName[] = "F:/C++/file/text.txt";
FILE* fp = fopen(fileName, "wb");
if (fp == NULL) {
printf("文件打開失敗,文件路徑不存在\n ");
return;
}
// 比如我需要保存服務器的IP和端口號到配置文件(按行存儲)
char ip[] = "192.169.0.1";
int port = 8080;
// 定義一行的緩衝區
char line[256];
// 保存ip
sprintf(line, "ip=%s\n", ip);
fwrite(line, 1, strlen(line), fp);
// 保存端口號
sprintf(line, "port=%d\n", port);
fwrite(line, 1, strlen(line), fp);
fclose(fp);
}
4.2 按行讀取、解析數據
使用fgets()函數,內部會檢查,如果都到\n時,停止讀取。返回實際讀取的字節
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
// 按行讀取、解析數據(fgets()函數,內部會檢查,如果都到\n時,停止讀取。返回實際讀取的字節)
void read_line_content();
int main() {
read_line_content();
return 0;
}
// 按行讀取、解析數據
void read_line_content() {
// 文件路徑+文件名
const char fileName[] = "F:/C++/file/text.txt";
FILE* fp = fopen(fileName, "rb");
if (fp == NULL) {
printf("文件打開失敗,文件不存在\n ");
return;
}
// 我的文本數據
//ip=192.169.0.1
//port=8080
// 按行讀取數據的緩衝區(一行最多512個字節)
char buf[512];
// feof()函數是檢測文件是否已經讀取到末尾
while (! feof(fp))
{
char* line = fgets(buf, 512, fp);
if (line) {
//printf("got: %s \n", line);
// 解析字符串 (sscanf()只適提取數字,不適合字符串), 需要自己解析(比較麻煩)
// 字符串爲指定char的下標
int eq = 0;
// 獲取字符串長度
int len = strlen(line);
// 動態創建新的字符串對象
char* copy = (char*)malloc(len + 1);
// copy字符創的下標
int count = 0;
for (int i = 0; i < len; i++)
{
char ch = line[i];
// 等於換行符的時候跳過
if (ch == '\n') continue;
// 如果當下邊的char等於'='是。獲取'='的下標,賦值給eq
if (ch == '=')
eq = i;
if (eq && i > eq) {
copy[count] = line[i];
count++;
}
}
// 字符串結束(爲什麼len - (eq + 1) -1)
// 因爲len是原本字符串的長度,我們截取之後長度不夠;
// eq + 1 = 下標是從0開始的; 爲什麼還要 -1呢, '\n'也算一個字節;
copy[len - (eq + 1) - 1] = 0;
// 解析之後賦值給當前的line
strcpy(line, copy);
// 釋放
free(copy);
printf("read: %s \n", line);
}
}
fclose(fp);
}
寫入到文本的數據:
解析文本數據: