Linux應用編程基本IO操作
- 一、main 函數
- 1、main 函數寫法之無傳參
- 2、main 函數寫法之有傳參
- 二、open 打開文件
- 三、write 寫文件
- 四、read 讀文件
- 五、close 關閉文件
- 六、 lseek
- 七、 返回錯誤處理與 errno
- 7.1 strerror 函數
- 7.2 perror 函數
- 八、 exit、_exit、_Exit
- 8.1_exit()和_Exit()函數
- 8.2 exit()函數
- 九、 格式化 I/O
- 9.1 格式化輸出
- printf()函數
- fprintf()函數
- dprintf()函數
- sprintf()函數
- snprintf()函數
- 9.2 格式化輸入
- scanf()函數
- fscanf()函數
- sscanf()函數
一、main 函數
1、main 函數寫法之無傳參
int main(void) {/* 代碼 */
}
2、main 函數寫法之有傳參
int main(int argc, char **argv) {/* 代碼 */
}
二、open 打開文件
在 Linux 系統中要操作一個文件,需要先打開該文件,得到文件描述符,然后再對文件進行相應的讀寫操作(或其他操作),最后在關閉該文件;open 函數用于打開文件,當然除了打開已經存在的文件之外,還可以創建一個新的文件,函數原型如下所示:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
open 函數使用示例
(1)使用 open 函數打開一個已經存在的文件(例如當前目錄下的 app.c 文件),使用只讀方式打開:
int fd = open("./app.c", O_RDONLY)
if (-1 == fd)
return fd;
(2)使用 open 函數打開一個已經存在的文件(例如當前目錄下的 app.c 文件),使用可讀可寫方式打開:
int fd = open("./app.c", O_RDWR)
if (-1 == fd)
return fd;
(3)使用 open 函數打開一個指定的文件(譬如/home/dengtao/hello),使用可讀可寫方式,如果該文件是
一個符號鏈接文件,則不對其進行解引用,直接返回錯誤
int fd = open("/home/dengtao/hello", O_RDWR | O_NOFOLLOW);
if (-1 == fd)
return fd;
(4)使用 open 函數打開一個指定的文件(譬如/home/dengtao/hello),如果該文件不存在則創建該文件,
創建該文件時,將文件權限設置如下:
文件所屬者擁有讀、寫、執行權限;
同組用戶與其他用戶只有讀權限。
使用可讀可寫方式打開:
int fd = open("/home/dengtao/hello", O_RDWR | O_CREAT, S_IRWXU | S_IRGRP | S_IROTH);
if (-1 == fd)
return fd;
三、write 寫文件
調用 write 函數可向打開的文件寫入數據,其函數原型如下所示(可通過"man 2 write"查看):
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
四、read 讀文件
調用 read 函數可從打開的文件中讀取數據,其函數原型如下所示(可通過"man 2 read"查看):
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
五、close 關閉文件
可調用 close 函數關閉一個已經打開的文件,其函數原型如下所示(可通過"man 2 close"查看):
#include <unistd.h>
int close(int fd);
六、 lseek
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
使用示例:
(1)將讀寫位置移動到文件開頭處:
off_t off = lseek(fd, 0, SEEK_SET);
if (-1 == off)
return -1;
(2)將讀寫位置移動到文件末尾:
off_t off = lseek(fd, 0, SEEK_END);
if (-1 == off)
return -1;
(3)將讀寫位置移動到偏移文件開頭 100 個字節處:
off_t off = lseek(fd, 100, SEEK_SET);
if (-1 == off)
return -1;
(4)獲取當前讀寫位置偏移量:
off_t off = lseek(fd, 0, SEEK_CUR);
if (-1 == off)
return -1;
七、 返回錯誤處理與 errno
7.1 strerror 函數
前面給大家說到了 errno 變量,但是 errno 僅僅只是一個錯誤編號,對于開發者來說,即使拿到了 errno也不知道錯誤為何?還需要對比源碼中對此編號的錯誤定義,可以說非常不友好,這里介紹一個 C 庫函數strerror(),該函數可以將對應的 errno 轉換成適合我們查看的字符串信息,其函數原型如下所示(可通過"man 3 strerror"命令查看,注意此函數是 C 庫函數,并不是系統調用):
#include <string.h>
char *strerror(int errnum);
接下來我們測試下,測試代碼如下:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main(void)
{
int fd;
/* 打開文件 */
fd = open("./test_file", O_RDONLY);
if (-1 == fd) {
printf("Error: %s\n", strerror(errno));
return -1;
}
close(fd);
return 0;
}
7.2 perror 函數
除了 strerror 函數之外,我們還可以使用 perror 函數來查看錯誤信息,一般用的最多的還是這個函數,調用此函數不需要傳入 errno,函數內部會自己去獲取 errno 變量的值,調用此函數會直接將錯誤提示字符串打印出來,而不是返回字符串,除此之外還可以在輸出的錯誤提示字符串之前加入自己的打印信息,函數原型如下所示(可通過"man 3 perror"命令查看):
#include <stdio.h>
void perror(const char *s);
接下來我們進行測試,測試代碼如下所示:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main(void)
{
int fd;
/* 打開文件 */
fd = open("./test_file", O_RDONLY);
if (-1 == fd) {
perror("open error");
return -1;
}
close(fd);
return 0;
}
八、 exit、_exit、_Exit
8.1_exit()和_Exit()函數
main 函數中使用 return 后返回,return 執行后把控制權交給調用函數,結束該進程。調用_exit()函數會清除其使用的內存空間,并銷毀其在內核中的各種數據結構,關閉進程的所有文件描述符,并結束進程、將控制權交給操作系統。_exit()函數原型如下所示:
#include <unistd.h>
void _exit(int status);
調用函數需要傳入 status 狀態標志,0 表示正常結束、若為其它值則表示程序執行過程中檢測到有錯誤發生。使用示例如下:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main(void)
{
int fd;
/* 打開文件 */
fd = open("./test_file", O_RDONLY);
if (-1 == fd) {
perror("open error");
_exit(-1);
}
close(fd);
_exit(0); }
_Exit()函數原型如下所示:
#include <stdlib.h>
void _Exit(int status);
_exit()和_Exit()兩者等價,用法作用是一樣的,這里就不再講了,需要注意的是這 2 個函數都是系統調用。
8.2 exit()函數
exit()函數_exit()函數都是用來終止進程的,exit()是一個標準 C 庫函數,而_exit()和_Exit()是系統調用。執行 exit()會執行一些清理工作,最后調用_exit()函數。exit()函數原型如下:
#include <stdlib.h>
void exit(int status);
九、 格式化 I/O
在前面編寫的測試代碼中,會經常使用到庫函數 printf()用于輸出程序中的打印信息,printf()函數可將格式化數據寫入到標準輸出,所以通常稱為格式化輸出。除了 printf()之外,格式化輸出還包括:fprintf()、dprintf()、sprintf()、snprintf()這 4 個庫函數。
除了格式化輸出之外,自然也有格式化輸入,從標準輸入中獲取格式化數據,格式化輸入包括:scanf()、fscanf()、sscanf()這三個庫函數,那么本小節將向大家介紹 C 語言庫函數的格式化 I/O。
9.1 格式化輸出
C 庫函數提供了 5 個格式化輸出函數,包括:printf()、fprintf()、dprintf()、sprintf()、snprintf(),其函數定義如下所示:
#include <stdio.h>
int printf(const char *format, ...);
int fprintf(FILE *stream, const char *format, ...);
int dprintf(int fd, const char *format, ...);
int sprintf(char *buf, const char *format, ...);
int snprintf(char *buf, size_t size, const char *format, ...);
printf()函數
前面章節內容編寫的示例代碼中多次使用了該函數,用于將程序中的字符串信息輸出顯示到終端(也就是標準輸出),相信各位讀者學習 C 語言時肯定用過該函數,它是一個可變參函數,除了一個固定參數 format外,后面還可攜帶 0 個或多個參數。
函數調用成功返回打印輸出的字符數;失敗將返回一個負值!
打印“Hello World”:
printf("Hello World!\n");
打印數字 5:
printf("%d\n", 5);
fprintf()函數
fprintf()可將格式化數據寫入到由 FILE 指針指定的文件中,譬如將字符串“Hello World”寫入到標準錯誤:
fprintf(stderr, "Hello World!\n");
向標準錯誤寫入數字 5:
fprintf(stderr, "%d\n", 5);
函數調用成功返回寫入到文件中的字符數;失敗將返回一個負值!
dprintf()函數
dprintf()可將格式化數據寫入到由文件描述符 fd 指定的文件中,譬如將字符串“Hello World”寫入到標準錯誤:
dprintf(STDERR_FILENO, "Hello World!\n");
向標準錯誤寫入數字 5:
dprintf(STDERR_FILENO, "%d\n", 5);
函數調用成功返回寫入到文件中的字符數;失敗將返回一個負值!
sprintf()函數
sprintf()函數將格式化數據存儲在由參數 buf 所指定的緩沖區中,譬如將字符串“Hello World”存放在緩沖區中:
char buf[100];
sprintf(buf, "Hello World!\n");
當然這種用法并沒有意義,事實上,我們一般會使用這個函數進行格式化轉換,并將轉換后的字符串存放在緩沖區中,譬如將數字 100 轉換為字符串"100",將轉換后得到的字符串存放在 buf 中:
char buf[20] = {0};
sprintf(buf, "%d", 100);
sprintf()函數會在字符串尾端自動加上一個字符串終止字符’\0’。
snprintf()函數
sprintf()函數可能會發生緩沖區溢出的問題,存在安全隱患,為了解決這個問題,引入了 snprintf()函數;在該函數中,使用參數 size 顯式的指定緩沖區的大小,如果寫入到緩沖區的字節數大于參數 size 指定的大小,超出的部分將會被丟棄!如果緩沖區空間足夠大,snprintf()函數就會返回寫入到緩沖區的字符數,與sprintf()函數相同,也會在字符串末尾自動添加終止字符’\0’。
9.2 格式化輸入
C 庫函數提供了 3 個格式化輸入函數,包括:scanf()、fscanf()、sscanf(),其函數定義如下所示:
#include <stdio.h>
int scanf(const char *format, ...);
int fscanf(FILE *stream, const char *format, ...);
int sscanf(const char *str, const char *format, ...);
scanf()函數
int a, b, c;
scanf("%d %d %d", &a, &b, &c);
fscanf()函數
int a, b, c;
fscanf(stdin, "%d %d %d", &a, &b, &c);
sscanf()函數
char *str = "5454 hello";
char buf[10];
int a;
sscanf(str, "%d %s", &a, buf);