C语言文件操作基础 -- 标准I/O与文件I/O

简介: 不同操作系统支持的文件类型不相同

文件基础

概念: 一组相关数据的集合

文件类型 头字母
常规文件 r
目录文件 d
字符设备文件 c
块设备文件 b
管道文件 p
套接字文件 s
符号链接文件 l

不同操作系统支持的文件类型不相同


标准I/O

标准I/O 由ANSI C标准定义


主流操作系统上都实现了C库


标准I/O通过缓冲机制减少系统调用,实现更高的效率


标准I/O - 文本流和二进制流

windows下:


二进制流 : 换行符 <> “\n”


文本流 : 换行符 <> “\r\n”


Linux下:


换行符 <> “\n”


标准I/O - 流的缓冲类型

全缓冲:当流的缓冲区无数据或者无空间的时候才执行实际的I/O操作


行缓冲:当在输入和输出中遇到"\n"执行实际的I/O操作,当流和一个终端关联时,典型的行缓冲


无缓冲:数据直接写入文件,流不进行缓冲


标准I/O - stdin,stdout,stderr

标准I/O预定义3个流,程序运行时候自动打开

标准输入流 0 STDIN_FILENO stdin
标准输出流 1 STDOUT_FILENO stdout
标准错误流 2 STDERR_FILENO stderr


标准I/O - 打开流

下列函数可以用于打开一个标准的I/O流

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

成功时候返回流指针;错误时候返回NULL。


mode参数:

方式 介绍
“r"或"rb” 以只读方式打开文件,文件必须存在。
"r+“或"r+b” 以读写方式打开文件,文件必须存在。
“w"或"wb” 以只写方式打开文件,若文件存在则文件长度清零,若文件不存在则创建。
"w+“或"w+b” 以读写方式打开文件,其他同"w"。
“a"或"ab” 以只写方式打开文件,若文件不存在则创建;向文件写入的数据会被追加到文件末尾。
"a+“或"a+b” 以读写方式打开文件,其他同"a"。

当给定"b"参数时,表示以二进制方式打开文件,但是Linux下忽略该参数。


示例:

#include <stdio.h>
int main()
{
    FILE *fp;
    if((fp=fopen("test.txt","r+")) == NULL)
    {
        printf("fopen error\n");
        return -1;
    }
    return 0;
}

新建文件权限

fopen() 创建的文件访问权限是0666(rw-rw-rw-);


Linux系统中 umask 设定会影响文件的访问权限,其规则为(0666 & ~umask);


处理错误信息

extern int errno;
void perror(const char *s);
char *strerror(int errno);


1.errno 存放错误号


2.perror先输出字符串s,再输出错误号对应的错误信息;


3.strerror根据错误号返回对应的错误信息


#include <stdio.h>
int main()
{
    FILE *fp;
    if((fp=fopen("test.txt","r+")) == NULL)
    {
        perror("fopen");
        return -1;
    }
    return 0;
}
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
    FILE *fp;
    if((fp=fopen("test.txt","r+")) == NULL)
    {
        printf("fopen %s\n", strerror(errno));
        return -1;
    }
    return 0;
}

标准I/O - 关闭流

int fclose(FILE *stream)


1.fclose()调用成功返回0,返回失败返回EOF 设置errno


2.流关闭时候自动刷新缓冲中的数据并且释放缓冲区


3.当一个程序正常终止时,所有打开的流都会被关闭


4.流一旦关闭后就不能执行操作


标准I/O - 读写流

流支持不同的读写方式:


读写一个字符: fgetc()/fputc() 一次读/写一个字符。


读写一行: fgets()/fputs() 一次读/写一行。


读写若干个对象: fread()/fwrite() 每次读/写若干个对象,而每个对象具有相同的长度。


fgetc

int fgetc(FILE *stream);
int getc(FILE *stream);
int getchar(void);
#include <stdio.h>
int main()
{
    int ch;
    ch = fgetc(stdin);
    printf("%c\n", ch);
    return 0;
}
#include <stdio.h>
int main()
{
    FILE *fp;
    int ch,count = 0;
    if((fp=fopen("test.txt","r+")) == NULL)
    {
        perror("fopen");
        return -1;
    }
    while((ch= fgetc(fp))!=EOF)
    {
        count ++;
    }
    printf("total, %d bytes\n", count);
    return 0;
}

fputc

int fputc(int c, FILE *stream);
int putc(int c, FILE *stream);
int puttchar(int c);
#include <stdio.h>
int main()
{
    fputc('a',stdout);
    putchar('\n');
    return 0;
}
#include <stdio.h>
int main()
{
    FILE *fp;
    int ch;
    if((fp=fopen("test.txt","w+"))== NULL)
    {
        perror("fopen");
        return -1;
    }
    for(ch='a';ch <='z';ch++)
    {
        fputc(ch, fp);
    }
    return 0;
}

fgets

char *gets(char *s);   // 不建议使用
char *fgets(char *s, int size, FILE *stream);
#include <stdio.h>
#define N 6
int main()
{
    char buf[N];
    fgets(buf, N, stdin);
    printf("buf : %s\n", buf);
    return 0;
}

fputs

int puts(const char *s);
int fputs(const char *s, FILE *stream);
#include <stdio.h>
int main()
{
    puts("hello world!");
    return 0;
}
#include <stdio.h>
int main()
{
    FILE *fp;
    char buf[] = "hello world";
    if((fp = fopen("test.txt","a"))== NULL)
    {
        perror("fopen");
        return -1;
    }
    fputs(buf, fp);
    return 0;
}

标准I/O - 按照对象读写

下列函数用来从流中读取若干对象:

size_t fread(void *ptr, size_t size, size_t n, FILE *fp);
size_t fwrite(const void *ptr, size_t size, size_t n, FILE *fp)

成功返回读写的对象个数,出错返回EOF。


既可以读写文本文件,又可以读写数据文件。


fread/fwrite

#include <stdio.h>
int main()
{
    FILE *fp;
    fp = fopen("test.txt","r");
    int s[10];
    if(fread(s, sizeof(int), 10, fp) < 0)
    {
        perror("fread");
        return -1;
    }
    return 0;
}
#include <stdio.h>
struct student
{
    int no;
    char name[8];
    float score;
};
int main()
{
    FILE *fp;
    fp = fopen("test.txt","rw");
    student s[] = { {1, "zhangs", 97}, {2, " wang", 98}};
    fwrite(s, sizeof(student), 2, fp);
    student s1[2];
    fread(s1, sizeof (student), 2, fp);
    printf("%s\n", s1[0].name);
    printf("%s\n", s1[1].name);
    return 0;
}

标准I/O - 刷新流

1.缓冲区满了之后会自动刷新


2.当流关闭的时候,如果缓冲还有数据会刷新


3.fflush刷新

int fflush(FILE *fp);

成功时候返回0,出错时候返回EOF。


将流缓冲区中的数据写入实际文件。


LINUX下只能刷新输出缓冲区。

#include <stdio.h>
int main()
{
    FILE *fp;
    fp = fopen("test.txt","w");
    if(fp == NULL)
    {
        perror("fopen");
        return -1;
    }
    fputc('a', fp);
    fflush(fp);
    while(1)
    {
    }
    return 0;
}

标准I/O - 定位流

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

1.ftell 成功时候返回当前读写位置,错误时候返回EOF


2.fseek()定位一个流,成功时候返回0, 出错时候返回EOF


whence参数: SEEK_SET开始位置、SEEK_CUR当前位置、SEEK_END结束位置


3.rewind()将流定位到起始位置


当对流进行读写的时候,读写位置会自动后移

#include <stdio.h>
int main()
{
    FILE *fp;
    fp = fopen("test.txt","r+");
    if(fp == NULL)
    {
        perror("fopen");
        return -1;
    }
    fseek(fp, 0, SEEK_END);
    fputc('t', fp);
    return 0;
}
#include <stdio.h>
int main()
{
    FILE *fp;
    fp = fopen("test.txt","r+");
    if(fp == NULL)
    {
        perror("fopen");
        return -1;
    }
    fseek(fp, 0, SEEK_END);
    printf("length is %d\n", ftell(fp));
    return 0;
}

标准I/O - 其他函数

int ferror(FILE *stream);
int feof(FILE *steram);

ferror()返回1表示流出错;否则返回0


feof()返回1表示文件已经到末尾;否则返回0


标准I/O - 格式化输出

int printf(const char *fmt, ...);
int fprintf(FILE *stream, const char *fmt, ...);
int sprintf(char *s, const char *fmt, ...);
#include <stdio.h>
int main()
{
    int year = 2023, month = 1, date = 9;
    char buf[64];
    FILE *fp;
    fp = fopen("test.txt","a+");
    if(fp == NULL)
    {
        perror("fopen");
        return -1;
    }
    fprintf(fp,"%d-%d-%d\n", year, month, date);
    sprintf(buf,"%d-%d-%d\n", year, month, date);
    printf("%s", buf);
    return 0;
}

文件I/O

标准I/O:ANSIC,带缓冲,流FILE


文件I/O:POSIX,无缓冲,文件描述符号


#include


文件I/O是posix定义的一组函数


不提供缓冲机制,每次读写操作都引起系统调用


核心概念是文件描述符


访问各种类型文件


LINUX下标准I/O是基于文件I/O实现的


文件I/O - 文件描述符

每打开一个文件都会对应一个文件描述符


文件描述符是一个非负整数,linux为程序中每个打开的文件分配一个文件描述符


文件描述符从0开始依次递增


文件IO操作通过文件描述符来完成


0 1 2表述标准输入,标准输出和标准错误


文件I/O - open

int open(const char *pathname, int oflag, ...);

成功时候返回文件描述符;出错时返回EOF


打开文件时使用两个参数


创建文件时第三个参数置顶新文件权限


只能打开设备文件

原型

int open(const char *pathname, int oflag, mode_t mode);


参数 pathname 被打开的文件名称(可以包括路径名称)


flags O_RDONLY: O_WRONLY:O_RDWR 三个互斥



O_CREAT:如果文件不存在则创建一个新的文件,使用第三个参数为其设置权限



O_EXCL:如果使用O_CREAT时候文件存在则可返回错误消息,这一参数可测试文件是否存在



O_NOCTTY:使用本参数时候如果文件为终端,那么终端不可以作为调用open()系统调用的那个进程控制终端。



O_TRUNC:如果文件存在,则文件清空



O_APPEND:以追加方式打开文件


mode 被打开文件权限,为8进制表示法
#include <fcntl.h>
#include <stdio.h>
int main()
{
    int fd;
    if((fd = open("1.txt", O_WRONLY | O_CREAT |O_TRUNC, 0666)) < 0)
    {
        perror("open");
        return -1;
    }
    return 0;
}
#include <fcntl.h>
#include <stdio.h>
int main()
{
    int fd;
    if((fd = open("1.txt", O_RDWR | O_CREAT |O_EXCL, 0666)) < 0)
    {
        perror("open");
        return -1;
    }
    return 0;
}

文件I/O - close

int close(int fd);

成功时候返回0;出错返回EOF;


程序结束时候自动关闭所有打开的文件。


文件I/O - read/write

size_t read(int fd, void *buf, size_t count);
size_t write(int fd, void *buf, size_t count);
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
int main()
{
    int fd;
    char buf[64];
    if((fd = open("2.txt", O_RDWR)) < 0)
    {
        close(fd);
        perror("open");
        return -1;
    }
    size_t n = read(fd, buf, 64);
    printf("%s\n", buf);
    printf("%d\n", n);
    close(fd);
    return 0;
}
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
int main()
{
    int fd;
    char buf[64];
    if((fd = open("2.txt", O_RDWR | O_APPEND | O_TRUNC)) < 0)
    {
        close(fd);
        perror("open");
        return -1;
    }
    while(fgets(buf, 20, stdin) != NULL)
    {
        if(strcmp(buf, "quit\n") == 0) break;
        write(fd, buf, strlen(buf));
    }
    return 0;
}

文件I/O - lseek

long lseek(int fd, long offset, int whence);

用法与fseek一致。


实现文件复制:

#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
int main()
{
    int fds, fdt, n;
    char buf[64];
    if((fds = open("2.txt", O_RDONLY)) == -1)
    {
        perror("open1");
        return -1;
    }
    if((fdt = open("1.txt", O_WRONLY | O_CREAT | O_TRUNC)) == -1)
    {
        perror("open2");
        return -1;
    }
    while ((n = read(fds, buf, 64)) > 0)
    {
        write(fdt, buf, n);
    }
    return 0;
}

访问目录


访问目录 - opendir closedir

struct direct *readdir(DIR *dirp);

struct direct是用来描述目录流中一个目录项的结构体类型


包含成员char d_name[256]


成功时候返回目录流dirp中的下一个目录项


出错或到末尾时候返回NULL

int closedir(DIR *dirp);

打印一个目录下的所有文件名称:

#include <fcntl.h>
#include <stdio.h>
#include <dirent.h>
int main()
{
    DIR *dirp;
    struct  dirent *dp;
    if((dirp = opendir("./")) == NULL)
    {
        perror("opendir");
        return -1;
    }
    while((dp = readdir(dirp)) != NULL)
    {
        printf("%s\n" , dp->d_name);
    }
    closedir(dirp);
    return 0;
}

修改文件访问权限 - chmod/fchmod

int chmod(const char *path, mode_t mode);
int fchmod(int fd, mode_t mode);

成功时候返回0,出错时候返回EOF


root和文件所有者能修改访问权限


示例: chmod(“test.txt”, 0666);


获取文件属性 - stat/lstat/fstat

int stat(const char *path, struct stat *buf);
int lstat(const char *path, struct stat *buf);
int fstat(int fd, struct stat *buf);

struct stat:

mode_t st_mode; 类型和访问权限


uid_t st_uid; 所有者id


uid_t st_gid; 用户组id


off_t st_size; 文件大小


time_t st_mtime; 最后修改时间


**st_mode:

#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#include <stdio.h>
#include <time.h>
#include <sys/stat.h>
#include <sys/types.h>
int main()
{
    struct stat buf;
    struct tm *dt;
    int n;
    if(stat("test.txt", &buf) < 0)
    {
        perror("stat ");
        return -1;
    }
    switch (buf.st_mode &S_IFMT)
    {
        case S_IFREG:
            printf("-");
            break;
    }
    for( n = 8; n >=0; n--)
    {
        if(buf.st_mode &(1<<n))
        {
            switch (n%3)
            {
                case 2:
                    printf("r");
                    break;
                case 1:
                    printf("w");
                    break;
                case 0:
                    printf("x");
                    break;
            }
        }
        else
        {
            printf("-");
        }
    }
    printf( " %ld", buf.st_size);
    dt = localtime(&buf.st_mtime);
    printf( " %d-%02d-%02d", dt->tm_year+1900, dt->tm_mon+1, dt->tm_mday);
    return 0;
}


目录
相关文章
|
24天前
|
存储 C语言
【c语言】玩转文件操作
本文介绍了C语言中文件操作的基础知识,包括文件的打开和关闭、文件的顺序读写、文件的随机读写以及文件读取结束的判定。详细讲解了`fopen`、`fclose`、`fseek`、`ftell`、`rewind`等函数的使用方法,并通过示例代码展示了如何进行文件的读写操作。最后,还介绍了如何判断文件读取结束的原因,帮助读者更好地理解和应用文件操作技术。
31 2
|
29天前
|
存储 编译器 C语言
如何在 C 语言中判断文件缓冲区是否需要刷新?
在C语言中,可以通过检查文件流的内部状态或使用`fflush`函数尝试刷新缓冲区来判断文件缓冲区是否需要刷新。通常,当缓冲区满、遇到换行符或显式调用`fflush`时,缓冲区会自动刷新。
|
29天前
|
存储 编译器 C语言
C语言:文件缓冲区刷新方式有几种
C语言中文件缓冲区的刷新方式主要包括三种:自动刷新(如遇到换行符或缓冲区满)、显式调用 fflush() 函数强制刷新、以及关闭文件时自动刷新。这些方法确保数据及时写入文件。
|
1月前
|
存储 C语言
C语言文件操作(2)
【10月更文挑战第2天】
|
1月前
|
程序员 编译器 C语言
C语言底层知识------文件操作
本文详细介绍了文件操作的基本概念,包括文件的分类(程序文件和数据文件,其中着重于数据文件的文本文件和二进制文件),流的概念及其在C程序中的应用,以及标准输入输出流stdin、stdout和stderr的作用。作者通过示例展示了如何使用fopen、fclose和常见的读写函数如fgetc、fputc和fgets进行文件操作。
22 2
|
1月前
|
存储 缓存 编译器
文件操作——C语言
文件操作——C语言
|
1月前
|
存储 C语言
简述C语言文件操作
简述C语言文件操作
11 0
|
1月前
|
存储 文件存储 C语言
深入C语言:文件操作实现局外影响程序
深入C语言:文件操作实现局外影响程序
|
1月前
|
存储 程序员 编译器
C语言文件操作(1)
【10月更文挑战第1天】
|
1月前
|
存储 C语言
C语言的文件操作
C语言的文件操作
21 0