C/C++文件读写

简介

C/C++中可以使用以fopen、fclose为代表的文件操作函数对文件进行读写。

注:本文在Linux平台进行演示。(Ubuntu 12.04 LTS + gcc version 4.6.3)

fopen/ fclose

在C中,对文件的操作套路都是先用fopen打开文件,做些读写的操作,然后关闭文件。

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

  • path 指文件路径及文件名
  • mode 打开模式,主要集中在r w a + b t这几种模式组合
  • 返回值:失败则返回NULL,成功返回FILE指针

int fclose(FILE *fp);

  • fp 为打开的文件指针
  • 返回值成功返回0,失败返回EOF(多为-1)

b t 这两种模式,指二进制模式和文本模式,在Linux平台下并没有任何区别。在windows平台下对文本模式的换行符\n会处理为\r\n。

常用模式说明如下

mode description
r 只读方式打开,文件必须存在否则失败
r+ 读写方式打开,文件必须存在否则失败
rb+ 读写方式打开二进制文件,文件必须存在否则失败
w 只写方式打开,文件不存在则新建,存在则清空文件内容
w+ 读写方式打开,文件不存在则新建,存在则清空文件内容
a 只写方式打开,文件不存在则新建,存在则写入的内容追加到文件尾部
a+ 读写方式打开,文件不存在则新建,存在则写入的内容追加到文件尾部

示例

我们通过打开当前目录下的data.txt文件,用argv[1]传递打开模式,测试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
当前目录下文件情况

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

分别用r r+ rb+ 方式打开,若不存在文件则都打开失败

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模式打开,使其自动建立文件,再用r三种模式打开则可以成功

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+

对w a模式的理解离不开对文件的读写,这里就先介绍下文件的读写和文件指针位置,然后接着介绍w 模式和 a模式

fread/fwrite

fread和fwrite是经典的读写函数,主要功能是将数据从文件读到buf或者从buf写到文件中,常用于二进制文件的读写当中。

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 读取的每一个数据的大小
  • nmemb 读取多少个数据项(总共读取的大小为 size * nmemb)
  • stream 文件指针
  • 返回值 成功返回真正读取/写入文件的数据项的个数(可以根据此判断读取或者写入的数据是否有效)

ftell/fseek

ftell和 fseek 用来获取和文件指针的位置,可以把文件内容看做一个字符串,我们用指针对其进行操作,fseek可以移动到字符串的头、尾等位置

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

  • stream 文件指针
  • offset whence,以whence为基准移动offset个字节的指针位置。基准位置有(SEEK_SET/SEEK_CUR/SEEK_END)分别代表文件首位置和文件尾位置
  • 返回值,成功返回0,失败返回-1

下面修改测试代码后,我们继续测试w a两种模式打开文件

#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打开文件,w/wb/wb+读不出来我没读出来。
    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+模式进行文件读取,在我机器上测试时发现直接用这三种模式没有读出来,也不知道是不是个例。

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$

跟预期基本一致,只是C中并没有对模式进行严格校验,wbwb+ 这种也可以。

下面我们再看一下a 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$

可以看到三种模式均可以在尾部写文件,a模式打开后,文件指针直接在文件尾部,而a+ ab+文件指针则在文件首部,我没有使用fseek操作,写的数据也自动加到了文件尾部,而且fwrite后会自动修改文件指针。

那么这里如果用a模式打开是否可以通过fseek把文件指针移动到文件首部呢?

#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;
}

测试结果

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$

从结果中可以看出a模式下也可以通过fseek移动指针。

通过fopen/fclose、fread/fwrite、ftell/fseek,我们可以完成几乎所有的文件操作,下面再介绍下文件其他的IO函数。

fgetc/fputc

int fgetc(FILE *stream);

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

int fputc(int c, FILE *stream);

  • c 要写入文件的字符
  • stream 文件指针
  • 返回值: 成功返回写入的字符,失败返回EOF(-1)

同样写和读分开打开

#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个字符,自动添加'\0',遇到\n EOF会提前停止
  • 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, ...);

  • 返回值: 成功则返回读到参数个数,有可能只成功部分参数,注意校验
    int fprintf(FILE *stream, const char *format, ...);
  • 返回值: 成功返回输出的字符个数
#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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,563评论 18 139
  • ¥开启¥ 【iAPP实现进入界面执行逐一显】 〖2017-08-25 15:22:14〗 《//首先开一个线程,因...
    小菜c阅读 6,335评论 0 17
  • 1. 作业一:截图与客户沟通中用到的这三种成交法过程。【没有实际案例可以找人演练截图,重点是能够熟悉运用所讲三大成交法】
    汪汪汪巧丽阅读 120评论 0 0
  • 我不知道,这样一个命题我能不能写好 因为我是一枚不折不扣的大龄剩女 没有经营婚姻的经验 可这是我最常思考的问题。 ...
    gracing阅读 154评论 0 0
  • 一直在和老师学习,练习写作。许是三月的缘故,连续好几天都是以桃花为题材的例文,我都以其他植物代过,今天依然是桃花主...
    嵇游心阅读 447评论 5 3