C/C++文件讀寫(xiě)

簡(jiǎn)介

C/C++中可以使用以fopen、fclose為代表的文件操作函數(shù)對(duì)文件進(jìn)行讀寫(xiě)。

注:本文在Linux平臺(tái)進(jìn)行演示。(Ubuntu 12.04 LTS + gcc version 4.6.3)

fopen/ fclose

在C中,對(duì)文件的操作套路都是先用fopen打開(kāi)文件,做些讀寫(xiě)的操作,然后關(guān)閉文件。

FILE *fopen(const char *path, const char *mode);

  • path 指文件路徑及文件名
  • mode 打開(kāi)模式,主要集中在r w a + b t這幾種模式組合
  • 返回值:失敗則返回NULL,成功返回FILE指針

int fclose(FILE *fp);

  • fp 為打開(kāi)的文件指針
  • 返回值成功返回0,失敗返回EOF(多為-1)

b t 這兩種模式,指二進(jìn)制模式和文本模式,在Linux平臺(tái)下并沒(méi)有任何區(qū)別。在windows平臺(tái)下對(duì)文本模式的換行符\n會(huì)處理為\r\n。

常用模式說(shuō)明如下

mode description
r 只讀方式打開(kāi),文件必須存在否則失敗
r+ 讀寫(xiě)方式打開(kāi),文件必須存在否則失敗
rb+ 讀寫(xiě)方式打開(kāi)二進(jìn)制文件,文件必須存在否則失敗
w 只寫(xiě)方式打開(kāi),文件不存在則新建,存在則清空文件內(nèi)容
w+ 讀寫(xiě)方式打開(kāi),文件不存在則新建,存在則清空文件內(nèi)容
a 只寫(xiě)方式打開(kāi),文件不存在則新建,存在則寫(xiě)入的內(nèi)容追加到文件尾部
a+ 讀寫(xiě)方式打開(kāi),文件不存在則新建,存在則寫(xiě)入的內(nèi)容追加到文件尾部

示例

我們通過(guò)打開(kāi)當(dāng)前目錄下的data.txt文件,用argv[1]傳遞打開(kāi)模式,測(cè)試r r+ rb+ 三種模式

#include <stdio.h>
#include <stdlib.h>

const char* TEST_FILE_PATH = "./data.txt";

int main(int argc, char **argv)
{
    if ((2 != argc) || (NULL == argv[1]))
    {
        printf("Please Run As: ./a.out r \n");
        return -1;
    }

    FILE *fp = fopen(TEST_FILE_PATH, (const char*)argv[1]);
    if(NULL == fp)
    {
        printf("FileName[%s] Mode[%s] Open Failed \n", TEST_FILE_PATH, argv[1]);
        return -1;
    }

    fclose(fp);
    fp = NULL;

    return 0;
}

編譯 g++ test_mode_r.cpp
當(dāng)前目錄下文件情況

yyl@Machine:/media/sf_share/lc++/io_file$ ls
a.out test_mode_r.cpp

分別用r r+ rb+ 方式打開(kāi),若不存在文件則都打開(kāi)失敗

yyl@Machine:/media/sf_share/lc++/io_file$ ./a.out r
FileName[./data.txt] Mode[r] Open Failed
yyl@Machine:/media/sf_share/lc++/io_file$ ./a.out r+
FileName[./data.txt] Mode[r+] Open Failed
yyl@Machine:/media/sf_share/lc++/io_file$ ./a.out rb+
FileName[./data.txt] Mode[rb+] Open Failed
yyl@Machine:/media/sf_share/lc++/io_file$

用w模式打開(kāi),使其自動(dòng)建立文件,再用r三種模式打開(kāi)則可以成功

yyl@Machine:/media/sf_share/lc++/io_file$ ./a.out w
yyl@Machine:/media/sf_share/lc++/io_file$ ls
a.out data.txt test_mode_r.cpp
yyl@Machine:/media/sf_share/lc++/io_file$ cat data.txt
yyl@Machine:/media/sf_share/lc++/io_file$ ./a.out r
yyl@Machine:/media/sf_share/lc++/io_file$ ./a.out r+
yyl@Machine:/media/sf_share/lc++/io_file$ ./a.out rb+

對(duì)w a模式的理解離不開(kāi)對(duì)文件的讀寫(xiě),這里就先介紹下文件的讀寫(xiě)和文件指針位置,然后接著介紹w 模式和 a模式

fread/fwrite

fread和fwrite是經(jīng)典的讀寫(xiě)函數(shù),主要功能是將數(shù)據(jù)從文件讀到buf或者從buf寫(xiě)到文件中,常用于二進(jìn)制文件的讀寫(xiě)當(dāng)中。

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

  • ptr buf首地址
  • size 讀取的每一個(gè)數(shù)據(jù)的大小
  • nmemb 讀取多少個(gè)數(shù)據(jù)項(xiàng)(總共讀取的大小為 size * nmemb)
  • stream 文件指針
  • 返回值 成功返回真正讀取/寫(xiě)入文件的數(shù)據(jù)項(xiàng)的個(gè)數(shù)(可以根據(jù)此判斷讀取或者寫(xiě)入的數(shù)據(jù)是否有效)

ftell/fseek

ftell和 fseek 用來(lái)獲取和文件指針的位置,可以把文件內(nèi)容看做一個(gè)字符串,我們用指針對(duì)其進(jìn)行操作,fseek可以移動(dòng)到字符串的頭、尾等位置

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

  • stream 文件指針
  • offset whence,以whence為基準(zhǔn)移動(dòng)offset個(gè)字節(jié)的指針位置?;鶞?zhǔn)位置有(SEEK_SET/SEEK_CUR/SEEK_END)分別代表文件首位置和文件尾位置
  • 返回值,成功返回0,失敗返回-1

下面修改測(cè)試代碼后,我們繼續(xù)測(cè)試w a兩種模式打開(kāi)文件

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef unsigned char BYTE;

const BYTE  MAX_BUF_LEN    = 0xfe;
const char* TEST_FILE_PATH = "./data.txt";

int main(int argc, char **argv)
{
    if ((2 != argc) || (NULL == argv[1]))
    {
        printf("Please Run As: ./a.out r \n");
        return -1;
    }

    FILE *fp = fopen(TEST_FILE_PATH, (const char*)argv[1]);
    if(NULL == fp)
    {
        printf("FileName[%s] Mode[%s] Open Failed \n", TEST_FILE_PATH, argv[1]);
        return -1;
    }
    printf("OpenMode[%s] ftell=%ld \n", argv[1], ftell(fp));

    char byBuf[MAX_BUF_LEN + 1] = {0};

    int iRet = snprintf(byBuf, MAX_BUF_LEN, "ABCDEFG%d",7);

    iRet = fwrite((const void*)byBuf, 1, iRet, fp);
    printf("fwrite iRet = %d, strlen byBuf = %d, byBuf = %s,ftell = %ld\n", iRet, strlen(byBuf), byBuf, ftell(fp));

    (void)memset(byBuf, 0, MAX_BUF_LEN + 1);
    // (void)fseek(fp, 0, SEEK_SET);
    // (void)fflush(fp);        // 注意這里。我重新rb打開(kāi)文件,w/wb/wb+讀不出來(lái)我沒(méi)讀出來(lái)。
    fclose(fp);
    fp = NULL;

    fp = fopen(TEST_FILE_PATH, "rb");
    if(NULL == fp)
    {
        printf("FileName[%s] Mode[%s] Open Failed \n", TEST_FILE_PATH, argv[1]);
        return -1;
    }
    printf("OpenMode[%s] ftell=%ld \n", "rb", ftell(fp));
    
    iRet = fread((void*)byBuf, 1, MAX_BUF_LEN, fp);
    printf("fread  iRet = %d, strlen byBuf = %d, byBuf = %s,ftell = %ld\n", iRet, strlen(byBuf), byBuf, ftell(fp));

    fclose(fp);
    fp = NULL;

    return 0;
}

w w+ wb+模式,不建議使用w w+ wb+模式進(jìn)行文件讀取,在我機(jī)器上測(cè)試時(shí)發(fā)現(xiàn)直接用這三種模式?jīng)]有讀出來(lái),也不知道是不是個(gè)例。

fread iRet = 8, strlen byBuf = 8, byBuf = ABCDEFG7,ftell = 8
yyl@Machine:/media/sf_share/lc++/io_file$ ./a.out w
OpenMode[w] ftell=0
fwrite iRet = 8, strlen byBuf = 8, byBuf = ABCDEFG7,ftell = 8
OpenMode[rb] ftell=0
fread iRet = 8, strlen byBuf = 8, byBuf = ABCDEFG7,ftell = 8
yyl@Machine:/media/sf_share/lc++/io_file$ ./a.out wb
OpenMode[wb] ftell=0
fwrite iRet = 8, strlen byBuf = 8, byBuf = ABCDEFG7,ftell = 8
OpenMode[rb] ftell=0
fread iRet = 8, strlen byBuf = 8, byBuf = ABCDEFG7,ftell = 8
yyl@Machine:/media/sf_share/lc++/io_file$ ./a.out wbwb+
OpenMode[wbwb+] ftell=0
fwrite iRet = 8, strlen byBuf = 8, byBuf = ABCDEFG7,ftell = 8
OpenMode[rb] ftell=0

fread iRet = 8, strlen byBuf = 8, byBuf = ABCDEFG7,ftell = 8
yyl@Machine:/media/sf_share/lc++/io_file$ ./a.out wb+
OpenMode[wb+] ftell=0
fwrite iRet = 8, strlen byBuf = 8, byBuf = ABCDEFG7,ftell = 8
OpenMode[rb] ftell=0
fread iRet = 8, strlen byBuf = 8, byBuf = ABCDEFG7,ftell = 8
yyl@Machine:/media/sf_share/lc++/io_file$

跟預(yù)期基本一致,只是C中并沒(méi)有對(duì)模式進(jìn)行嚴(yán)格校驗(yàn),wbwb+ 這種也可以。

下面我們?cè)倏匆幌耡 a+ ab+ 這三種模式

yyl@Machine:/media/sf_share/lc++/io_file$ ./a.out a
OpenMode[a] ftell=8
fwrite iRet = 8, strlen byBuf = 8, byBuf = ABCDEFG7,ftell = 16
OpenMode[rb] ftell=0
fread iRet = 16, strlen byBuf = 16, byBuf = ABCDEFG7ABCDEFG7,ftell = 16
yyl@Machine:/media/sf_share/lc++/io_file$ ./a.out a+
OpenMode[a+] ftell=0
fwrite iRet = 8, strlen byBuf = 8, byBuf = ABCDEFG7,ftell = 24
OpenMode[rb] ftell=0
fread iRet = 24, strlen byBuf = 24, byBuf = ABCDEFG7ABCDEFG7ABCDEFG7,ftell = 24
yyl@Machine:/media/sf_share/lc++/io_file$ ./a.out ab+
OpenMode[ab+] ftell=0
fwrite iRet = 8, strlen byBuf = 8, byBuf = ABCDEFG7,ftell = 32
OpenMode[rb] ftell=0
fread iRet = 32, strlen byBuf = 32, byBuf = ABCDEFG7ABCDEFG7ABCDEFG7ABCDEFG7,ftell = 32
yyl@Machine:/media/sf_share/lc++/io_file$

可以看到三種模式均可以在尾部寫(xiě)文件,a模式打開(kāi)后,文件指針直接在文件尾部,而a+ ab+文件指針則在文件首部,我沒(méi)有使用fseek操作,寫(xiě)的數(shù)據(jù)也自動(dòng)加到了文件尾部,而且fwrite后會(huì)自動(dòng)修改文件指針。

那么這里如果用a模式打開(kāi)是否可以通過(guò)fseek把文件指針移動(dòng)到文件首部呢?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef unsigned char BYTE;

const BYTE  MAX_BUF_LEN    = 0xfe;
const char* TEST_FILE_PATH = "./data.txt";

int main(int argc, char **argv)
{
    if ((2 != argc) || (NULL == argv[1]))
    {
        printf("Please Run As: ./a.out r \n");
        return -1;
    }

    FILE *fp = fopen(TEST_FILE_PATH, (const char*)argv[1]);
    if(NULL == fp)
    {
        printf("FileName[%s] Mode[%s] Open Failed \n", TEST_FILE_PATH, argv[1]);
        return -1;
    }
    printf("OpenMode[%s] ftell=%ld \n", argv[1], ftell(fp));

    int iRet = fseek(fp, 0, SEEK_SET);
    printf("fseek SEEK_SET iRet = %d,ftell=%ld \n", iRet, ftell(fp));

    fclose(fp);
    fp = NULL;

    return 0;
}

測(cè)試結(jié)果

yyl@Machine:/media/sf_share/lc++/io_file$ ./a.out a
OpenMode[a] ftell=32
fseek SEEK_SET iRet = 0,ftell=0
yyl@Machine:/media/sf_share/lc++/io_file$

從結(jié)果中可以看出a模式下也可以通過(guò)fseek移動(dòng)指針。

通過(guò)fopen/fclose、fread/fwrite、ftell/fseek,我們可以完成幾乎所有的文件操作,下面再介紹下文件其他的IO函數(shù)。

fgetc/fputc

int fgetc(FILE *stream);

  • stream 文件指針
  • 返回值: 成功返回所得字符,失敗返回EOF(-1)

int fputc(int c, FILE *stream);

  • c 要寫(xiě)入文件的字符
  • stream 文件指針
  • 返回值: 成功返回寫(xiě)入的字符,失敗返回EOF(-1)

同樣寫(xiě)和讀分開(kāi)打開(kāi)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef unsigned char BYTE;

const BYTE  MAX_BUF_LEN    = 0xfe;
const char* TEST_FILE_PATH = "./data.txt";

int main(int argc, char **argv)
{
    if ((2 != argc) || (NULL == argv[1]))
    {
        printf("Please Run As: ./a.out r \n");
        return -1;
    }

    FILE *fp = fopen(TEST_FILE_PATH, "rb");
    if (NULL == fp)
    {
        printf("FileName[%s] Mode[%s] Open Failed \n", TEST_FILE_PATH, argv[1]);
        return -1;
    }

    char ch = 0;
    printf("fgetc:\n");
    while ((ch = fgetc(fp)) != EOF)
    {
        (void)putchar(ch);
    }
    printf("\n");
    fclose(fp);
    fp = NULL;

    fp = fopen(TEST_FILE_PATH, (const char*)argv[1]);
    if (NULL == fp)
    {
        printf("FileName[%s] Mode[%s] Open Failed \n", TEST_FILE_PATH, argv[1]);
        return -1;
    }

    (void)fseek(fp, 0, SEEK_SET);
    const char *buf = "Fuck!";
    for (const char *p = buf; *p != '\0'; p++)
    {
        if (*p != fputc(*p, fp))
        {
            printf("fpuc failed ch[%c] \n", *p);
        }
    }
    fclose(fp);
    fp = NULL;

    fp = fopen(TEST_FILE_PATH, "rb");
    if (NULL == fp)
    {
        printf("FileName[%s] Mode[%s] Open Failed \n", TEST_FILE_PATH, argv[1]);
        return -1;
    }

    ch = 0;
    printf("fgetc:\n");
    while ((ch = fgetc(fp)) != EOF)
    {
        (void)putchar(ch);
    }
    printf("\n");

    fclose(fp);
    fp = NULL;

    return 0;
}

yyl@Machine:/media/sf_share/lc++/io_file$ cat data.txt
Fuck!yyl@Machine:/media/sf_share/lc++/io_file$ ./a.out w+
fgetc:
Fuck!
fgetc:
Fuck!
yyl@Machine:/media/sf_share/lc++/io_file$

fgets/fputs

char *fgets(char *s, int size, FILE *stream);

  • s 字符串buf
  • size 讀取的大小,最多取size-1個(gè)字符,自動(dòng)添加'\0',遇到\n EOF會(huì)提前停止
  • stream 文件指針

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

  • fputs() writes the string s to stream, without its terminating null byte ('\0').

fscanf/fprintf

文件字符串輸出格式化IO,類似scanf和printf。注意其返回值。

int fscanf(FILE *stream, const char *format, ...);

  • 返回值: 成功則返回讀到參數(shù)個(gè)數(shù),有可能只成功部分參數(shù),注意校驗(yàn)
    int fprintf(FILE *stream, const char *format, ...);
  • 返回值: 成功返回輸出的字符個(gè)數(shù)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef unsigned char BYTE;

const BYTE  MAX_BUF_LEN    = 0xfe;
const char* TEST_FILE_PATH = "./data.txt";

int main(int argc, char **argv)
{

    FILE *fp = fopen(TEST_FILE_PATH, "wb+");
    if (NULL == fp)
    {
        printf("FileName[%s] Mode[%s] Open Failed \n", TEST_FILE_PATH, argv[1]);
        return -1;
    }

    int val_a = 23;
    int val_b = 18;
    int iRet = fprintf(fp, "%dDGDG%d", val_a, val_b);
    printf("fprintf iRet = %d\n", iRet);

    fclose(fp);
    fp = fopen(TEST_FILE_PATH, "rb+");
    if (NULL == fp)
    {
        printf("FileName[%s] Mode[%s] Open Failed \n", TEST_FILE_PATH, argv[1]);
        return -1;
    }

    val_a = 0;
    val_b = 0;
    iRet = fscanf(fp, "%dDGDG%d", &val_a, &val_b);
    printf("iRet = %d, a = %d, b = %d \n", iRet, val_a, val_b);

    return 0;
}

yyl@Machine:/media/sf_share/lc++/io_file$ ./a.out
fprintf iRet = 8
iRet = 2, a = 23, b = 18

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,569評(píng)論 19 139
  • ¥開(kāi)啟¥ 【iAPP實(shí)現(xiàn)進(jìn)入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開(kāi)一個(gè)線程,因...
    小菜c閱讀 7,335評(píng)論 0 17
  • 1. 作業(yè)一:截圖與客戶溝通中用到的這三種成交法過(guò)程?!緵](méi)有實(shí)際案例可以找人演練截圖,重點(diǎn)是能夠熟悉運(yùn)用所講三大成交法】
    汪汪汪巧麗閱讀 190評(píng)論 0 0
  • 我不知道,這樣一個(gè)命題我能不能寫(xiě)好 因?yàn)槲沂且幻恫徽鄄豢鄣拇簖g剩女 沒(méi)有經(jīng)營(yíng)婚姻的經(jīng)驗(yàn) 可這是我最常思考的問(wèn)題。 ...
    gracing閱讀 175評(píng)論 0 0
  • 一直在和老師學(xué)習(xí),練習(xí)寫(xiě)作。許是三月的緣故,連續(xù)好幾天都是以桃花為題材的例文,我都以其他植物代過(guò),今天依然是桃花主...
    嵇游心閱讀 506評(píng)論 5 3

友情鏈接更多精彩內(nèi)容