Linux編程學習筆記 | Linux IO學習[2] - 標準IO

在上一篇Linux編程學習筆記 | Linux IO學習[1] - 文件IO中,我總結了Linux下的文件IO。文件IO是偏底層的IO操做,在平時的平常工做中,使用文件IO的頻率仍是比較低的。咱們天天使用的 printf() 就不是文件IO,而是另外一類IO - 標準IO。在這篇文章中,我將介紹Linux下的標準IO並經過實例來講明如何使用它們。linux

標準IO庫

要使用標準IO庫,須要包含頭文件 <stdio.h> 。該庫爲用戶建立了一個鏈接底層系統調用的通用接口,它是ANSI C標準制定的庫,所以具備可移植性(文件IO是基於Unix的POSIX標準,不可移植到Windows)。同文件IO相似,它須要先打開一個文件以創建一個訪問途徑,文件IO的訪問途徑是經過文件描述符,標準IO的訪問途徑是流(stream),它被實現爲指向結構FILE的指針。git

同文件IO相似,在程序啓動時也有3個默認的文件流:github

標準流 變量或宏 說明
0 stdin 標準輸入
1 stdout 標準輸出
2 stderr 標準錯誤輸出

標準IO基本操做

標準IO的函數相對文件IO來講要多不少,我這裏主要介紹13個標準IO函數。編程

打開/建立文件流

fopen() 和文件IO中的 open() 相似,用於打開文件流,函數說明以下:segmentfault

FILE *fopen(const char *restrict pathname, const char *restrict mode);

args:
    const char *restrict pathname: 文件的路徑
    const char *restrict mode    : 文件打開的模式
                                    
return:
    返回指向文件的指針,指針不爲NULL是成功,指針爲NULL是失敗

文件打開的模式有如下6種:函數

1. "r" or "rb"            : 以只讀形式打開文(文件必須存在)
2. "w" or "wb"            : 以寫方式打開文件並將文件長度截爲0或建立一個供寫的文件 
3. "a" or "ab"            : 以寫方式打開文件並將內容寫到文件末或建立一個文件
4. "r+" or "rb+" or "r+b" : 以更新的方式(讀/寫)打開文件(文件必須存在)
5. "w+" or "wb+" or "w+b" : 以更新的方式(讀/寫)打開文件並將文件長度截爲0或建立一個文件
6. "a+" or "ab+" or "a+b" : 以更新的方式(讀/寫)打開文件並將更新內容寫到文件末或建立一個文件

fopen()open() 不一樣, fopen() 並不能在建立文件時改變其訪問權限。學習

關閉文件流

fclose() 和文件IO中的 close() 相似,用於關閉文件流,函數說明以下:指針

int fclose(FILE *stream);

args:
    FILE *stream: 指向被關閉文件的指針 

return:
    關閉文件成功返回0,關閉文件失敗返回而EOF

咱們來看第一個例子,文件的打開和關閉:rest

#include <stdio.h>

int main(int argc, char *argv[])
{
    FILE *fp;

    //fp = fopen("stdio.log", "r+");
    fp = fopen("stdio.log", "w+");
    if (fp == NULL) {
        printf("File create fail...\n");
        return -1; 
    } else {
        printf("File create success...\n");
    }
    
    fclose(fp);   

    return 0; 
}

運行結果:
新建一個叫 stdio.log 的文件,並輸出
File create success...
若是咱們註釋掉第8行,去掉第7行的註釋,那麼將輸出
File create fail...code

修改文件流讀寫偏移量

fseek() 和文件IO中的 lseek() 相似,用於修改文件流讀寫的偏移量,函數說明以下:

int fseek(FILE *stream, long offset, int whence);

args:
    FILE *stream: 指向文件的文件指針
    long offset : 偏移量移動的距離
    int whence  : 偏移量的基址
                    - SEEK_SET 文件開始處
                    - SEEK_CUR 文件當前位置
                    - SEEK_END 文件結束處

return:
    修改偏移量成功返回0, 修改偏移量失敗返回-1

whenceSEEK_CURSEEK_END 時, offset 可正負。

寫文件流

fwrite()

fwrite() 和文件IO中的 write() 相似,函數說明以下:

size_t fwrite(const void *restrict ptr, size_t size, size_t nitems, FILE *restrict stream);

args:
    const void *restrict ptr: 寫入數據在內存空間存儲的地址
    size_t size             : 單個元素的大小
    size_t nitems           : 寫入數據元素的個數
    FILE *restrict stream   : 指向寫入文件的文件指針 

return:
    實際寫入的元素個數,非負整數是成功,-1是失敗

fputs()

fputs() 將字符串(不包括 `\0` )寫入文件,函數說明以下:

int fputs(const char *restrict s, FILE *restrict stream);

args:
    const char *restrict s: 寫入的字符串
    FILE *restrict stream : 指向寫入文件的文件指針  

return:
    寫入文件的狀態,非負整數是成功,EOF是失敗

puts()

puts() 將字符串(不包括 `\0` )寫入 stdout ,並在行末添加一個換行符,函數說明以下:

int puts(const char *s);

args:
    const char *s: 寫入的字符串

return:
    寫出到stdio的狀態,非負整數是成功,EOF是失敗

fputc()

fputc() 將一個字符寫入文件,函數說明以下:

int fputc(int c, FILE *stream);

args:
    int char    : 要寫入的字符
    FILE *stream: 指向寫入文件的文件指針 

return:
    若是沒有錯誤,返回寫入的字符,不然返回EOF

putc()

putc()fputc() 基本同樣,只不過 putc() 是用宏實現而 fputc 是用函數實現。

int putc(int c, FILE *stream);

args:
    int c       : 要寫入的字符
    FILE *stream: 指向寫入文件的文件指針 

return:
    若是沒有錯誤,返回寫入的字符,不然返回EOF

咱們經過例子來看看上面這幾個函數的使用方法:

#include <stdio.h>

int main(int argc, char *argv[])
{
    FILE *fp;

    fp = fopen("stdio.log", "w+");
    if (fp == NULL) {
        printf("File create fail...\n");
        return -1; 
    } else {
        printf("File create success...\n");
    }
    
    /* fwrite() function */ 
    char buffer_1[] = "This is fwrite DEMO..."; 
    size_t wr_size = 0; 
    wr_size = fwrite(buffer_1, 1, sizeof(buffer_1), fp); 
    printf("wr_size = %d\n", wr_size); 

    /* fputs() function */ 
    char buffer_2[] = "\nThis is fputs DEMO...\n"; 
    int fputs_status = 0; 
    fputs_status = fputs(buffer_2, fp); 
    printf("fputs_status = %d\n", wr_size); 
    
    /* puts function */
    char buffer_3[] = "This is puts DEMO..."; 
    puts(buffer_3);

    /* fputc function */
    char buffer_4[] = "This is fputc DEMO...\n";
    int ret;
    for (int i = 0; i < sizeof(buffer_4); i++) {
        ret = fputc(buffer_4[i], fp);
        printf("%c", ret);
    }

    /* putc function */
    char buffer_5[] = "This is putc DEMO...\n";
    for (int i = 0; i < sizeof(buffer_5); i++) {
        ret = fputc(buffer_5[i], fp);
        printf("%c", ret);
    }
    
    fclose(fp);   

    return 0; 
}

運行結果:
在生成的 std_io.log 文件中會輸出如下內容,其中 @^ 就是 `\0`

This is fwrite DEMO...^@
This is fputs DEMO...
This is fputc DEMO...
^@This is putc DEMO...
^@

注意 fputs 函數並無輸出 `\0` 。在終端會輸出:

File create success...
wr_size = 23
fputs_status = 23
This is puts DEMO...
This is fputc DEMO...
This is putc DEMO...

puts 函數直接將字符串輸出到 stdio

讀文件流

fread()

fread() 和文件IO中的 read() 相似,函數說明以下:

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

args:
    void *ptr   : 讀取數據存儲的內存空間的地址  
    size_t size : 單個元素的大小
    size_t nmemb: 讀取數據元素的個數
    FILE *stream: 指向讀取文件的文件指針

return:
    實際讀取的元素個數,非負整數是成功,-1是失敗

fgets()

fgets() 用於讀取文件中的字符串,而後將其存儲到內存空間,函數說明以下:

char *fgets(char *restrict s, int n, FILE *restrict stream);

args:
    char *restrict s     : 讀取後字符串存儲的內存空間地址 
    int n                : 最大讀取字符數
    FILE *restrict stream: 指向讀取文件的文件指針

return:
    若是讀取沒有錯誤且沒有讀入EOF,返回寫入的字符串
    若是讀取沒有錯誤但讀入EOF,返回NULL指針
    若是讀取出現錯誤,返回NULL指針

這裏須要注意下該函數將在什麼時候中止讀取:
若是讀取的字符數量達到 n - 1 ,或讀取了換行符,或讀取了字符串結束符,只要有一個知足則該函數會中止繼續讀取。

gets()

gets()stdin 中讀取字符串並存放在內存中,函數說明以下:

char *gets(char *s);

args:
    char *s: 讀取後字符串存儲的內存空間地址  

return:
    若是讀取沒有錯誤且沒有讀入EOF,返回讀取的字符串
    若是讀取沒有錯誤但讀入EOF,返回NULL指針
    若是讀取出現錯誤,返回NULL指針

讀取操做將在讀入換行符或EOF後結束。

fgetc()

fgetc() 從一個文件讀取一個字符,函數說明以下:

int fgetc(FILE *stream);

args:
    FILE *stream: 指向讀取文件的文件指針 

return:
    若是讀取沒有錯誤且沒有讀入EOF,返回讀取的字符
    若是讀取沒有錯誤但讀入EOF,返回EOF
    若是讀取出現錯誤,返回EOF

getc()

getc()fgetc() 基本同樣,只不過 getc() 是用宏實現而 fgetc() 是用函數實現。

int getc(FILE *stream);

args:
    FILE *stream: 指向讀取文件的文件指針 

return:
    若是讀取沒有錯誤且沒有讀入EOF,返回讀取的字符
    若是讀取沒有錯誤但讀入EOF,返回EOF
    若是讀取出現錯誤,返回EOF

說完了讀操做的函數,咱們也經過一個例子來看看如何使用這些函數:

#include <stdio.h>

int main(int argc, char *argv[])
{
    FILE *fp;
    
    fp = fopen("stdio.log", "r+");
    if (fp == NULL) {
        printf("File open fail...\n");
        return -1; 
    } else {
        printf("File open success...\n");
    }
    
    /* fread() function */ 
    char buffer_1[50]; 
    size_t rd_size = 0; 
    rd_size = fread(buffer_1, 1, 24, fp); 
    printf("rd_size = %d\n", rd_size); 
    printf("fread get: %s\n", buffer_1);

    /* fgets() function */ 
    char buffer_2[50];
    char *fgets_status; 
    fgets_status = fgets(buffer_2, 23, fp); 
    printf("fgets_status = %s", fgets_status); 
    printf("fgets get: %s", buffer_2);
    
    /* gets function */
    char buffer_3[50];
    gets(buffer_3);
    printf("gets get: %s", buffer_3);

    /* fgetc function */
    int ret;
    while ((ret = fgetc(fp)) != EOF)
       printf("%c", ret);

    fclose(fp);   

    return 0; 
}

在編譯過程當中,編譯器會警告:

warning: implicit declaration of function ‘gets’ [-Wimplicit-function-declaration]
     gets(buffer_3);
     ^~~~
/tmp/cc3YWk3i.o: In function `main':
fread_get.c:(.text+0x11d): warning: the `gets' function is dangerous and should not be used.

由於 gets() 函數過於危險,在C11中的 <stdio.h> 已經再也不包含 gets() 函數,所以會出現這個警告。
運行結果:
要讀取的文件是以前寫函數生成的 stdio.log 文件,終端輸出以下。

File open success...
rd_size = 24
fread get: This is fwrite DEMO...
fgets_status = This is fputs DEMO...
fgets get: This is fputs DEMO...
test
gets get: testThis is fputc DEMO...
This is putc DEMO...

總結

這篇文章主要介紹瞭如何使用幾個基本的標準IO函數對文件進行操做,相對於文件IO而言,標準IO使用更方便,而且支持跨平臺使用。同時在傳輸大文件時,標準IO也不比文件IO慢。文中出現的代碼均可在個人github上找到。

若是以爲本文對你有幫助,請多多點贊支持,謝謝!

相關文章
相關標籤/搜索