檔案操作
cache:是讀的緩衝區,讀內容先讀到cache中,是讀的加速機制
buffer:是寫的緩衝區,寫內容先寫到buff中,是寫的加速機制
對一個檔案的操作有兩種不同的方式,既可以使用由作業系統直接提供的程式設計介面
(API),即系統呼叫,也可以使用由標準C庫提供的標準IO函式。
系統IO
open
功能 | 開啟一個指定的檔案並獲得檔案描述符,或者建立一個新檔案 |
---|---|
標頭檔案 | <sys/types.h> <sys/stat.h> <fcntl.h> |
原型 | int open(const char *pathname, int flags); int open(const char *pathname, int flags, mode_t mode); |
引數 | - pathname :即將要開啟的檔案路徑- flags :- O_RDONLY :只讀方式開啟檔案- O_WRONLY :只寫方式開啟檔案- O_RDWR :讀寫方式開啟檔案- O_CREAT :如果檔案不存在,則建立該檔案- O_EXCL :如果使用 O_CREAT 選項且檔案存在,則返回錯誤訊息- O_NOCTTY :如果檔案為終端,那麼終端不可以作為呼叫 open() 系統呼叫的那個程序的控制終端- O_TRUNC :如檔案已經存在,則刪除檔案中原有資料- O_APPEND :以追加方式開啟檔案- mode :如果檔案被新建,指定其許可權為 mode (八進位制表示法) |
返回值 | 成功:大於等於0的整數(即檔案描述符) 失敗:-1 |
備註 | 無 |
** 示例程式碼**
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main()
{
int fd;
//以讀寫的方式建立新檔案
fd=open("myhello",O_RDWR | O_CREAT,0777);
//以讀寫的方式建立新檔案,並判斷是否存在
//fd=open("myhello",O_RDWR | O_CREAT | O_EXCL,0777);
//以讀寫的方式建立新檔案,若檔案存在則進行截斷(清空檔案內容)
//fd = open("myhello", O_RDWR | O_CREAT | O_TRUNC, 0777);
if (fd < 0)
{
printf("can not open file %s\n", argv[1]);
printf("errno = %d\n", errno);
printf("err: %s\n", strerror(errno));
perror("open");
}
else
{
printf("fd = %d\n", fd);
}
close(fd);
return 0;
}
close
功能 | 關閉一個開啟的檔案描述符 |
---|---|
標頭檔案 | <unistd.h> |
原型 | int close(int fd); |
引數 | fd :要關閉的檔案描述符 |
返回值 | 成功:0 失敗:-1 |
備註 | 重複關閉一個已經關閉的檔案或者尚未開啟的檔案是安全的 |
示例程式碼
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
int main(int argc, char *argv[])
{
int fd;
// 假設 fd 是已經開啟的檔案描述符
int ret = close(fd);
if (ret < 0)
{
printf("can not close file descriptor %d\n", fd);
printf("errno = %d\n", errno);
printf("err: %s\n", strerror(errno));
perror("close");
}
return 0;
}
read
功能 | 從指定檔案中讀取資料 |
---|---|
標頭檔案 | <unistd.h> |
原型 | ssize_t read(int fd, void *buf, size_t count); |
引數 | - fd :從檔案 fd 中讀取資料- buf :指向存放讀到的資料的緩衝區- count :想要從檔案 fd 中讀取的位元組數 |
返回值 | 成功:實際讀到的位元組數 失敗:-1 |
備註 | 實際讀到的位元組數小於等於 count |
- 如果返回值是0,說明讀到檔案末尾
write
功能 | 將資料寫入指定的檔案 |
---|---|
標頭檔案 | <unistd.h> |
原型 | ssize_t write(int fd, const void *buf, size_t count); |
引數 | - fd :將資料寫入到檔案 fd 中- buf :指向即將要寫入的資料- count :要寫入的位元組數 |
返回值 | 成功:實際寫入的位元組數 失敗:-1 |
備註 | 實際寫入的位元組數小於等於 count |
** 示例程式碼 **
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
int main() {
// 開啟檔案
int fd = open("example.txt", O_RDWR | O_CREAT, 0666);
if (fd < 0) {
perror("open");
exit(EXIT_FAILURE);
}
// 寫入資料
const char *message = "Hello, this is a test message.\n";
ssize_t bytes_written = write(fd, message, strlen(message));
if (bytes_written < 0) {
perror("write");
close(fd);
exit(EXIT_FAILURE);
}
// 讀取資料
char buffer[100];
lseek(fd, 0, SEEK_SET); // 將檔案指標移至檔案開頭
ssize_t bytes_read = read(fd, buffer, sizeof(buffer) - 1);
if (bytes_read < 0) {
perror("read");
close(fd);
exit(EXIT_FAILURE);
}
buffer[bytes_read] = '\0'; // 新增字串結束符
// 輸出讀取的資料
printf("Data read from file: %s", buffer);
// 關閉檔案
close(fd);
return 0;
}
- 這個程式的作用是開啟一個檔案,向檔案中寫入一條訊息,然後再讀取出來並列印。
dup dup2
功能 | 複製檔案描述符 |
---|---|
標頭檔案 | <unistd.h> |
原型 | int dup(int oldfd); int dup2(int oldfd, int newfd); |
引數 | - oldfd :要複製的檔案描述符- newfd :指定的新檔案描述符 |
返回值 | 成功:新的檔案描述符 失敗:-1 |
備註 | 無 |
dup
是英文單詞duplicate
的縮寫,意味著複製一個已有的檔案描述符,dup
將會返回一個最小未用的檔案描述符作為複製;
dup2
則可以透過第二個引數來指定描述符,如果這個描述符已經存在,則將會被覆蓋。
lseek
功能 | 調整檔案位置偏移量 |
---|---|
標頭檔案 | <sys/types.h> <unistd.h> |
原型 | off_t lseek(int fd, off_t offset, int whence); |
引數 | - fd :要調整位置偏移量的檔案的描述符- offset :新位置偏移量相對基準點的偏移- whence :基準點- SEEK_SET :檔案開頭處- SEEK_CUR :當前位置- SEEK_END :檔案末尾處 |
返回值 | 成功:新檔案位置偏移量 失敗:-1 |
備註 | 只對普通檔案有效,特殊檔案是無法調整偏移量的 |
** 示例程式碼 **
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
int main() {
// 開啟檔案
int fd = open("example.txt", O_RDWR);
if (fd < 0) {
perror("open");
exit(EXIT_FAILURE);
}
// 設定新的位置偏移量
off_t new_offset = lseek(fd, 0, SEEK_SET);
if (new_offset == -1) {
perror("lseek");
close(fd);
exit(EXIT_FAILURE);
}
// 關閉檔案
close(fd);
return 0;
}
ioctl fcntl
功能 | 檔案控制 |
---|---|
標頭檔案 | <sys/ioctl.h> |
原型 | int ioctl(int fd, int request, ...); |
引數 | - fd :要控制的檔案描述符- request :針對不同檔案的各種控制命令字- ... :根據不同的命令字而不同 |
返回值 | 成功:一般情況下是0,但有些特定的請求將返回非負整數 失敗:-1 |
備註 | 無 |
request
是一個由底層驅動提供的命令字, 一些通用的命令字被放置在標頭檔案/usr/include,(不同的系統存放位置也許不同)中,後面的變參也由前面的request命令字決定。
ioctl
是裝置驅動程式中裝置控制介面函式,一個字元裝置驅動通常會實現裝置開啟、關閉、讀、寫等功能,在一些需要細分的情況下,如果需要擴充套件新的功能,通常以增設 ioctl()
命令的方式實現。
ioctl
函式用於對裝置進行控制操作,它是一種通用的裝置 I/O 控制介面。透過 ioctl
函式,可以向裝置傳送控制命令,或者獲取裝置的狀態資訊。這些控制命令通常由預定義的常量表示,稱為 IOCTL 命令字。
使用 ioctl
函式可以實現很多裝置相關的功能,例如:
- 設定裝置引數:如串列埠的波特率、資料位、停止位等;
- 控制裝置行為:如開啟或關閉裝置的某些功能;
- 查詢裝置狀態:如獲取裝置的錯誤資訊、緩衝區狀態等。
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <linux/fb.h>
int main(int argc, char const *argv[])
{
//1.開啟LCD
int lcd_fd = open("/dev/fb0",O_RDWR);
//2.利用ioctl函式獲取LCD硬體引數
struct fb_var_screeninfo lcd_vinfo;
ioctl(lcd_fd,FBIOGET_VSCREENINFO,&lcd_vinfo);
//3.輸出LCD的寬、高、色深
printf("lcd height= %-5d\n", lcd_vinfo.yres); //480
printf("lcd width= %-5d\n", lcd_vinfo.xres); //800
printf("lcd bpp=%-5d\n",lcd_vinfo.bits_per_pixel); //32
return 0;
}
- 透過
ioctl(lcd_fd, FBIOGET_VSCREENINFO, &lcd_vinfo)
呼叫,將請求傳送給 LCD 裝置檔案描述符lcd_fd
,並將 LCD 裝置的硬體引數儲存在結構體lcd_vinfo
中。
fcntl
功能 | 檔案控制 |
---|---|
標頭檔案 | <unistd.h> <fcntl.h> |
原型 | int fcntl(int fd, int cmd, .../*arg*/); |
引數 | - fd :要控制的檔案描述符- cmd :控制命令字- .../*arg*/ :根據不同的命令字而不同 |
返回值 | 成功:根據不同的 cmd ,返回值不同失敗:-1 |
備註 | 無 |
fcntl
函式用於對檔案描述符進行各種控制操作,例如設定檔案狀態標誌、獲取檔案狀態標誌、複製檔案描述符等。其功能包括但不限於:
- 修改已開啟檔案的屬性,比如設定檔案的狀態標誌(例如設定非阻塞模式)、檔案描述符的標誌、檔案的讀寫鎖等。
- 複製檔案描述符,使兩個描述符指向相同的檔案。
- 獲取已開啟檔案的各種屬性資訊,如獲取檔案狀態標誌、獲取檔案的讀寫鎖資訊等。
這些操作可以透過 cmd
引數來指定。cmd
引數決定了 fcntl
函式的具體行為,例如 F_GETFL
用於獲取檔案狀態標誌,F_SETFL
用於設定檔案狀態標誌,F_DUPFD
用於複製檔案描述符等。
總的來說,fcntl
函式是一個靈活的介面,用於實現對檔案描述符的各種控制操作。
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main() {
// 開啟檔案
int fd = open("example.txt", O_RDONLY);
if (fd == -1) {
perror("open");
return 1;
}
// 獲取當前檔案狀態標誌
int flags = fcntl(fd, F_GETFL);
if (flags == -1) {
perror("fcntl");
return 1;
}
// 設定檔案狀態標誌為非阻塞模式
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
perror("fcntl");
return 1;
}
// 演示讀取檔案,此時設定了非阻塞模式
char buffer[1024];
ssize_t bytes_read = read(fd, buffer, sizeof(buffer));
if (bytes_read == -1) {
perror("read");
} else {
printf("Read %zd bytes: %s\n", bytes_read, buffer);
}
// 關閉檔案
close(fd);
return 0;
}
在這個示例中,我們首先使用 open
函式開啟一個檔案,然後使用 fcntl
函式獲取檔案的狀態標誌,接著設定檔案的狀態標誌為非阻塞模式,並最後讀取檔案。
mmap
功能 | 將檔案或其他物件對映到記憶體 |
---|---|
標頭檔案 | <sys/mman.h> |
原型 | void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); |
引數 | - addr :指定對映區域的首地址,通常設為 NULL ,讓核心選擇適當的地址- length :對映區域的長度- prot :保護對映區域的許可權- PROT_READ :允許讀取對映區域的內容- PROT_WRITE :允許寫入對映區域的內容- PROT_EXEC :允許執行對映區域的內容- PROT_NONE :拒絕對對映區域的訪問- flags :控制對映區域的屬性- MAP_SHARED :共享對映,對對映區域的修改會影響到檔案或其他對映該檔案的程序- MAP_PRIVATE :私有對映,對對映區域的修改不會影響到檔案或其他對映該檔案的程序- MAP_ANONYMOUS :建立匿名對映,不與檔案關聯,常用於建立匿名記憶體- fd :要對映的檔案描述符,如果對映的是匿名記憶體,則為 -1 - offset :對映檔案的偏移量,通常設為 0 |
返回值 | 成功:對映區域的起始地址 失敗: MAP_FAILED --->(void*)-1 |
備註 | - 如果對映的是檔案,則 fd 引數指定檔案描述符,offset 引數指定檔案的偏移量;如果對映的是匿名記憶體,則 fd 引數為 -1 ,offset 引數無效。- 對映區域的長度必須是頁面大小的整數倍,通常使用 sysconf(_SC_PAGE_SIZE) 獲取頁面大小。 |
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main() {
// 開啟檔案
int fd = open("example.txt", O_RDONLY);
if (fd == -1) {
perror("open");
return 1;
}
// 獲取檔案大小
struct stat file_stat;
if (fstat(fd, &file_stat) == -1) {
perror("fstat");
close(fd);
return 1;
}
off_t file_size = file_stat.st_size;
// 對映檔案到記憶體
void *addr = mmap(NULL, file_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (addr == MAP_FAILED) {
perror("mmap");
close(fd);
return 1;
}
// 讀取對映區域的內容
printf("Mapped content: %s\n", (char *)addr);
// 解除記憶體對映
if (munmap(addr, file_size) == -1) {
perror("munmap");
close(fd);
return 1;
}
// 關閉檔案
close(fd);
return 0;
}
標準IO
fopen
函式 | fopen |
---|---|
功能 | 開啟一個檔案 |
標頭檔案 | <stdio.h> |
原型 | FILE *fopen(const char *path, const char *mode); |
引數 | - path :即將要開啟的檔案路徑名- mode :- "r" : 以只讀方式開啟檔案,要求檔案必須存在。- "r+" : 以讀寫方式開啟檔案,要求檔案必須存在。- "w" : 以只寫方式開啟檔案,檔案如果不存在將會建立新檔案,如果存在將會將其內容清空。- "w+" : 以讀寫方式開啟檔案,檔案如果不存在將會建立新檔案,如果存在將會將其內容清空。- "a" : 以只寫方式開啟檔案,檔案如果不存在將會建立新檔案,且檔案位置偏移量被自動定位到檔案末尾(即以追加方式寫資料)。- "a+" : 以讀寫方式開啟檔案,檔案如果不存在將會建立新檔案,且檔案位置偏移量被自動定位到檔案末尾(即以追加方式寫資料)。 |
返回值 | 成功:FILE * ----> 檔案指標 失敗: NULL |
備註 | 最好要以二進位制檔案開啟rb``wb 使用標準IO的時候,是不可以反覆關閉相同的檔案,因為釋放已經被釋放的堆記憶體,會導致段錯誤! |
思考:fopen函式的返回值是一個指向被開啟檔案的FILE型別的指標,請問FILE型別是什麼?
回答:FILE型別其實是一個結構體資料型別,它包含了標準 I/O 庫函式為管理檔案所需要的所有資訊,比如包括用於實際I/O 的檔案描述符、指向檔案緩衝區的指標、緩衝區的長度、當前緩衝區中的位元組數以及出錯標誌等。標頭檔案stdio.h中有關於FILE型別的相關描述,
fclose
函式 | fclose |
---|---|
功能 | 關閉一個檔案 |
標頭檔案 | <stdio.h> |
原型 | int fclose(FILE *stream); |
引數 | stream :要關閉的檔案流指標 |
返回值 | 成功:0 失敗:非 0 值 |
備註 | 不能對同一個檔案重複關閉 |
-
使用標準IO函式處理檔案的最大特點是,資料將會先儲存在一個標準IO 緩衝區中,而後在一定條件下才被一併flush(沖洗,或稱重新整理)至核心緩衝區,而不是像系統IO那樣,資料直接被flush至核心。 注意到,標準IO函式
fopen()
實質上是系統IO函式open()
的封裝,他們是一一對應的,每一次fopen()
都會導致系統分配一個file{ }
結構體和一個FILE{}
來儲存維護該檔案的讀寫資訊,每一次的開啟和操作都可以不一樣,是相對獨立的,因此可以在多執行緒或者多程序中多次開啟同一個檔案,再利用檔案空洞技術進行多點讀寫。 -
預設開啟的三個標準檔案,標準輸出入輸出裝置在系統IO是預設被開啟的,在標準IO也是一樣,在程式開始就已經擁有相應的檔案指標
裝置 | 檔案描述符(int) | 檔案指標(FILE *) | 標準輸入裝置(鍵盤) |
---|---|---|---|
標準輸入裝置 | 0 | STDIN_FILENO | stdin |
標準輸出裝置 | 1 | STDOUT_FILENO | stdout |
標準出錯裝置 | 2 | STDERR_FILENO | stderr |
每次一個字元的讀寫標準IO函式介面
fgetc getc getchar
功能 | 獲取指定檔案的一個字元 |
---|---|
標頭檔案 | <stdio.h> |
原型 | int fgetc(FILE *stream); |
int getc(FILE *stream); |
|
int getchar(void); |
|
引數 | stream :檔案指標 |
返回值 | 成功讀取到的字元 |
失敗:EOF | |
備註 | 當返回EOF時,檔案stream可能已達末尾,或者遇到錯誤 |
fputc putc putchar
功能 | 將一個字元寫入一個指定的檔案 |
---|---|
標頭檔案 | <stdio.h> |
原型 | int fputc(int c, FILE *stream); |
int putc(int c, FILE *stream); |
|
int putchar(int c); |
|
引數 | c :要寫入的字元 |
stream :寫入的檔案指標 |
|
返回值 | 成功寫入到的字元 |
失敗:EOF | |
備註 | 無 |
1,fgec()、getc()和getchar()返回值是int,而不是char,原因是因為他們在出錯或者讀到檔案末尾的時候需要返回一個值為-1的EOF標記,而char型資料有可能因 為系統的差異而無法表示負整數。
2,當fgec()、getc()和getchar()返回EOF時,有可能是發生了錯誤,也有可能是讀到了檔案末尾,需要用 feof(),ferror()來判斷。
feof ferror
功能 | 判斷一個檔案是否到達檔案末尾 |
---|---|
標頭檔案 | <stdio.h> |
原型 | int feof(FILE *stream); |
int ferror(FILE *stream); |
|
引數 | stream :進行判斷的檔案指標 |
返回值 | feof :如果檔案已達末尾則返回真,否則返回假 |
ferror :如果檔案遇到錯誤則返回真,否則返回假 |
|
備註 | 無 |
每次一行字元的讀寫標準IO函式介面
fgetc gets
功能 | 從指定檔案讀取最多一行資料 |
---|---|
標頭檔案 | <stdio.h> |
原型 | char *fgets(char *s, int size, FILE *stream); |
char *gets(char *s); |
|
引數 | s :自定義緩衝區指標 |
size :自定義緩衝區大小 |
|
stream :即將被讀取資料的檔案指標 |
|
返回值 | 成功:自定義緩衝區指標 s |
失敗:NULL |
|
備註 | 1. gets() 預設從檔案 stdin 讀入資料 |
2. 當返回 NULL 時,檔案 stream 可能已達末尾,或者遇到錯誤 |
fputs puts
功能 | 將資料寫入指定的檔案 |
---|---|
標頭檔案 | <stdio.h> |
原型 | int fputs(const char *s, FILE *stream); |
int puts(const char *s); |
|
引數 | s :自定義緩衝區指標 |
stream :即將被寫入資料的檔案指標 |
|
返回值 | 成功:非負整數 |
失敗:EOF |
|
備註 | puts() 預設將資料寫入檔案 stdout |
fgets()
跟fgetc()
一樣,當其返回NULL時並不能確定究竟是達到檔案末尾還是 碰到錯誤,需要用feof()
/ferror()
來進一步判斷。fgets()
每次讀取至多不超過size個位元組的一行,所謂“一行”即資料至多包含一 個換行符’\n’。gets()
是一個已經過時的介面,因為他沒有指定自定義緩衝區s的大小,這樣很容 易造成緩衝區溢位,導致程式段訪問錯誤。fgets()
和fputs()
,gets()
和puts()
一般成對使用,鑑於gets()的不安全性, 一般建議使用前者。
** 每次讀寫若干資料塊的標準IO函式介面 **
fread fwrite
功能 | 從指定檔案讀取若干個資料塊 |
---|---|
標頭檔案 | <stdio.h> |
原型 | size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); |
引數 | ptr :自定義緩衝區指標 |
size :資料塊大小 |
|
nmemb :資料塊個數 |
|
stream :即將被讀取資料的檔案指標 |
|
返回值 | 成功:讀取的資料塊個數,等於 nmemb |
失敗:讀取的資料塊個數,小於 nmemb 或等於 0 |
|
備註 | 當返回小於 nmemb 時,檔案 stream 可能已達末尾,或者遇到錯誤 |
功能 | 將若干塊資料寫入指定的檔案 |
---|---|
標頭檔案 | <stdio.h> |
原型 | size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); |
引數 | ptr :自定義緩衝區指標 |
size :資料塊大小 |
|
nmemb :資料塊個數 |
|
stream :即將被寫入資料的檔案指標 |
|
返回值 | 成功:寫入的資料塊個數,等於 nmemb |
失敗:寫入的資料塊個數,小於 nmemb 或等於 0 |
|
備註 | 無 |
- 如果
fread()
返回值小於nmemb
時,則可能已達末尾,或者遇到錯誤,需要借於feof()
/ferror()
來加以進一步判斷。- 當發生上述第1種情況時,其返回值並不能真正反映其讀取或者寫入的資料塊數, 而只是一個所謂的“截短值”,比如正常讀取5個資料塊,每個資料塊100個位元組,在執 行成功的情況下返回值是5,表示讀到5個資料塊總共500個位元組,但是如果只讀到499 個資料塊,那麼返回值就變成4,而如果讀到99個位元組,那麼
fread()
會返回0。因此當 發生返回值小於nmemb
時,需要仔細確定究竟讀取了幾個位元組,而不能直接從返回值確定。
** 獲取或設定檔案當前位置偏移量 **
fseek ftell rewind
功能 | 設定指定檔案的當前位置偏移量 |
---|---|
標頭檔案 | <stdio.h> |
原型 | int fseek(FILE *stream, long offset, int whence); |
引數 | stream :需要設定位置偏移量的檔案指標 |
offset :新位置偏移量相對基準點的偏移 |
|
whence :基準點 |
|
- SEEK_SET :檔案開頭處 |
|
- SEEK_CUR :當前位置 |
|
- SEEK_END :檔案末尾處 |
|
返回值 | 成功:0 |
失敗:-1 | |
備註 | 無 |
功能 | 獲取指定檔案的當前位置偏移量 |
---|---|
標頭檔案 | <stdio.h> |
原型 | long ftell(FILE *stream); |
引數 | stream :需要返回當前檔案位置偏移量的檔案指標 |
返回值 | 成功:當前檔案位置偏移量 |
失敗:-1 | |
備註 | 無 |
功能 | 將指定檔案的當前位置偏移量設定到檔案開頭處 |
---|---|
標頭檔案 | <stdio.h> |
原型 | void rewind(FILE *stream); |
引數 | stream :需要設定位置偏移量的檔案指標 |
返回值 | 無 |
備註 | 該函式的功能是將檔案 stream 的位置偏移量置位到檔案開頭處 |
1,
fseek()
的用法基本上跟系統IO的lseek()
是一致的。2,
rewind(fp)
相等於fseek(fp,0L,SEEK_SE)
;
** 標準格式化IO函式 **
fprintf printf snprintf sprintf
功能 | 將格式化資料寫入指定的檔案或者記憶體 |
---|---|
標頭檔案 | <stdio.h> |
原型 | int fprintf(FILE *restrict stream, const char *restrict format, ...); |
int printf(const char *restrict format, ...); |
|
int snprintf(char *restrict s, size_t n, const char *restrict format, ...); |
|
int sprintf(char *restrict s, const char *restrict format, ...); |
|
引數 | stream :寫入資料的檔案指標 |
format :格式控制串 |
|
s :寫入資料的自定義緩衝區 |
|
n :自定義緩衝區的大小 |
|
返回值 | 成功:成功寫入的位元組數 |
失敗:-1 | |
備註 | 無 |
fscanf scanf sscanf
功能 | 從指定的檔案或者記憶體中讀取格式化資料 |
---|---|
標頭檔案 | <stdio.h> |
原型 | int fscanf(FILE *restrict stream, const char *restrict format, ...); |
int scanf(const char *restrict format, ...); |
|
int sscanf(const char *restrict s, const char *restrict format, ...); |
|
引數 | stream :讀出資料的檔案指標 |
format :格式控制串 |
|
s :讀出資料的自定義緩衝區 |
|
返回值 | 成功:正確匹配且賦值的資料個數 |
失敗:EOF | |
備註 | 無 |
1,
fprintf()
不僅可以像printf()
一樣向標準輸出裝置輸出資訊,也可以向由stream
指定的任何有相應許可權的檔案寫入資料。
2,sprintf()
和snprintf()
都是向一塊自定義緩衝區寫入資料,不同的是後者第二個參
數提供了這塊緩衝區的大小,避免緩衝區溢位,因此應儘量使用後者,放棄使用前者。
3,fscanf()
不僅可以像scanf()
一樣從標準輸入裝置讀入資訊,也可以從由stream
指定的任何有相應許可權的檔案讀入資料。
4,sscanf()
從一塊由s指定的自定義緩衝區中讀入資料。
5,最重要的一條:這些函式的讀寫都是帶格式的,這些所謂的格式由下表規定:
格式控制符
格式控制符 | 含義 | 範例 |
---|---|---|
%d |
有符號十進位制整型數 | int a = 1; printf("%d", a); |
%u |
無符號十進位制整型數 | int a = 1; printf("%u", a); |
%o |
無符號八進位制整型數 | int a = 1; printf("%o", a); |
%x |
無符號十六進位制整型數 | int a = 1; printf("%x", a); |
%c |
字元 | char a = 'd'; printf("%c", a); |
%s |
字串 | char *a = "xy"; printf("%s", a); |
%f |
計數法單精度浮點數 | float a = 1.0; printf("%f", a); |
%e |
科學技術法單精度浮點數 | float a = 1.0; printf("%e", a); |
%p |
指標 | int *a; printf("%p", a); |
%.5s |
取字串的前5個字元 | char *a = "abcdefghijk"; printf("%.5s", a); |
%.5f |
取單精度浮點數小數點後5位小數 | float a = 1.0; printf("%.5f", a); |
%5d |
位寬至少為5個字元,右對齊 | int a = 1; printf("%5d", a); |
%-5d |
位寬至少為5個字元,左對齊 | int a = 1; printf("%-5d", a); |
%hd |
半個有符號數十進位制整型數 | short a = 1; printf("%hd", a); |
%hhd |
半半個有符號數十進位制整型數 | char a = 1; printf("%hhd", a); |
%lf |
雙精度浮點數 | double a = 1.0; printf("%lf", a); |
%Le |
科學技術法雙精度浮點數 | double a = 1.0; printf("%Le", a); |
%Lf |
長雙精度浮點數 | long double a = 1.0; printf("%Lf", a); |
%Le |
科學技術法長雙精度浮點數 | long double a = 1.0; printf("%Le", a); |
** 示例程式碼 **
- 注意,這一組函式跟之前的標準IO最大的區別是帶有格式控制,因此最適用於有格式 的檔案處理,假設有一個檔案儲存了班級學生的姓名、性別、年齡和身高,如下:
很明顯這個檔案是帶有格式的,假如我們需要將這個檔案讀入程式,再將之列印到螢幕
顯示出來,可以使用fscanf()
和fprintf()
來實現:
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<unistd.h>
#include<string.h>
#include<strings.h>
#include<errno.h>
#include<sys/stat.h>
#include<sys/types.h>
#define NAMELEN 20
// 學生結構體
struct student {
char name[NAMELEN];
char sex;
int age;
float stature;
struct student *next; // 用以形成連結串列
};
// 初始化連結串列
struct student *init_list(void) {
struct student *head = malloc(sizeof(struct student));
head->next = NULL;
return head;
}
// 向連結串列中新增學生節點
void add_student(struct student *head, struct student *new) {
struct student *tmp = head;
while (tmp->next != NULL)
tmp = tmp->next;
tmp->next = new;
}
// 顯示連結串列中的所有學生資訊
void show_student(struct student *head) {
struct student *tmp = head->next;
while (tmp != NULL) {
fprintf(stdout, "%-5s %c %d %.1f\n",
tmp->name, tmp->sex, tmp->age, tmp->stature);
tmp = tmp->next;
}
}
int main(int argc, char **argv) {
// 開啟檔案
FILE *fp = fopen("format_data", "r");
if (fp == NULL) {
perror("Error opening file");
return errno;
}
// 初始化連結串列
struct student *head = init_list();
int count = 0;
// 從檔案中讀取資料並新增到連結串列中
while (1) {
struct student *new = malloc(sizeof(struct student));
if (fscanf(fp, "%s %c %d %f", new->name, &(new->sex), &(new->age), &(new->stature)) == EOF) {
break;
}
add_student(head, new);
count++;
}
// 輸出新增的學生數
printf("%d students have been added.\n", count);
// 顯示所有學生資訊
show_student(head);
// 關閉檔案
fclose(fp);
return 0;
}
** 檔案屬性 **
--- 待補齊