C語(yǔ)言文件隨機(jī)讀寫的完全指南
導(dǎo)讀
無(wú)論是字符級(jí)的 fgetc/fputc,字符串操作的 fgets/fputs,還是格式化的 fscanf/fprintf,以及二進(jìn)制的 fread/fwrite,這些函數(shù)都遵循著"從頭到尾、依次處理"的順序讀寫模式。
然而,在實(shí)際編程中,我們常常需要更靈活的文件操作方式。今天,我們將進(jìn)入文件操作的另一個(gè)重要領(lǐng)域——隨機(jī)讀寫,學(xué)習(xí)如何精準(zhǔn)控制文件中的讀寫位置。讓我們開始探索以下五個(gè)核心函數(shù):
fseek—— 設(shè)置文件中的位置指示器位置ftell—— 獲取文件中的位置指示器位置rewind—— 重置文件中的位置指示器位置fgetpos—— 獲取文件中的位置指示器位置fsetpos—— 設(shè)置文件中的位置指示器位置
一、fseek

上圖給出了函數(shù)用法的介紹,我們可以看到該函數(shù)不僅能夠?qū)ξ谋疚募褂茫€可以對(duì)二進(jìn)制文件使用;

上圖給我們介紹了函數(shù)的三個(gè)參數(shù)與返回值,從這兩張圖片介紹中,我們可以了解該函數(shù)的使用方法:
- 向函數(shù)中傳入3個(gè)參數(shù)
stream:指向識(shí)別流的FILE對(duì)象指針offset:文件中光標(biāo)的位置- 二進(jìn)制文件中:從
origin的參考值中偏移的字節(jié)數(shù) - 文本文件中:0 或者由
ftell函數(shù)獲取的返回值
- 二進(jìn)制文件中:從
origin:為offset參考的位置SEEK_SET: 文件開頭SEEK_CUR:文件指針的當(dāng)前位置SEEK_END:文件末尾
- 函數(shù)會(huì)將與流關(guān)聯(lián)的文件內(nèi)部的光標(biāo)設(shè)置為一個(gè)新位置:
- 二進(jìn)制文件:新位置由
origin的參考位置的偏移量offset定義 - 文本文件:新位置為
0或者由先前調(diào)用ftell函數(shù)時(shí)的返回值,并且origin的參考值必須為SEEK_SET,即文件開頭; - 當(dāng)函數(shù)在這些參考值之外調(diào)用其他值,是否支持還取決于特定的系統(tǒng)和庫(kù)的實(shí)現(xiàn)。即若當(dāng)前系統(tǒng)支持,那此時(shí)的使用方法無(wú)法移植。
- 二進(jìn)制文件:新位置由
- 函數(shù)在完成設(shè)置后,會(huì)給出相應(yīng)的返回值:
- 設(shè)置成功,則函數(shù)返回
0 - 設(shè)置失敗,則函數(shù)返回非零值
- 如果發(fā)生讀取或?qū)懭脲e(cuò)誤,則函數(shù)會(huì)設(shè)置錯(cuò)誤指示器(
ferror)
- 設(shè)置成功,則函數(shù)返回
從用法中我們可以看到,該函數(shù)的使用與 ftell 函數(shù)是分不開的,因此我們下面直接來(lái)看一下 ftell 函數(shù)應(yīng)該如何使用;
二、ftell
2.1 函數(shù)介紹

該函數(shù)的使用方法為:
- 向函數(shù)中傳入一個(gè)參數(shù):
stream:指向識(shí)別流的FILE對(duì)象指針
- 函數(shù)會(huì)從流中獲取當(dāng)前位置指示器的值
- 成功時(shí),則將當(dāng)前值返回
- 失敗時(shí),則返回
-1L,并將errno設(shè)置為系統(tǒng)的特定正值
2.2 函數(shù)使用
該函數(shù)的使用我們可以簡(jiǎn)單的理解為,函數(shù)會(huì)獲取文件中的光標(biāo)位置:
- 當(dāng)文件為二進(jìn)制文件時(shí),函數(shù)的返回值為文件光標(biāo)從文件開頭開始的偏移量
- 當(dāng)文件為文本文件時(shí),函數(shù)的返回值可能沒有實(shí)際意義,但是仍可在調(diào)用
fseek時(shí)作為函數(shù)參數(shù),讓fseek將光標(biāo)的位置恢復(fù)到當(dāng)前位置
現(xiàn)在對(duì)于 fseek 和 ftell 這兩個(gè)函數(shù)我們就能夠理解為:
ftell用于獲取文件中的光標(biāo)位置fseek用于設(shè)置文件中的光標(biāo)位置
接下來(lái)我們就可以嘗試著使用這兩個(gè)函數(shù)了:
void test1() {
FILE* pf = fopen("C:\\Users\\LIQIAN\\Desktop\\data.txt", "r+");
if (pf == NULL) {
perror("fopen");
return;
}
// 通過(guò) ftell 獲取文件中的光標(biāo)當(dāng)前位置
long int index = ftell(pf);
// 檢查返回值
if (index == -1L) {
perror("ftell");
fclose(pf);
pf = NULL;
return;
}
// 通過(guò) fgetc 讀取當(dāng)前位置的文本信息
int ch = fgetc(pf);
while (ch != EOF) {
printf("ch = %c\n", ch);
ch = fgetc(pf);
}
// 通過(guò) fseek 設(shè)置文件中的光標(biāo)位置
int set = fseek(pf, index, SEEK_SET);
// 由于我們打開的為文本文件,因此 origin 的值必須為文件開頭(SEEK_SET)
if (set != 0) {
perror("fseek");
fclose(pf);
pf = NULL;
return;
}
// 再一次讀取當(dāng)前位置的元素
ch = fgetc(pf);
printf("ch = %c\n", ch);
fclose(pf);
pf = NULL;
}
這里的測(cè)試代碼邏輯比較簡(jiǎn)單:
- 先通過(guò)
ftell記錄文件當(dāng)前的光標(biāo)起始位置 - 通過(guò)
fgetc進(jìn)行內(nèi)容讀取,直到光標(biāo)移動(dòng)到文件末尾 - 之后通過(guò)
fseek重新設(shè)置光標(biāo)的位置 - 最后再一次通過(guò)
fgetc來(lái)讀取當(dāng)前位置的內(nèi)容
下面我們就來(lái)運(yùn)行以下該測(cè)試代碼:

從測(cè)試結(jié)果中可以看到,文件剛打開時(shí),光標(biāo)的位置為文件開頭,當(dāng)我們通過(guò) ftell 記錄了此時(shí)的位置后,我們通過(guò) fgetc 移動(dòng)了光標(biāo),不管光標(biāo)最后在哪里,我們都可以通過(guò) fseek 函數(shù)將光標(biāo)設(shè)置到最初我們記錄的位置;
那可能有朋友會(huì)問(wèn),如果我們?cè)谟涗浌鈽?biāo)的起始位置時(shí),光標(biāo)并不在文件開頭,這時(shí)我們需要將光標(biāo)重置到文件開頭,應(yīng)該怎么辦呢?
此時(shí)有兩種辦法:
- 通過(guò)將
offset參數(shù)值置為0,即int set = fseek(pf, 0, SEEK_SET); - 通過(guò)
rewind函數(shù)重置光標(biāo)位置
接下來(lái),我們就來(lái)看看 rwind 函數(shù)的具體用法;
三、rewind
3.1 函數(shù)介紹

該函數(shù)的用法如下:
- 向函數(shù)中傳入一個(gè)參數(shù):
stream:指向標(biāo)識(shí)流的FILE對(duì)象指針
- 函數(shù)會(huì)清除流中的文件末尾內(nèi)部指示器和錯(cuò)誤內(nèi)部指示器,并將文件內(nèi)部的位置指示器設(shè)置為文件開頭
3.2 函數(shù)使用
該函數(shù)簡(jiǎn)單的理解就是用于重置文件內(nèi)部的光標(biāo)位置。接下來(lái)我們就來(lái)對(duì)其進(jìn)行測(cè)試:
void test2() {
FILE* pf = fopen("C:\\Users\\LIQIAN\\Desktop\\data.txt", "r+");
if (pf == NULL) {
perror("fopen");
return;
}
// 通過(guò) fgetc 先讀取一定的文本信息
for (int i = 0; i < 3; i++) {
int ch = fgetc(pf);
printf("ch = %c ", ch);
}
printf("\n");
// 通過(guò) ftell 獲取文件中的光標(biāo)當(dāng)前位置
long int index = ftell(pf);
// 檢查返回值
if (index == -1L) {
perror("ftell");
fclose(pf);
pf = NULL;
return;
}
// 通過(guò) fgetc 讀取當(dāng)前位置的文本信息
int ch = fgetc(pf);
while (ch != EOF) {
printf("ch = %c ", ch);
ch = fgetc(pf);
}
printf("\n");
// 通過(guò) fseek 設(shè)置文件中的光標(biāo)位置
int set = fseek(pf, index, SEEK_SET);
// 由于我們打開的為文本文件,因此 origin 的值必須為文件開頭(SEEK_SET)
if (set != 0) {
perror("fseek");
fclose(pf);
pf = NULL;
return;
}
// 再一次讀取當(dāng)前位置的元素
ch = fgetc(pf);
printf("ch = %c\n", ch);
// 通過(guò) rewind 重置光標(biāo)位置
rewind(pf);
// 再一次讀取當(dāng)前位置的元素
ch = fgetc(pf);
printf("ch = %c\n", ch);
fclose(pf);
pf = NULL;
}
我們此時(shí)的測(cè)試邏輯很簡(jiǎn)單:
- 文件在打開時(shí),光標(biāo)會(huì)位于文件開頭
- 首先,我們通過(guò)
fgetc讀取數(shù)據(jù)來(lái)移動(dòng)光標(biāo) - 完成寫入后,通過(guò)
ftell來(lái)記錄光標(biāo)此時(shí)的位置 - 接下來(lái),我們繼續(xù)通過(guò)
fgetc讀取數(shù)據(jù),進(jìn)一步移動(dòng)光標(biāo) - 之后,我們?cè)偻ㄟ^(guò)
fseek來(lái)恢復(fù)光標(biāo)的位置 - 最后,我們?cè)偻ㄟ^(guò)
rewind來(lái)重置光標(biāo)的位置
下面我們就來(lái)對(duì)其測(cè)試一下:

可以看到,當(dāng)我們使用 rewind 后,光標(biāo)的位置就從我們最開始記錄的 l 處重置為了文件開頭 H 處。
因此當(dāng)我們?cè)趯?duì)文件進(jìn)行操作時(shí),如果我們需要重置光標(biāo)的位置,我們就可以通過(guò) rewind 實(shí)現(xiàn)。
四、fgetpos
4.1 函數(shù)介紹

該函數(shù)的用法如下所示:
- 向函數(shù)中傳入兩個(gè)參數(shù):
stream:指向標(biāo)識(shí)流的FILE對(duì)象指針pos:指向fpos_t對(duì)象指針
- 函數(shù)會(huì)將從
stream中獲取到的當(dāng)前位置信息填入到pos中- 成功時(shí),函數(shù)返回
0 - 發(fā)生錯(cuò)誤時(shí),
errno會(huì)被設(shè)置為平臺(tái)特定的正值,并且函數(shù)返回一個(gè)非零值
- 成功時(shí),函數(shù)返回
4.2 fpos_t
在函數(shù)的用法介紹中,我們看到了一個(gè)新的類型:fpos_t 。那么在使用該函數(shù)前,我們先來(lái)認(rèn)識(shí)一下這個(gè)新的類型:

該類型就是專門用于記錄文件中的光標(biāo)位置的類型,該類型的變量中存儲(chǔ)的信息,通常是通過(guò) fgetpos 函數(shù)進(jìn)行填充,并且其變量中的信息不能直接讀取,只能夠在調(diào)用 fsetpos 時(shí),作為參數(shù)使用。
那也就是說(shuō),fgetpos 與 fsetpos 這兩個(gè)函數(shù)應(yīng)該搭配起來(lái)使用,那么接下來(lái)我們就一起來(lái)看看如何 fsetpos 的具體用法;
五、fsetpos
5.1 函數(shù)介紹

該函數(shù)的用法如下所示:
- 向函數(shù)中傳入兩個(gè)參數(shù):
stream:指向標(biāo)識(shí)流的FILE對(duì)象指針pos:指向fpos_t對(duì)象指針
- 函數(shù)會(huì)將
stream的當(dāng)前位置恢復(fù)到pos中- 成功時(shí),函數(shù)返回
0 - 發(fā)生錯(cuò)誤時(shí),
errno會(huì)被設(shè)置為平臺(tái)特定的正值,并且函數(shù)返回一個(gè)非零值
- 成功時(shí),函數(shù)返回
5.2 函數(shù)使用
該函數(shù)的用法我們可以簡(jiǎn)單的理解為,將文件中的光標(biāo)位置恢復(fù)到 pos 中存儲(chǔ)的位置。
這里需要注意的是,pos_t 的對(duì)象中存儲(chǔ)的信息只能通過(guò) fgetpos 獲取,我們無(wú)法直接對(duì)其進(jìn)行賦值操作?。?!
接下來(lái)我們就來(lái)嘗試著使用一下這兩個(gè)函數(shù):
void test3() {
FILE* pf = fopen("C:\\Users\\LIQIAN\\Desktop\\data.txt", "r");
if (pf == NULL) {
perror("fopen");
return;
}
// 通過(guò) fgetc 讀取數(shù)據(jù)來(lái)移動(dòng)光標(biāo)
for (int i = 0; i < 3; i++) {
int ch = fgetc(pf);
printf("ch = %c\n", ch);
}
printf("\n");
// 記錄光標(biāo)位置變量
fpos_t pos;
// 通過(guò) fgetpos 獲取當(dāng)前光標(biāo)位置
int get = fgetpos(pf, &pos);
// 通過(guò) fgetc 移動(dòng)光標(biāo)
int ch = fgetc(pf);
while (ch != '!') {
printf("ch = %c\n", ch);
ch = fgetc(pf);
}
printf("\nch = %c\n", ch);
// 通過(guò) fsetpos 來(lái)恢復(fù)光標(biāo)位置
int set = fsetpos(pf, &pos);
ch = fgetc(pf);
printf("\nch = %c\n", ch);
fclose(pf);
pf = NULL;
}
這一次我們測(cè)試的邏輯與前面的邏輯一致:
- 先通過(guò)
fgetc來(lái)移動(dòng)光標(biāo) - 再通過(guò)
fgetpos來(lái)獲取光標(biāo)位置信息 - 之后再一次通過(guò)
fgetc來(lái)移動(dòng)光標(biāo) - 最后通過(guò)
fsetpos來(lái)設(shè)置光標(biāo)位置
下面我們就來(lái)測(cè)試一下:

可以看到,函數(shù) fgetpos 與 fsetpos 二者配合起來(lái)使用,同樣能夠達(dá)到光標(biāo)定位與設(shè)置的效果。
現(xiàn)在問(wèn)題來(lái)了,這兩個(gè)函數(shù)與前面我們介紹的 ftell 和 fseek 之間有什么不同呢?
5.3 函數(shù)比較
5.3.1 相同點(diǎn)
不管是 ftell 與 fseek 的搭配使用,還是 fgetpos 與 fsetpos 的搭配使用,他們都是用來(lái)完成兩件事:
- 記錄光標(biāo)位置
- 設(shè)置光標(biāo)位置
5.3.2 不同點(diǎn)
這二者之間不同的地方在于—— 記錄光標(biāo)位置的方式不同:
ftell是通過(guò)long int類型的變量來(lái)記錄光標(biāo)的位置fgetpos則是通過(guò)fpos_t類型的變量來(lái)記錄光標(biāo)位置
因此,這就導(dǎo)致了二者能夠處理的文件大小有所區(qū)別:
- 當(dāng)光標(biāo)的位置值大于
long int時(shí),我們?cè)偻ㄟ^(guò)ftell來(lái)記錄光標(biāo)的位置就會(huì)出現(xiàn)問(wèn)題 - 由于
fgetpos是通過(guò)fpos_t來(lái)記錄,因此,我們不需要考慮光標(biāo)的位置是否會(huì)存在溢出的情況
也就是說(shuō),當(dāng)我們處理大文件時(shí),我們選擇使用 fgetpos 和 fsetpos 會(huì)更加保險(xiǎn)一點(diǎn)。
結(jié)語(yǔ)
通過(guò)今天的學(xué)習(xí),我們深入掌握了C語(yǔ)言中實(shí)現(xiàn)文件隨機(jī)讀寫的五個(gè)核心函數(shù)。讓我們簡(jiǎn)單回顧一下本篇的重要內(nèi)容:
函數(shù)功能總結(jié):
fseek:精確定位文件光標(biāo),支持三種參考位置(文件頭、當(dāng)前位置、文件尾)ftell:獲取當(dāng)前光標(biāo)位置,為fseek提供定位依據(jù)rewind:快速重置光標(biāo)到文件開頭,并清除錯(cuò)誤指示器fgetpos:使用fpos_t類型安全記錄光標(biāo)位置fsetpos:與fgetpos配合,精準(zhǔn)恢復(fù)光標(biāo)位置
關(guān)鍵區(qū)別:
ftell/fseek使用long類型,適合一般文件操作fgetpos/fsetpos使用fpos_t類型,更適合大文件處理
實(shí)踐價(jià)值:
掌握了這些函數(shù),我們就能在文件中自由"穿梭",實(shí)現(xiàn)高效的隨機(jī)訪問(wèn),為復(fù)雜文件操作打下堅(jiān)實(shí)基礎(chǔ)。
以上就是C語(yǔ)言文件隨機(jī)讀寫的完全指南的詳細(xì)內(nèi)容,更多關(guān)于C語(yǔ)言文件隨機(jī)讀寫的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
c++中的字節(jié)序與符號(hào)位的問(wèn)題
這篇文章主要介紹了c++中的字節(jié)序與符號(hào)位的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07
C#委托所蘊(yùn)含的函數(shù)指針概念詳細(xì)解析
C#中用委托這種概念實(shí)現(xiàn)了函數(shù)指針技術(shù)而已,另外.ent提供額外的安全性,當(dāng)然也損失了靈活性2013-09-09
使用VS2019編譯CEF2623項(xiàng)目的libcef_dll_wrapper.lib的方法
這篇文章主要介紹了使用VS2019編譯CEF2623項(xiàng)目的libcef_dll_wrapper.lib的方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-04-04
關(guān)于C++11中限定作用域的枚舉類型的問(wèn)題
C++中有兩種類型的枚舉:不限定作用域的枚舉類型和限定作用域的枚舉類型。限定作用域的枚舉類型是C++11標(biāo)準(zhǔn)引入的新類型,對(duì)C++11中限定作用域的枚舉類型相關(guān)知識(shí)感興趣的朋友一起看看吧2022-01-01
C語(yǔ)言實(shí)現(xiàn)控制臺(tái)五子棋小游戲
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)控制臺(tái)五子棋小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-07-07
簡(jiǎn)單總結(jié)C語(yǔ)言中各種類型的指針的概念
這篇文章主要簡(jiǎn)單總結(jié)了C語(yǔ)言中各種類型的指針的概念,指針可以說(shuō)是C語(yǔ)言本身所具有的最大特性,平時(shí)根據(jù)不同使用場(chǎng)合習(xí)慣地將其簡(jiǎn)單分類,需要的朋友可以參考下2016-03-03
VS?Code?C++?環(huán)境配置及?HelloWorld?程序(保姆教程)
文章詳細(xì)介紹了在Windows、macOS和Linux上配置VSCode的C++開發(fā)環(huán)境,并提供了創(chuàng)建、編譯、調(diào)試和使用CMake的步驟,還涵蓋了常見問(wèn)題的解決方法和快速模板項(xiàng)目的獲取,感興趣的朋友跟隨小編一起看看吧2025-12-12

