文件操作函数

fopen/fclose

fopen

在操作文件之前要用fopen命令打开文件,操作完毕要用fclose命令关闭文件。

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

文件的路径名,可以是相对路径也可以是绝对路径。

mode

文件打开方式,mode参数是一个字符串,由rwab+五个字符组合而成。r表示读;w表示写;a表示追加;b表示二进制模式,不写b则表示文本模式;+表示读写。

Hello\r\nWorld\r\n
  • Windows系统下,以文本模式打开:读"\r\n",会被转换成"\n"。写"\n",会被转换成"\r\n";以二进制模式打开则不会做这种转换。
  • UNIX系统下,不管以文本模式还是二进制模式打开,数据都不会转换。
返回值

如果文件打开成功,返回一个指向FILE结构体的文件指针。

如果文件打开失败,返回NULL。

fclose

把文件指针传给fclose可以关闭它所标识的文件。

int fclose(FILE *fp);
fp

是指向FILE结构体的指针,用于指示打开的文件。

返回值

成功时返回 0,失败时返回EOF。

stdin/stdout/stderr

程序启动时会自动打开终端设备,并且用3个FILE*指针stdin、stdout和stderr指向这个设备,这三个文件指针是Iibc中定义的全局变量,在stdio.h中声明,用户程序可以直接使用这三个文件指针。

stdin

用途

用于从标准输入设备读取数据,通常是从键盘输入。

类型

FILE*指针,指向一个输入流。

函数

主要通过scanf、fget、fread等函数进行数据读取。

stdout

用途

用于向标准输出设备写入数据,通常是显示到屏幕上。

类型

FILE*指针,指向一个输出流。

函数

主要通过printf、puts、fwrite等函数进行数据输出。

stderr

用途

用于输出错误信息,通常是显示到屏幕上,与标准输出流分开。

类型

FILE*指针,指向一个错误流。

函数

主要通过fprintf、perror等函数进行错误信息输出。

errno与perror/strerror函数

errno

errno在头文件errno.h中声明,是一个整型变量,所有错误码都是正整数。如果在程序中打印错误信息时直接打印errno的值,不能很直观地看出是什么错误。比较好的办法是用perror将errno解释成字符串再打印。

perror

perror根据当前errno的值,查找对应的错误信息字符串,并将其添加到输出中。

void perror(const char *s);
s

用户提供的自定义字符串,作为错误信息的前缀。

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

int main() {
    FILE *fp = fopen("without", "r");
    if (fp == NULL) {
        perror("Open file without");
        printf("errno: %d\n", errno);
        exit(1);
    }
    return 0;
}

strerror

strerror函数用于根据错误码返回对应的错误信息字符串,声明在string.h头文件中。

char *strerror(int errnum);
errnum

表示错误码的整数值,通常这个值是由errno变量提供的。

返回值

返回一个指向静态内存区域的指针,该区域包含错误码errnum所对应的错误信息字符串。需要注意的是,返回的字符串是静态的,因此每次调用strerror都会覆盖之前的内容。

以字节为单位的I/O函数

fgetc

从指定的文件中读一个字节。系统对于每个打开的文件都记录着当前读写位置,当文件打开时,读写位置在文件开头,每调用一次fgetc,读写位置向后移动一个字节,因此可以连续多次调用fgetc函数依次读取多个字节。

int fgetc(FILE *stream);
stream

指向FILE结构的指针,表示要从中读取字符的文件流。

返回值

成功时,返回读到的字符(int类型)。

失败时,返回EOF,表示遇到文件末尾或发生错误。

getchar

从标准输入流读一个字节,调用getchar()相当于调用fgetc(stdin)。

int getchar(void);
返回值

成功时,返回读到的字符(int类型)。

失败时,返回EOF,表示遇到文件末尾或发生错误。

fputc

fputc函数向指定的文件中写一个字节。每调用一次fputc,读写位置向后移动一个字节,因此可以连续多次调用fputc函数依次写入多个字节。如果文件是以追加方式打开的,则每次调用fputc总是先把读写位置移到文件末尾然后把要写入的字节追加到后面。

int fputc(int c, FILE *stream);
c

要写入的字符(int类型)。

stream

指向FILE结构的指针,表示要写入字符的文件流。

返回值

成功时,返回写入的字符(int类型)。

失败时,返回EOF,表示写入错误。

putchar

putchar向标准输出写一个字节,调用putchar(c)相当于调用fputc(c, stdout)。

int putchar(int c);
c

要写入的字符(int类型)。

返回值

成功时,返回写入的字符(int类型)。

失败时,返回EOF,表示写入错误。

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

int main() {
    FILE *fp;
    int ch; 

    // 打开文件 "file" 以进行读写操作。如果文件不存在,它将被创建。
    if ((fp = fopen("file", "w+")) == NULL) {
        perror("Open file\n");  // 打开文件失败,打印错误信息
        exit(1);  
    }

    // 从标准输入读取字符并写入文件,直到遇到文件结束符(EOF)
    while ((ch = getchar()) != EOF)
        fputc(ch, fp);  

    // 将文件指针移动到文件开头,以便重新读取文件内容
    rewind(fp);

    // 从文件读取字符并输出到标准输出,直到遇到文件结束符(EOF)
    while ((ch = fgetc(fp)) != EOF)
        putchar(ch); 

    // 关闭文件
    fclose(fp);

    return 0;  
}

在终端设备输入时,表示文件结束的方法:

在输入过程中,当光标在行首时,按下Ctrl+D(也称为EOF)来标记输入结束。

如果光标不在行首,通常需要连续按Ctrl+D两次,第一次表示当前输入结束,第二次表示输入流结束。

操作读写位置的函数

fseek

用于移动文件指针,可以用于随机访问文件中的任何位置。

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

指向FILE结构的指针,表示文件流。

offset

从whence指定的位置开始的偏移量(以字节为单位)。offset可正可负,负值表示向前移动,正值表示向后移动。如果向前移动的字节数超过了文件开头则出错返回。如果向后移动的字节数超过了文件末尾,再次写入时将增大文件尺寸,从原来的文件末尾到fseek移动之后的读写位置之间的字节都是0。

whence

在文本模式下使用fseek时,如果需要从文件的特定位置开始读取或写入,最好使用ftell来获取当前指针位置,并以此作为参考点。

在二进制模式下,fseek的使用较为灵活。

表示基准位置的常量:

  • SEEK_SET从文件开头移动offset个字节。
  • SEEK_CUR从当前读写位置移动offset个字节。
  • SEEK_END从文件末尾移动offset个字节。
#include <stdio.h>
#include <stdlib.h>

int main() {
    FILE *fp;
    
    // 打开文件 "file" 以进行读写操作
    if ((fp = fopen("file", "r+")) == NULL) {
        perror("Error opening file textfile");
        exit(1);
    }

    // 移动文件指针到文件的第10个字节
    if (fseek(fp, 10L, SEEK_SET) != 0) {
        perror("Error seeking file textfile");
        fclose(fp);
        exit(1);
    }

    // 写入字符 'K' 到文件
    if (fputc('K', fp) == EOF) {
        perror("Error writing to file textfile");
        fclose(fp);
        exit(1);
    }

    // 关闭文件
    fclose(fp);

    return 0;
}
返回值

成功时,返回写入的字符(int类型)。

失败时,返回EOF,表示写入错误。

ftell

获取当前文件指针的位置。

long ftell(FILE *stream);
stream

指向FILE结构的指针,表示文件流。

返回值

成功时,返回当前文件指针的位置(相对于文件开头的偏移量)。

失败时,返回-1L。

rewind

将文件指针重置到文件开头。

void rewind(FILE *stream);
stream

指向FILE结构的指针,表示文件流。

以字符串为单位的I/O函数

fgets

从指定的文件中读一行字符到调用者提供的缓冲区中。

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

缓冲区首地址。

size

缓冲区长度。

stream

指向FILE结构的指针,表示文件流。

返回值

如果fgets读到文件末尾的最后几个字符,不管是不是以'\n'结尾都加上一个'\0'存入缓冲区并返回。下次再调用fgets时读写位置已经到达文件末尾,则返回NULL。

fgets主要用于读取文本文件而不适合处理二进制文件。

fputs

向指定的文件中写入一个字符串。

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

puts

向标准输出写入一个字符串。

int puts(const char *s);

缓冲区s中保存的是以NULL结尾的字符串。

fputs将该字符串写入文件stream,但并不写入结尾的'\0'。

puts将字符串s写到标准输出(不包括结尾的'\0')​,然后自动写一个'\n'到标准输出。 

以记录为单位的I/O函数

记录是指一串固定长度的字节,比如一个int、一个结构体或者一个定长数组。

fread

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

指向用于存储读取数据的内存块的指针。

size

每个数据项的大小,以字节为单位。

nmemb

要读取的数据项的数量。

stream

指向FILE结构的指针,表示文件流。

返回值

返回实际读取的数据项的数量。

fwrite

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

指向要写入的数据的内存块的指针。

size

每个数据项的大小,以字节为单位。

nmemb

要写入的数据项的数量。

stream

指向FILE结构的指针,表示文件流。

返回值

返回实际写入的数据项的数量。

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

struct record {
    char name[10];
    int age;
};

int main()
{
    // 初始化一个 record 数组,包含两个结构体
    struct record array[2] = {{"Ken", 24}, {"Knuth", 28}};
    
    // 打开文件 recfile 以进行写操作
    FILE *fp = fopen("recfile", "w");
    if (fp == NULL) {
        // 如果文件打开失败,打印错误信息并退出程序
        perror("Open file recfile");
        exit(1);
    }

    // 将数组写入文件
    size_t written = fwrite(array, sizeof(struct record), 2, fp);
    if (written != 2) {
        // 如果写入的记录数不等于预期值,打印错误信息
        perror("Write error");
        exit(1);
    }

    // 关闭文件
    fclose(fp);
    return 0;
}
#include <stdio.h>
#include <stdlib.h>

// 定义结构体 record
struct record {
    char name[10]; 
    int age;     
};

int main()
{
    // 定义一个 record 类型的数组,用来存储两个记录
    struct record array[2];

    // 打开文件 "recfile" 进行读取
    FILE *fp = fopen("recfile", "r");
    if (fp == NULL) { 
        perror("Open file recfile");
        exit(1); 
    }

    // 从文件中读取两个 record 结构体
    fread(array, sizeof(struct record), 2, fp);

    printf("Name1: %s\tAge1: %d\n", array[0].name, array[0].age);
    printf("Name2: %s\tAge2: %d\n", array[1].name, array[1].age);

    // 关闭文件
    fclose(fp);

    return 0;
}

直接在文件中读写结构体的程序是不可移植的,因为不同平台的大小端可能不同,结构体的填充方式也可能不同。

  • 6
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值