linux文件编程_文件

news2024/10/1 14:00:26

1. 文件编程概述

之前在windows中对文件的操作是:打开文档—>编辑文档—>保存文档—>关闭文档

我们的Linux文件编程主要是利用代码对文件进行操作:文件创建、打开、编辑等自动化执行等

在Linux我们要使用编程调用api函数的方式进行文档的编辑:

打开:open 读写:write/read 光标定位:lseek 关闭:close

2. 文件打开与创建

2.1. 什么是API

API,全称是 Application Programming Interface,即应用程序编程接口。API 是一组定义良好的规则和协议,允许不同的软件应用程序之间进行通信或交互。

简单来说,API 就像是软件应用程序之间的桥梁,它定义了两者如何相互请求和交换数据。例如,应用程序A可以通过API向应用程序B请求信息,而应用程序B通过API返回所需的数据。API定义了这些请求和响应的格式和标准,因此应用程序开发者可以在不知道内部实现细节的情况下使用API进行编程。

API 的使用场景
  • 调用外部服务:如调用支付服务API处理在线支付。
  • 集成第三方功能:如使用地图API嵌入地图功能到应用程序中。
  • 模块化编程:如使用操作系统API管理文件、进程等。

在Linux操作系统中,操作系统提供了一系列的API(应用程序接口)供开发者使用。这些API涵盖了操作系统的各个层面,包括进程管理、内存管理、文件系统操作、网络通信等

2.2. 下面是一些常见的Linux系统API及其用途:
2.2.1. 进程管理API:
    • fork():创建一个新进程,称为子进程。
    • exec() 系列函数(如 execl(), execv(), execvp() 等):用于替换当前进程的内存映像,从而运行一个新的程序。
    • wait() / waitpid():用于等待子进程的终止,并获取其退出状态。
    • getpid() / getppid():获取当前进程的ID或父进程的ID。
    • kill():向指定进程发送信号(例如终止进程)。
2.2.2. 内存管理API:
    • malloc() / calloc() / realloc():动态分配内存。
    • free():释放动态分配的内存。
    • mmap():用于将文件或设备映射到内存,或直接分配匿名内存段。
    • munmap():取消内存映射。
2.2.3. 文件操作API:
    • open():打开文件或设备,返回文件描述符。
    • read() / write():从文件读取数据或向文件写入数据。
    • close():关闭文件描述符。
    • lseek():移动文件指针位置。
    • stat() / fstat() / lstat():获取文件的属性信息。
    • unlink():删除文件。
2.2.4. 线程操作API(基于POSIX线程库 pthreads):
    • pthread_create():创建一个新线程。
    • pthread_join():等待线程终止。
    • pthread_mutex_lock() / pthread_mutex_unlock():锁定/解锁互斥量(用于线程同步)。
    • pthread_cond_wait() / pthread_cond_signal():等待/发送条件变量信号。
    • pthread_exit():退出线程。
2.2.5. 网络通信API(基于BSD套接字):
    • socket():创建一个新的套接字。
    • bind():绑定套接字到特定的IP地址和端口号。
    • listen():监听进入的连接请求(用于服务器)。
    • accept():接受连接请求并返回一个新的套接字。
    • connect():与服务器建立连接。
    • send() / recv():发送/接收数据。
    • close():关闭套接字。
2.2.6. 信号处理API:
    • signal():设置信号处理函数。
    • sigaction():设置或检查信号的处理方式。
    • sigprocmask():修改或检查信号屏蔽字。

这些API是Linux操作系统核心功能的基础,通过它们,开发者可以与操作系统的不同部分进行交互,完成各种系统级操作。在开发过程中,理解和熟练使用这些API非常重要。

2.3. 打开/创建文件 有3个API

比如说这个是我们运行的一个.\a.out程序,这个程序有可能打开很多个文件,比如说open里面有一个叫做file1,还有file2,file3等等,我们都用open来打开这些东西。

把它打开。好,你下一步进行write,进行写文件,进行读文件的时候,我们如何知道我该操作哪一个文件,我该操作哪一个文件,就要通过open的返回值,叫做文件描述符。,我们open文件会返回一个文件描述符,后续比如说file1返回的描述符5,file2返回的描述符7,你下一次进行write的时候,你要把这个描述符传进来[write(5)],你就知道你打开的是file1文件,而不是file2文件,因为我们一个进程打开的肯定是是不止一个文件,就算你就打开一个文件,你也要文件描述符。

为了区分后面write跟read对文件的操作,你要区分哪个文件,我们通过描述符,简单的了解一下这个描述,它实际上是一个索引,我们每个程序打开,每个进程打开一个文件的时候,它都会建立一个结构体来管理这些文件,这个描述符就是它会指向这些结构,内核里面的结构体;是一个所起索引的一个作用,您先了解这么多就可以了。也就是说我们open的返回值很重要的,返回值就是文件描述符,这个是比较重要的。你如果没有这个描述符,你后面read、write对他就起不到作用了。

/*
	Linux下:man 2 open可以查看对应手册
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

上面三行为头文件,
 
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
int creat(const char *pathname, mode_t mode);

int 整型数    open 打开     
const  将变量定义为不可修改的常量
pathname	文件路径的意思

open的返回值 == 文件描述符
pathname (含路径,缺省为当前路径)

flags     权限的意思 
    O_RDONLY:	只读打开        
    O_WRONLY:	只写打开        
    O_RDWR:	可读可写打开
    

  • open

open函数用于打开指定路径名的文件。

  • 语法:
  1. int open(const char *pathname, int flags);
  2. int open(const char *pathname, int flags, mode_t mode);
  • pathname:要打开的文件的路径。
  • flags:指定文件的访问模式和其他选项。常见的标志包括:
    • O_RDONLY:只读模式打开文件。
    • rdonly
    • wronly
    • rdonly
    • O_WRONLY:只写模式打开文件。
    • O_RDWR:读写模式打开文件。
    • O_CREAT:如果文件不存在则创建文件(需要提供第三个参数mode)。
    • O_TRUNC:如果文件已存在且可以写入,则将其长度截断为0。
    • O_APPEND:以追加模式打开文件。
  • mode:指定文件权限位(当使用O_CREAT标志时必须提供),例如:
    • S_IRUSR:文件所有者的读权限。
    • S_IWUSR:文件所有者的写权限。
    • S_IRGRP:组用户的读权限。
    • S_IWGRP:组用户的写权限。
  • creat

creat函数用于创建一个新文件或截断一个现有文件。

  • 语法:

int creat(const char *pathname, mode_t mode);

  • pathname:要创建或截断的文件的路径。
  • mode:指定文件的权限位,类似于open函数中的mode参数。
2.4. open函数打开一个文件:

利用open函数打开file1文件,如果文件存在则返回3,否则返回-1

源代码:

#include <sys/types.h>
#include <sys/stat.h>      
#include <fcntl.h>
#include <stdio.h>

int main(){

	int fd;
	fd = open("./fill1",O_RDWR);
	
	printf("fd = %d",fd);
	return 0;
}

2.4.1. 源代码详解:
  1. 头文件提供了所需的函数和类型定义:
  • sys/types.hsys/stat.h 提供文件相关的系统数据类型。
  • fcntl.h 提供文件控制选项和函数,如 open
  • stdio.h 提供标准输入输出函数,如 printf
  1. main解释
  • int main(): 这段代码定义了程序的入口点main函数。
  • int fd;: 定义一个整数变量fd,用来存储文件描述符。
  • fd = open("./file1", O_RDWR);:
    • open函数尝试打开当前目录下名为file1的文件。
    • "./file1":文件的相对路径。
    • O_RDWR:以读写模式打开文件。
    • 返回值:如果打开成功,open函数返回一个非负的文件描述符,用于后续对文件的操作。如果打开失败,返回值为-1
  • printf("fd = %d", fd);: 使用printf函数打印文件描述符的值。如果文件成功打开,fd会是一个非负整数。如果打开失败,fd会是-1
  • return 0;: 表示程序正常结束,返回0。
  • 查找成功

我们刚开始touch了一个file1文件,用open函数可以打开这个文件,返回3

  • 查找失败

我们rm删除了file1文件,用open函数打开这个文件就失败了,返回-1

2.5. open函数打开文件失败创建一个文件:

使用open函数打开file1文件失败后,就创建一个file1文件

O_CREAT函数 创建文件

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

int main(){

        int fd;
        fd = open("./file1",O_RDWR);  // 使用open 打开一个名为file1的文件,并且以读写模式打开

        if(fd == -1){  //如果open 返回是负数,就说明没有找到这个文件,打开失败。需要创建文件。
                printf("open file1 failed\n");  //原样输出这句话 
                fd = open("./file1",O_RDWR|O_CREAT,0600);   // 可读可写|或者 O_CREAT创建文件 并且授权文件所有者可读写权限,并且返回文件描述符。
                if(fd > 0)  //若文件描述符>0则代表创建文件成功!
                        printf("create file1 success\n");
        }
        printf("fd = %d\n",fd);  //
        return 0;
}

最终我们可以看到创建file1文件成功!

  • mode 权限模式

1.可读: r 4

2.可写: w 2

3.可执行 x 1

0600:6代表4+2(可读可写)

2.6. 其他函数使用
2.6.1. O_EXCL函数
int main(){

	int fd;
	
		fd = open("./file1",O_RDWR|O_CREAT|O_EXCL,0600);
		if(fd == -1 ){
			printf(" file1 exit cunzai\n");
	}
	printf("fd = %d\n",fd);
	return 0;
}
  • 注意:
    • fd = open("./file1", O_RDWR | O_CREAT | O_EXCL, 0600);
    • open 函数用于打开或创建一个文件。
    • ./file1:表示要打开或创建的文件的路径(当前目录下的 file1 文件)。
  • 标志参数
    • O_RDWR:以读写模式打开文件。
    • O_CREAT:如果文件不存在,则创建文件。
    • O_EXCL:与 O_CREAT 一起使用时,如果文件已经存在,则 open 调用失败,并返回 -1
  • 权限参数0600):当 O_CREAT 标志被指定时,这个参数定义了新创建文件的权限。0600 表示文件拥有者有读写权限,而其他用户没有任何权限。
  • f (fd == -1):检查 open 函数的返回值是否为 -1,这表示文件打开或创建失败。
    • 如果 fd == -1,这可能是因为文件已经存在(由于使用了 O_EXCL 标志),或者其他原因导致无法打开文件。
    • printf("file1 already exists\n");:如果文件已经存在,输出提示信息 "file1 already exists"
  • 总结:

这段代码尝试打开名为 file1 的文件。如果文件不存在,它会创建该文件,并设置权限为仅文件拥有者可读写。如果文件已经存在,由于使用了 O_EXCL 标志,open 调用会失败,并输出文件存在提示信息。

2.6.2. O_APPNED函数

int main(){

	int fd;		
	char *buf = "lxl hen shuai!!!";
	
	
	fd = open("./file1",O_RDWR|O_APPEND);
	
		
	printf("open susceess : fd = %d\n",fd);

	int n_write = write(fd,buf,strlen(buf));
	if(fd != -1){ 
		printf("file nice ok!!!\n");
		printf("write %d byte to file \n",n_write);
	}

	close(fd);	// OFF
	return 0;
}

fd = open("./file1", O_RDWR | O_APPEND);

  • open 函数用于打开文件 file1
  • 标志参数
    • O_RDWR:以读写模式打开文件。
    • O_APPEND:在每次写操作时,将数据追加到文件末尾,而不是覆盖文件内容。

如果目标文件描述符对应的文件中存在数据内容,则以读写模式打开,并设置为追加模式(每次写入的数据都会追加到文件中数据末尾的位置)。

2.6.3. O_TRUNC 函数
int main(){

        int fd;
        char *buf = "test!!!";


        fd = open("./file1",O_RDWR|O_TRUNC);


        printf("open susceess : fd = %d\n",fd);

        int n_write = write(fd,buf,strlen(buf));
        if(fd != -1){
                printf("file nice ok!!!\n");
                printf("write %d byte to file \n",n_write);
        }

        close(fd);      // OFF
        return 0;
}

fd = open("./file1", O_RDWR | O_TRUNC);

  • open 函数用于打开文件 file1
  • 标志参数
    • O_RDWR:以读写模式打开文件。
    • O_TRUNC:如果文件存在,打开文件时会将其长度截断为 0 字节,即清空文件内容。
  • 如果文件成功打开,open 函数返回一个文件描述符(非负整数);如果失败,返回 -1
  • 总结:

这段代码执行以下操作:

  1. 打开文件 file1。如果文件存在,以读写模式打开,并使用 O_TRUNC 标志将文件内容清空(截断为 0 字节)。如果文件不存在,则打开失败,open 返回 -1
  2. 如果文件成功打开,将字符串 "lxl hen shuai!!!" 写入文件中,并输出写入的字节数。
2.6.4. creat 函数
  • 函数原型 int creat(const char *filename,mode_t mode);

filename :要创建的文件名(包含路径、缺省为当前路径)

mode: 创建模式 // 可读可写可执行

  • 常见创建模式

编码实现:

int main(){

	int fd;
	char *buf = "LXL heng shuai!!!";
	// int creat(const char *pathname,mode_t mode);

	fd = creat("/home/lxl/file1",S_IRWXU);
	close(fd);
	return 0;
}
  • 运行结果

/home/lxl 路径下创建了一个名为file1 的文件 ,并且授予可读可写可执行权限。

3. `文件写入操作编程

3.1. 文件写入函数write和文件关闭函数close原型和头文件:
/*
	Linux下:man 2 write可以查看对应手册
*/

#include <unistd.h>

ssize_t write(int fd, const void *buf, size_t count);

int fd 		    // 写入到刚打开的文件中
const void *buf  //无符号类型的字符缓冲区
size_t count    // 写入的字节大小
ssize_t         // 若成功写入文件,函数返回值为写入字节个数,若失败,返回-1。
    
3.1.1. write 函数有三个描述符
    1. int fd 文件描述符

open函数打开文件之后,都会有一个文件描述符,后续通过文件描述符对文件进行操作

    1. const void *buf 无类型的指针 (缓冲区)

指向要写入数据的缓冲区的指针。它通常是一个 void 类型的指针,因此可以指向任何类型的数据。

    1. size_t count 内存大小

这是一个 size_t 类型的变量,表示要写入的字节数。

3.2. 对文件写入内容:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main()
{
    int fd;  // 定义一个文件描述符变量 `fd`
    char *buf = "chenlichen handsome";  // 定义一个指向字符串的指针 `buf`

    fd = open("./file1", O_RDWR);  // 使用 `open` 函数以读写模式打开当前目录下的 `file1` 文件
    if (fd == -1) {  // 如果文件打开失败(即 `fd` 等于 `-1`)
        printf("file1 no failed\n");  // 输出 "file1 no failed" 表示打开失败
        fd = open("./file1", O_RDWR | O_CREAT, 0600);  
        // 如果 `file1` 文件不存在,使用 `O_CREAT` 标志创建该文件,并将其权限设置为 0600(所有者可读可写)
        if (fd > 0) {
            printf("create file1 success!\n");  // 如果文件描述符 `fd` 大于 0,表示创建文件成功
        }
    }
    printf("open success : fd = %d\n", fd);  // 输出文件描述符 `fd`,表示文件打开或创建成功

    // ssize_t write(int fd, const void *buf, size_t count);
    write(fd, buf, strlen(buf));  
    /*  使用 `write` 函数将 `buf` 中的内容写入到 `fd` 对应的文件中
        *  `sizeof(buf)` 在这里获取的是 `buf` 这个指针的大小,而不是字符串的长度
        *  在 64 位系统中,指针的大小通常为 8 个字节
        *  因此,这里的 `write` 实际上只会写入 8 个字节(而不是整个字符串)
    */

    close(fd);  // 关闭文件描述符 `fd`,即关闭文件
    return 0;  // 返回 0 表示程序正常结束
}
  • 运行代码 ,查看

  • 只能看到前8个字符

注意:在linux 下 sizeof函数 只给指针分配8个字节

获取所有字符串,使用strlen函数,可以得到全部的数据。

  • 查看完整字符:
int main()
{
	int fd;
	char *buf = "lxl heng shuai!!!");
 
	fd = open("./file1",O_RDWR);					//使用open函数打开file1文件		
	if(fd == -1){									//如果打开失败
		printf("file1 no failed\n");				//输出文件1号失败	
		fd = open("./file1",O_RDWR|O_CREAT,0600);	//文件不存在则创建它,需要同时说明第三个参数mode,权限为可读可写
		if(fd > 0){
			printf("create file1 success!\n");		//若文件描述符>0则代表创建文件成功!
		}
	}
	printf("open susceess : fd = %d\n",fd);			//输出创建或者打开的文件的文件描述符
    
	//ssize_t write(int fd, const void *buf, size_t count);
	 write(fd,buf,strlen(buf));		 	/*  将buf中的内容写入到fd对应的文件中
                                        *	注意 sizeof在这里扮演的是一个指针的角色,
                                        *	在linux系统中指针的长度是8个字节
                                    	*/
    
	close(fd);										//关闭刚写入的文件
	return 0;
}

通过open函数成功打开了file1文件,并将内容"lxl heng shuai!!!"写入到文件中

通过open函数打开file1文件失败后,创建了一个file1文件,创建的文件文件描述符是3,并将内容 "lxl heng shuai!!!"写入到文件中

  1. 注意 :
  • strlen可能大家忘了 我给大家补充一下

`strlen` 是 C 标准库函数,用于计算字符串的长度(不包括终止的空字符 `'\0'`)。

  • 头文件

使用 `strlen` 需要包含 `<string.h>` 头文件。

  • 函数原型

size_t strlen(const char *str);

  • 参数

- `str`:指向以 `'\0'` 结尾的字符串的指针。

  • 返回值

- 返回字符串的长度,不包括终止的空字符 `'\0'`。

`strlen` 函数用于计算以 `'\0'` 结尾的字符串的长度,不包括终止的空字符。它在处理字符串时非常有用,例如在字符串复制、连接和比较等操作中。

  • 头文件
  • <sys/types.h>:提供基础数据类型定义,供系统调用使用。
  • <sys/stat.h>:用于管理和获取文件状态和属性信息。
  • <fcntl.h>:用于文件控制操作,如打开文件、设置文件描述符等。
  • <stdio.h>:提供标准输入输出功能,如打印输出、文件操作等。
  • <unistd.h>:提供对底层系统调用的接口,如文件操作、进程管理等。
  • <string.h>:提供字符串和内存操作的标准函数。

4. 文件读取操作编程

  • 代码实现
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <unistd.h>   //
#include <stdlib.h>  // malloc


int main(){

        int fd;   // 定义一个整数型函数
        char *buf = "LXL heng shuai!!!";   //定义一个字符型指针,里面存放字符串
        fd = open("./file1",O_RDWR);     // open 打开函数   打开名为./file1 文件,并且返回一个文件描述符

        if(fd == -1){  // 如果 文件不存在
                printf("open file1 failed\n");  // 原样输出
                fd = open("./file1",O_RDWR|O_CREAT,0600);   // 创建一个名为./file1 文件,授予可读写权限,并且返回一个文件描述符
                if(fd > 0)   // 如果 文件的返回值 大于0,表示./file1 文件存在
                        printf("create file1 success\n");   // 原样输出这句话
         }
        printf("fd = %d\n",fd);    // 打印文件描述符号
  //    ssize_t write(int fd, const void *buf, size_t count);
 //     write(fd,buf,sizeof(buf));

    // 写入文件,将指针buf中所有的数据,写入当前文件描述符号中对应的文件中。并且返回文件中数据的字节数
        int n_write = write(fd,buf,strlen(buf));   

         
        if(n_write != -1){   // 如果写入成功
                printf("write %d byte to file\n",n_write);   // 打印出相关数据的字节数

        }
        close(fd);  // 关闭相关文件,使其光标符,回归到文件中数据的开头。
        fd = open("./file1",O_RDWR);   // 重新打开 ./file1文件
    
        char *readBuf;  
            // 定义了一个字符类型的指针 `readBuf`,它将用于指向一个存储读取内容的缓冲区。
        
        readBuf = (char *)malloc(sizeof(char) * n_write + 1);  
    
        // 使用 `malloc` 动态分配内存空间,大小为 `n_write` 字节加 1 个字节(通常用于存放字符串终止符 `\0`)。
        // `sizeof(char)` 通常为 1,但显式写出可以使代码更具可读性。
        // `malloc` 返回的指针类型为 `void*`,需要强制转换为 `char*` 类型。
        
        // ssize_t read(int fd, void *buf, size_t count);
        int n_read = read(fd, readBuf, n_write);  
        // 使用 `read` 函数从文件描述符 `fd` 中读取 `n_write` 个字节的数据,并将其存储在 `readBuf` 指向的缓冲区中。
        // `read` 函数返回实际读取的字节数,将其赋值给 `n_read`。
        
        printf("read %d, context: %s\n", n_read, readBuf);  
        // 打印读取的字节数 `n_read` 以及读取到的内容 `readBuf`。
        // `readBuf` 中存储的内容是从文件中读取的字节,假定它是一个以 `\0` 结尾的字符串。

        close(fd);
        return 0;
}
4.1.1. close函数

close(fd); // 关闭相关文件,使其光标符,回归到文件中数据的开头。

fd = open("./file1",O_RDWR); // 重新打开 ./file1文件

  • 注意

int n_read = read(fd, readBuf, n_write);

这里的n_write,即使写出100,也没有用,read函数读取,只会读取readBuf中的实际数据,而100表示,read函数最大读取的字节数

  • 举个例子:
int fd = open("file.txt", O_RDONLY);  // 假设 file.txt 中有 50 个字节的数据
char readBuf[100];  // 定义一个大小为 100 的缓冲区
int n_write = 100;  // 想要读取最多 100 个字节的数据

int n_read = read(fd, readBuf, n_write);  
// 实际上 `n_read` 会返回 50,因为文件中只有 50 个字节
// 这 50 个字节会被存入 `readBuf` 的前 50 个字节位置

printf("实际读取了 %d 个字节\n", n_read);  // 输出: 实际读取了 50 个字节
  • 总结:

n_write 确实会限制 read 函数最多读取的字节数,但实际读取的字节数取决于文件的内容以及其他因素。因此,即使你设置 n_write 为 100,read 函数只会读取 fd 指向的文件中实际存在的数据量,而不会读取超过文件内容的数据。

五、文件“光标”位置

4.1.1. lseek()函数

lseek()将与文件描述符fd相关联的打开文件描述的文件偏移量重新定位到参数offset。

函数原型:

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

  • fd是文件描述符,用于指定要操作的文件;
  • offset表示偏移量,即要从哪个位置开始移动;
  • whence表示起始点,即从文件的哪个位置开始计算偏移量,它有三个值:
    • SEEK_SET(默认值,表示从文件头开始计算)、
    • SEEK_CUR(表示从当前位置开始计算)
    • SEEK_END(表示从文件尾开始计算)。

如果成功偏移后,lseek函数返回移动光标后光标距开头的字节数,如果失败,返回值为-1。

        // off_t lseek(int fd, off_t offset, int whence);
        /*off_t lseek(int fd, off_t offset, int whence); lseek函数原型*/
		
		lseek(fd,0, SEEK_SET);            //将光标移动到头
		lseek(fd,-17, SEEK_END);          //将光标从尾巴移动到头
		lseek(fd,-19, SEEK_CUR);          //将光标从当前位置移动到头
		lseek(fd,-n_write, SEEK_CUR); 	  //将光标从当前位置移动到头

利用lseek()还可以计算文件大小:利用它的返回值,偏移后光标距文件开头的字节大小,只要此时光标位置位于文件末尾,那么它的返回值就是文件大小。

int main(){

	int fd;
	char *buf = "LXL heng shuai!!!";
	fd = open("./file1",O_RDWR);
	
	// 计算文件大小
	int filesize = lseek(fd,0,SEEK_END);
	printf("file's size is:%d\n",filesize);

	close(fd);
	return 0;
}
  • 运行结果

四、文件读取操作编程
4.1 文件读取函数read函数原型和头文件:

/*
	Linux下:man 2 read可以查看对应手册
*/
 
#include <unistd.h>
 
ssize_t read(int fd, void *buf, size_t count);	
 
fd				//文件描述符
void *buf		//待读入缓冲区
size_t count	//读出字节个数
ssize_t			//若成功读取文件,函数返回值为读出字节个数,若失败,返回-1 
    
//read函数作用:从fd指向的文件读取count个字节的数据放到buf缓冲区里面
4.2 使用read函数读取文件中的内容:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

int main(){

        int fd;
        char *buf = "LXL heng shuai!!!";
        fd = open("./file1",O_RDWR);

        if(fd == -1){
                printf("open file1 failed\n");
                fd = open("./file1",O_RDWR|O_CREAT,0600);
                if(fd > 0)
                        printf("create file1 success\n");
        }
        
        printf("fd = %d\n",fd);
  //    ssize_t write(int fd, const void *buf, size_t count);
 //     write(fd,buf,sizeof(buf));
        int n_write = write(fd,buf,strlen(buf));

        if(n_write != -1)
                printf("write %d byte to file\n",n_write);
        
        char *readBuf;
        readBuf = (char *)malloc(sizeof(char)*n_write + 1);
   //   ssize_t read(int fd, void *buf, size_t count);
        int n_read = read(fd,readBuf,n_write);

        printf("read %d,context:%s\n",n_read,readBuf);
        close(fd);
        return 0;
}

以下是每一行代码的详细中文解释:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

这些头文件提供了文件操作和其他必需的库函数:

  • sys/types.hsys/stat.h:提供文件相关的系统数据类型和函数。
  • fcntl.h:提供文件控制选项和函数,如 open
  • stdio.h:提供标准输入输出函数,如 printf
  • unistd.h:提供POSIX操作系统API,如 readwrite
  • string.h:提供字符串操作函数,如 strlen
  • stdlib.h:提供通用工具函数,如 mallocfree
int main(){
    int fd;
    char *buf = "LXL heng shuai!!!";
  • int main():定义程序的入口函数 main
  • int fd;:定义一个整数变量 fd,用来存储文件描述符。
  • char *buf = "LXL heng shuai!!!";:定义一个字符串指针 buf,指向要写入文件的字符串数据。
    fd = open("./file1", O_RDWR);
  • fd = open("./file1", O_RDWR);:尝试以读写模式打开当前目录下名为 file1 的文件。如果文件不存在或打开失败,fd 将为 -1
    if(fd == -1){
        printf("open file1 failed\n");
        fd = open("./file1", O_RDWR | O_CREAT, 0600);
        if(fd > 0)
            printf("create file1 success\n");
    }
  • if(fd == -1){ ... }:检查文件是否打开失败。
  • printf("open file1 failed\n");:如果打开失败,打印错误消息。
  • fd = open("./file1", O_RDWR | O_CREAT, 0600);:如果文件不存在,则以读写模式创建文件,权限为用户读写 (0600)。
  • if(fd > 0) ...:如果文件创建成功,打印成功消息。
    printf("fd = %d\n", fd);
  • 打印文件描述符的值。
    int n_write = write(fd, buf, strlen(buf));
  • int n_write = write(fd, buf, strlen(buf));:将 buf 中的字符串写入文件,写入长度为字符串的长度。
    if(n_write != -1){
        printf("write %d byte to file\n", n_write);
    }
  • if(n_write != -1){ ... }:检查写入是否成功。
  • printf("write %d byte to file\n", n_write);:如果写入成功,打印写入的字节数。。
    char *readBuf;
    readBuf = (char *)malloc(sizeof(char) * n_write + 1);
  • char *readBuf;:定义一个字符指针 readBuf,用于存储读取的数据。
  • readBuf = (char *)malloc(sizeof(char) * n_write + 1);:分配足够的内存以存储读取的数据,加一是为了存储字符串终止符 \0
    int n_read = read(fd, readBuf, n_write);
  • int n_read = read(fd, readBuf, n_write);:从文件中读取 n_write 个字节到 readBuf 缓冲区中。
    printf("read %d, context: %s\n", n_read, readBuf);
    close(fd);
    return 0;
}
  • printf("read %d, context: %s\n", n_read, readBuf);:打印读取的字节数和读取的内容。
  • close(fd);:关闭文件描述符。
  • return 0;:程序正常结束,返回0。

输出结果

我们可以看到我们向file1文件中写入了LXL heng shuai!!!l,但是使用read函数读取文件中的内容到readBuf中,但是读取到的字节为0,并且也没有读取到内容,这是因为我们往file1文件中写入了chenlichen handsome后,文件中光标的位置已经到了尾巴,如果想要读取成功需要将光标移动到头,后面再使用此方法

  • 这次我们这样:把写入内容到file1文件中后把文件关闭,读取的时候再重新的打开
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

int main(){

        int fd;
        char *buf = "LXL heng shuai!!!";
        fd = open("./file1",O_RDWR);

        if(fd == -1){
                printf("open file1 failed\n");
                fd = open("./file1",O_RDWR|O_CREAT,0600);
                if(fd > 0)
                        printf("create file1 success\n");
        }
        
        printf("fd = %d\n",fd);
  //    ssize_t write(int fd, const void *buf, size_t count);
 //     write(fd,buf,sizeof(buf));
        int n_write = write(fd,buf,strlen(buf));

        if(n_write != -1)
                printf("write %d byte to file\n",n_write);
        
        close(fd);
        fd = open("./file1",O_RDWR);

        char *readBuf;
        readBuf = (char *)malloc(sizeof(char)*n_write + 1);
   //   ssize_t read(int fd, void *buf, size_t count);
        int n_read = read(fd,readBuf,n_write);

        printf("read %d,context:%s\n",n_read,readBuf);
        close(fd);
        return 0;
}

close(fd);
fd = open("./file1", O_RDWR);

  • close(fd);:关闭文件。
  • fd = open("./file1", O_RDWR);:重新以读写模式打开文件

5. 文件光标移动操作

5.1. 文件光标移动函数lseek函数原型和头文件:
	Linux下:man 2 lseek可以查看对应手册
*/
 
#include <sys/types.h>
#include <unistd.h>
 
off_t lseek(int fd, off_t offset, int whence);
 
off_t			若成功移动光标,函数返回值为光标移动个数,若失败,返回-1
fd				文件描述符
off_t offset	针对whence光标偏移个数 
int whence		光标的绝对位置        
1.SEEK_SET  文件头	      The offset is set to offset bytes.	
2.SEEK_CUR  光标当前位置	The offset is set to its current location plus offset bytes.	
3.SEEK_END  文件内容末尾	The offset is set to the size of the file plus offset bytes.
				
lseek函数作用:将文件读写指针相对whence移动offset个
5.2. 使用lseek函数对文件进行操作:
include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>



int main(){

        int fd;
        char *buf = "LXL heng shuai!!!";
        fd = open("./file1",O_RDWR);

        if(fd == -1){
                printf("open file1 failed\n");
                fd = open("./file1",O_RDWR|O_CREAT,0600);
                if(fd > 0)
                        printf("create file1 success\n");
        }
 printf("fd = %d\n",fd);
  //    ssize_t write(int fd, const void *buf, size_t count);
 //     write(fd,buf,sizeof(buf));
        int n_write = write(fd,buf,strlen(buf));

        if(n_write != -1){
                printf("write %d byte to file\n",n_write);

        }
        // off_t lseek(int fd, off_t offset, int whence);

        char *readBuf;
        readBuf = (char *)malloc(sizeof(char)*n_write + 1);
        /*off_t lseek(int fd, off_t offset, int whence); lseek函数原型*/
        lseek(fd,0, SEEK_SET);          //将光标移动到头
//      lseek(fd,-17, SEEK_END);        //将光标从尾巴移动到头
//      lseek(fd,-17, SEEK_CUR);        //将光标从当前位置移动到头

//      ssize_t read(int fd, void *buf, size_t count);
        int n_read = read(fd,readBuf,n_write);

        printf("read %d,context:%s\n",n_read,readBuf);
        close(fd);
        return 0;
}
                 

输出结果:

针对于我们读取文件时遇到的问题,我们通过lseek函数将光标移动到头,成功读取到file1文件中的内容

lseek(fd, 0, SEEK_SET);

5.3. 使用lseek函数可以的巧妙的计算文件大小:
include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>


int main(){

        int fd;
        char *buf = "LXL heng shuai!!!";
        fd = open("./file1",O_RDWR);      open打开文件file1


        int filesize = lseek(fd,0,SEEK_END);     lseek计算 文件file1中的字符串大小 ,并且赋值给filesize函数
        printf("file's size is:%d\n",filesize);   输出文件中字符串的大小

        close(fd);     关闭文件
        return 0;
}

通过代码运行之后,我们得到了file1文件中的字符串是17个字符,通过ll命令查看,输出的结果与源文件字节大小一致。我们调用lseek函数可以巧妙的查看文件的大小

6. 文件描述符

1、对于内核而言,所有打开文件都由文件描述符引用。文件描述符是一个非负整数。当打开一个现存文件或者创建一个新文件时,内核向进程返回一个文件描述符。当读写一个文件时,用open和creat返回的文件描述符标识该文件,将其作为参数传递给read和write。

按照惯例,WNIXshell使用文件描述符0与进程的标准输入相结合,文件描述符1与标准输出相结合,文件描述符2与标准錯误输出相结合。STDINLFILENO、STDOUTFILENO、STDERR FILENO这几个宏代替了0、1、2这几个数。

2、文件描述符,这个数字在一个进程中表示一个特定含义,当我们on一个文件时,操作系统在内存中构建了一些数据结构来表示这个动态文件,然后返回给应用程序一个数字作为文件描述符,这个数字就和我们内存中维护的这个动态文件的这些数据结构绑定上了,以后我们应用程序如果要操作这个动态文件,只需要用这个文件描述符区分。

3、文件描述符的作用域就是当前进程,出了这个进程文件描述符就没有意义了。

open函数打开文件,打开成功返回一个文件描述符,打开失败,返回-1。

6.1. 文件编程的一般步骤

2.4 查看文件是否存在:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

int main(){

        int fd;         
        fd = open("./file2",O_RDWR|O_CREAT|O_EXCL,0600);

        if(fd == -1)   //如果O_EXCL和O_CREAT同时使用时,文件已经存在就返回-1代表文件存在
        printf("file nice ok!!!\n");
        return 0;
}

2.5 每次写内容都到文件的尾部:

当前我们file1文件中的内容是:

如果直接往里面添加内容会出现错误,会从头开始添加,我们不希望破坏之前的内容,我们可以在open函数的可读可写权限后面或上O_APPEND

6.1. 文件描述符

1、对于内核而言,所有打开文件都由文件描述符引用。文件描述符是一个非负整数。当打开一个现存文件或者创建一个新文件时,内核向进程返回一个文件描述符。当读写一个文件时,用open和creat返回的文件描述符标识该文件,将其作为参数传递给read和write。

按照惯例,WNIXshell使用文件描述符0与进程的标准输入相结合,文件描述符1与标准输出相结合,文件描述符2与标准錯误输出相结合。STDINLFILENO、STDOUTFILENO、STDERR FILENO这几个宏代替了0、1、2这几个数。

2、文件描述符,这个数字在一个进程中表示一个特定含义,当我们on一个文件时,操作系统在内存中构建了一些数据结构来表示这个动态文件,然后返回给应用程序一个数字作为文件描述符,这个数字就和我们内存中维护的这个动态文件的这些数据结构绑定上了,以后我们应用程序如果要操作这个动态文件,只需要用这个文件描述符区分。

3、文件描述符的作用域就是当前进程,出了这个进程文件描述符就没有意义了。

open函数打开文件,打开成功返回一个文件描述符,打开失败,返回-1。

6.2. 文件编程的一般步骤

7. 文件编程练手(一)实现copy指令(面试)

7.1. copy指令在Linux下的应用:

copy 指令在Linux系统是一个常用的指令,就是这样用:

cp demo4.c demo13.c

其中 copy是指令,demo4.c是源文件,demo13.c是目标文件,下面我们就来编程

CP 拷贝复制的意思, src.c源文件 des.c 目标文件

7.2. main函数的参数:
int main(int argc , char **argv)
    //  argc 参数的个数   **argv 指针数组   ,你把他当作成一个二级指针地址
{

	printf("totl params:%d\n",argc);   //   打印参数的总个数
	printf("No :1 params:%s\n",argv[0]);   //  输出运行指令
	printf("No :2 params:%s\n",argv[1]);   //   输出src。c指令
	printf("No :3 params:%s\n",argv[2]);  //    输出desc文件
    return 0;
}

7.3. 使用源文件输出模仿cp 复制源文件的内容。
  • 用文件编程实现copy指令的编程思路:
    1. 打开源文件src.c
    2. 读源文件src.c到buf
    3. 打开/创建目标文件des.c
    4. 将buf写入到目标文件des.c
    5. 关闭两个文件
int main(int argc , char **argv)
{
    int fdSrc;           // 源文件的文件描述符
    int fdDes;           // 目标文件的文件描述符
    char *readBuf = NULL; // 用于存储从源文件读取内容的缓冲区

    // 检查命令行参数数量是否正确
    if(argc != 3){
        printf("pararm error\n"); // 参数错误提示
        exit(-1); // 错误退出程序
    }

	fdSrc = open(argv[1],O_RDWR);   // 打开源文件 ,并且授予可读可写
	
    int size = lseek(fdSrc,0,SEEK_END);  // 获取源文件大小
	lseek(fdSrc,0,SEEK_SET);  // 回到文件开头位置


    //  给指针 readBUf 开辟空间大小
	readBuf = (char *)malloc(sizeof(char)*size + 8);
    
    //  将源文件的内容读取到缓冲区中  
    //	size: 指定一次读取的最大字节数。
    //	这里表示最多读取size字节的数据到 readBuf 中
	int n_read = read(fdSrc,readBuf,size);

    // 打开目标文件,如果没有就创建一个,并且授权用户可读可写
    // 如果目标文件存在,并且有内容,
    // O_TRUNC函数会将目标文件的所有内容清空
	fdDes = open(argv[2],O_RDWR|O_CREAT|O_TRUNC, 0600);

    //  将缓冲区的内容,写到目标文件中
	int n_write = write(fdDes,readBuf,strlen(readBuf));

       //	关闭源文件,以及目标文件 ,防止被破坏。
	close(fdSrc);
	close(fdDes);

	return 0;
}

我们可以看到打开我们cp的new2.c文件,和源文件demo8.c内容是一模一样的,这样就使用文件编程实现了copy指令

8. 文件编程练手(二)修改程序的配置文件(工作常用)

在工作中我们经常会遇到修改配置文件相关的问题,比如以下配置文件:

如:

SPEED=5

LENG=1

SCORE=90

LEVEL=95

8.1. 使用文件编程修改配置文件的指定内容:
  • 编程思路:
    1. 打开配置文件
    2. 读出配置文件内容
    3. 找到LENG字符串的起始位置
    4. 偏移LENG的长度,偏移到数据位置
    5. 更改数据位置的值
    6. 把读出的内容重新写入配置文件
    7. 关闭文件
int main(int argc , char **argv)
{
    int fdSrc;           // 源文件的文件描述符
    char *readBuf = NULL; // 用于存储文件内容的缓冲区

    // 检查命令行参数数量是否正确
    if(argc != 2){
        printf("param error\n"); // 参数错误
        exit(-1); // 退出程序,返回错误码
    }

    // 打开源文件,读写模式
    fdSrc = open(argv[1], O_RDWR);
    if (fdSrc == -1) {
        perror("Error opening source file"); // 打开文件失败
        exit(-1);
    }

    // 获取源文件的大小
    int size = lseek(fdSrc, 0, SEEK_END);
    if (size == -1) {
        perror("Error seeking to end of source file"); // 获取文件大小失败
        close(fdSrc);
        exit(-1);
    }
    lseek(fdSrc, 0, SEEK_SET); // 将文件指针重置到文件开头

    // 为读取的文件内容分配内存
    readBuf = (char *)malloc(sizeof(char) * size + 8); // 多分配一些内存以应对可能的扩展
    if (readBuf == NULL) {
        perror("Error allocating memory"); // 内存分配失败
        close(fdSrc);
        exit(-1);
    }

    // 从文件中读取内容到缓冲区
    int n_read = read(fdSrc, readBuf, size);
    if (n_read == -1) {
        perror("Error reading from source file"); // 读取文件失败
        free(readBuf);
        close(fdSrc);
        exit(-1);
    }
    
    // 查找 "LENG=" 字符串
    char *p = strstr(readBuf, "LENG=");
    if (p == NULL) {
        printf("not found\n"); // 如果没有找到 "LENG=",打印错误信息并退出
        exit(-1);
    }
    
    // 移动指针到 "LENG=" 后面的字符位置
    p = p + strlen("LENG=");
    *p = '8'; // 将该位置的字符修改为 '8'

    // 将修改后的内容写回文件
    lseek(fdSrc, 0, SEEK_SET); // 将文件指针重置到文件开头
    int n_write = write(fdSrc, readBuf, strlen(readBuf));
    if (n_write == -1) {
        perror("Error writing to source file"); // 写入文件失败
        free(readBuf);
        close(fdSrc);
        exit(-1);
    }

    // 释放内存并关闭文件描述符
    free(readBuf);
    close(fdSrc);

    return 0; // 程序成功结束
}

代码的目的是打开一个配置文件,查找特定的配置项(以 LENG= 开头的项),并将该配置项的值修改为 8

  • 代码实现

8.2. 修改字符串部分的解释
char *p = strstr(readBuf, "LENG=");
if (p == NULL) {
    printf("not found\n");
    exit(-1);
}
p = p+ strlen("LENG=");
*p = '8';
  • strstr(readBuf, "LENG=") 查找 "LENG=" 字符串的位置。

/*

Linux下 man strstr可以查看对应手册

*/

#include <string.h>

char *strstr(const char *haystack, const char *needle); //字符串查找函数

char * 返回值为要查找的字符串的第一个字符的指针

const char *haystack 待查找的原始字符串

const char *needle 为要查找的内容

  • p = p + strlen("LENG=") 将指针 p 移动到 "LENG=" 后面的字符位置。
  • *p = '8'; 将该位置的字符修改为 '8'

这样,如果原始文件中有 "LENG=5",它将被修改为 "LENG=8"

9. 写一个整数、结构体和结构体数组到文件

前面我们都是写字符串到文件中,那可不可以写入整数或者结构体呢?

ssize_t write(int fd, const void *buf, size_t count);
ssize_t read(int fd, void *buf, size_t count);	
 
这两个是写and读的函数原型,我们可以发现第二个参数都是:void *buf
这样我们即可以写入字符串,又可以将整数或者结构体的地址传递过去,进行读和写
9.1. 写一个整数到文件去

int main(int argc , char **argv)
{
    int fd;
    int date1 = 100;
    int date2 ;

    // 打开 file3的文件
    fd = open("./file3",O_RDWR);

    // 写入数据。
        // 将date1的数据写到file3中
        // sizeof( int)  文件大小   一个整数 4个字节 
    int n_write = write(fd,&date1,sizeof( int));

    //  将文件的鼠标归零
    lseek(fd,0,SEEK_SET);
    // 读数据。
        // 将file3的数据写到date2z中
        // sizeof( int)  文件大小   一个整数 4个字节 
    int n_read = read(fd,&date2,sizeof( int));

    // 输出这个整型数
    printf("int %d\n",date2;
    // 关闭file3 文件,以免损坏
    close(fd);
    return 0; 

}

运行结果

成功向file3文件中写入了一个整数,并且把文件中的整数读取到了data2中,最终输出结果

注意这个d,在file3文件中,是以阿斯克码表的形式,展现的,只不过是不是常规中我们认识的那种阿斯克码表,并不影响我们的读写操作。

9.2. 写一个结构体到文件中去
// 结构体
struct Test
{
	int a;
	int c;
};

int main(int argc , char **argv)
{
    int fd;
    struct Test date1 = {100,'a'};
    struct Test date2 ;
     // 打开 file3的文件
    fd = open("./file3",O_RDWR);
     // 写入数据。
        // 将date1的数据写到file3中
        // sizeof(struct Test) 文件大小   一个结构体的大小 
    int n_write = write(fd,&date1,sizeof(struct Test));

    //  将文件的鼠标归零
    lseek(fd,0,SEEK_SET);
    // 读数据。
        // 将file3的数据写到date2z中
      
    int n_read = read(fd,&date2,sizeof(struct Test));

     // 输出整形和字符
    printf("int %d,char %c\n",date2.a,date2.c);
    close(fd);
    return 0; 

}

代码实现

9.3. 数组结构体写入文件中

代码

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

struct Test
{
    int a;
    char c;  // 使用char类型存储字符
};

int main(int argc, char **argv)
{
    int fd;
    struct Test date1[2] = {{100, 'a'}, {123, 'l'}};  // 初始化数据
    struct Test date2[2];  // 用于读取数据

    // 打开文件,如果文件不存在则创建文件
    fd = open("./file4", O_RDWR | O_CREAT, 0600);
    if (fd < 0) {
        perror("打开文件出错");
        return 1;
    }

    // 写入数据到文件
    int n_write = write(fd, &date1, sizeof(struct Test) * 2);
    if (n_write < 0) {
        perror("写入文件出错");
        close(fd);
        return 1;
    }

    // 将文件指针移动到文件开头,以便重新读取
    if (lseek(fd, 0, SEEK_SET) < 0) {
        perror("文件定位出错");
        close(fd);
        return 1;
    }

    // 从文件中读取数据到date2
    int n_read = read(fd, &date2, sizeof(struct Test) * 2);
    if (n_read < 0) {
        perror("读取文件出错");
        close(fd);
        return 1;
    }

    // 输出读取到的数据
    printf("int %d, char %c\n", date2[0].a, date2[0].c);
    printf("int %d, char %c\n", date2[1].a, date2[1].c);

    // 关闭文件
    close(fd);
    return 0;
}

代码实现:

10. 标准C库对文件操作引入

10.1. fopen与open的区别

1、来源不同

open是unix系统调用函数(包括Linux),返回的是文件描述符,它是文件描述符表里的索引。 fopen是ANSIC标准中的C语言库函数,在不同的系统中应该调不同的内核api,返回的是一个指向文件结构的指针。

2、移植性

从来源看,fopen是C标准函数,因此拥有良好的移植性,而open是unix系统调用,移植性有限,如windows下相似的功能使用api函数CreatFile。

3、使用范围

open返回文件描述符,而文件描述符是unnix系统下的重要概念,unix下的一切设备都是文件的形式操作,如网络套接字、硬件设备等、当然包括操作普通正规文件(Regular File) Fopen是从来操纵普通正规文件(Regular File)的

4、 文件IO层次

如果从文件IO的角度来看,open属于低级IO函数,fopen属于高级IO函数,低级和高级的简单区分标准是:谁离系统内核更近,低级文件IO运行在内核态、高级文件IO运行在用户态。

5、 缓冲区

open没缓冲区(多次在用户态和内核态切换,执行速度慢,效率低),

fopen有缓冲区(在缓冲区读写后一次性写入文件,执行速度快,效率高)

更多详细介绍观看此路径总结open与fopen的区别 - NickyYe - 博客园,念这些啰里啰唆的概念不是我的风格!!!

主打的就是一个拽!!!

11. 标准C库对文件的操作

11.1. fopen函数

fopen函数是C语言中的一个文件操作函数,主要用于打开一个文件。这个函数需要包含头文件stdio.h。

fopen函数的原型为:FILE *fopen(const char *path, const char *mode);

其中,参数path为文件路径,mode为打开文件的模式。

打开文件的常用模式有:

  • “r”:以只读方式打开文件,如果文件不存在则返回NULL。
  • “w”:以写入方式打开文件,如果文件存在则清空其内容,如果不存在则创建新文件。
  • “a”:以追加方式打开文件,如果文件不存在则创建新文件,并将写入的数据附加到文件末尾。
  • “rb”:以二进制读模式打开文件,只允许读取操作。
  • “wb”:以二进制写模式打开文件,只允许写入操作。
  • “ab”:以二进制追加模式打开文件,允许在文件末尾进行追加操作。
  • “r+”:以读写方式打开文件,保留文件中未被覆盖的内容。
  • “w+”:以读写方式打开文件,如果文件存在则清空其内容,如果不存在则创建新文件。
  • “a+”:以追加读写方式打开文件,允许在文件末尾进行追加操作。

需要注意的是C库函数如果打开文件的权限不对,很有可能之后所做的操作都是没用的。所以要明确自己想要对文件进行什么操作,然后选择对应的文件权限。

fopen的返回值:如果文件顺利打开,fopen函数会返回一个指向该流的文件指针;如果文件打开失败,则返回NULL,并把错误代码存在errno中。在使用完文件后,一定要记得关闭它,因为不关闭文件可能会造成文件所占用内存泄露,以及在下次访问文件时出现问题。

11.2. fwrite函数

fwrite函数是C语言标准库中的一个文件处理函数,主要用于向指定的文件中写入若干数据块。这个函数可以操作二进制文件和文本文件,具有很大的灵活性。

//参数解释如下:ptr是一个指针,它指向要写入数据的内存首地址;size表示要写入数据的单个元素(即基本单元)的字节大小;nmemb是要写

fwrite的返回值和它的第三个参数有关系:

int n_fwrite = fwrite(ptr,sizeof(char),strlen(ptr),fd);

//如果按照每次写入一个字节,写ptr次将字节存放到fd文件中,那么fwrite的返回值就是存放的次数strlen(ptr)

int n_fwrite = fwrite(ptr,sizeof(char)*strlen(ptr),1,fd);

//如果按照第二种直接全部写入文件里,只写入一次,那么fwrite的返回值就是存放的次数1次

11.3. fread函数

fread函数是C语言中的一个文件操作函数,主要用于从指定的文件中读取若干字节数据到内存缓冲区中。

这个函数的原型定义如下:size_t fread (void *ptr, size_t size, size_t count, FILE *stream)。

//在这个函数中,ptr是一个指针,指向要读取数据的内存缓冲区的首地址;size表示每个数据项(基本单元)的字节数;count则代表要读取的数据项(基本单元)的数量;而stream则是指向目标文件的文件指针。

fread的返回值和fwrite的一样取决于它的第三个参数,它读取几次,最终的返回值就是多少。

11.4. fseek函数

fseek函数是C语言标准库中的一个函数,主要用于改变文件流的读写位置。

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

//fseek函数有三个参数,第一个参数是需要操作的文件指针,通常是一个指向FILE对象的指针。第二个参数是光标的偏移量,可以是一个长整型数,它表示要移动的字节数,如果这个数是正数则表示正向偏移,如果是负数则表示负向偏移。第三个参数为确定起点模式,具体来说,它是设定从文件的哪个位置开始进行偏移。可能的取值有:SEEK_SET(文件开头)、SEEK_CUR(当前位置)和SEEK_END(文件结尾)。

如果操作成功,则返回0;反之,如果执行失败(例如,当offset超过文件自身大小或超出long的正数范围2G),则不改变stream指向的位置,并返回一个非0值。

!!!注意,这里的fseek函数和lseek函数有区别,lseek的返回值是光标在当前位置到文件开头的距离,而fseek函数的返回值是0,所以说这里的fseek函数不能拿来通过偏移来求文件大小。

11.5. ftell函数

ftell函数是C语言标准库中的一个函数,主要用于获取当前文件位置指针的值,即从文件开头到当前位置的字节数量。

long ftell(FILE *stream);

该函数只有一个参数就是打开的FILE所指向的指针

如果操作成功,则返回从文件开头算起的字节数量;反之,如果执行失败(例如,当stream为NULL时),则返回-1L。

11.6. fclose函数

fclose函数是C语言标准库中的一个函数,其功能主要是关闭一个已经打开的文件流。

fclose函数原型为:int fclose (FILE *stream)

//其中,stream是一个指向FILE对象的指针,该FILE对象指定了要被关闭的流。

如果流成功关闭,fclose函数将返回0;反之,如果调用失败,那么函数将返回EOF(-1)。

11.7. 标准C库对文件打开写入读取关闭编程实现:
#include <stdio.h>
#include <string.h>

int main()
{
    // 文件指针
    FILE *fp;
    // 要写入文件的字符串
    char *str = "lxl hen shuai";
    // 用于读取文件内容的缓冲区,初始化为0
    char readbuf[128] = {0};
    
    // 打开或创建文件,模式为 "w+",即读写模式,如果文件存在则清空文件内容
    fp = fopen("./liu.txt", "w+");
    // 如果文件成功打开
    if(fp != NULL)
    {
        printf("成功打开或创建文件\n");
    }

    // 将字符串写入文件,使用 fwrite
    // fwrite 的参数依次为:要写入的数据,单个数据的大小,数据的个数,文件指针
    fwrite(str, sizeof(char), strlen(str), fp);
    //  每一次写一个字符,写strlen(str)次,

    // 将文件指针重新定位到文件开头,准备读取文件
    fseek(fp, 0, SEEK_SET);

    // 从文件中读取数据,使用 fread
    // fread 的参数依次为:读取到的缓冲区,单个数据的大小,数据的个数,文件指针
    fread(readbuf, sizeof(char), strlen(str), fp);
     //  每一次读一个字符,写strlen(str)次,
    // 输出从文件中读取的字符串
    printf("读取的字符串为: %s \n", readbuf);

    // 关闭文件
    fclose(fp);
    return 0;	
}
  • 另一个写法
#include <stdio.h>
#include <string.h>

int main()
{
    // 文件指针
    FILE *fp;
    // 要写入文件的字符串
    char *str = "lxl hen shuai";
    // 用于读取文件内容的缓冲区,初始化为0
    char readbuf[128] = {0};
    
    // 打开或创建文件,模式为 "w+",即读写模式,如果文件存在则清空文件内容
    fp = fopen("./liu.txt", "w+");
    // 如果文件成功打开
    if(fp != NULL)
    {
        printf("成功打开或创建文件\n");
    }

    // 将字符串写入文件,使用 fwrite
    // fwrite 的参数依次为:要写入的数据,单个数据的大小,数据的个数,文件指针
    fwrite(str, sizeof(char)*strlen(str), 1, fp);
    //  每一次写一乘strlen(str)个字符,写1次,

    // 将文件指针重新定位到文件开头,准备读取文件
    fseek(fp, 0, SEEK_SET);

    // 从文件中读取数据,使用 fread
    // fread 的参数依次为:读取到的缓冲区,单个数据的大小,数据的个数,文件指针
    fread(readbuf, sizeof(char)*strlen(str), 1, fp);
     //  每一次读一乘strlen(str)个字符,读1次,
    // 输出从文件中读取的字符串
    printf("读取的字符串为: %s \n", readbuf);

    // 关闭文件
    fclose(fp);
    return 0;	
}

12. 标准C库写入结构体到文件

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

// 定义一个结构体 Test,包含一个整数 a 和一个字符 c
struct Test
{
    int a;
    char c;
};

int main()
{
    // 文件指针
    FILE *fp;
    // 创建一个结构体实例 date,并初始化其成员
    struct Test date = {100, 'a'};
    // 创建一个未初始化的结构体实例 date2,用于存储从文件读取的数据
    struct Test date2;
    
    // 打开或创建文件,模式为 "w+",即读写模式,如果文件存在则清空文件内容
    fp = fopen("./liu.txt", "w+");
    // 如果文件成功打开
    if(fp != NULL)
    {
        printf("成功打开或创建文件\n");
    }
    
    // 将结构体 date 写入文件,使用 fwrite
    // fwrite 的参数依次为:要写入的数据指针,单个数据的大小,数据的个数,文件指针
    int n_write = fwrite(&date, sizeof(struct Test), 1, fp);
    //sizeof(struct Test) 为整个结构体大小
    
    // 将文件指针重新定位到文件开头,准备读取文件
    fseek(fp, 0, SEEK_SET);
    
    // 从文件中读取数据到结构体 date2 中,使用 fread
    // fread 的参数依次为:读取到的缓冲区,单个数据的大小,数据的个数,文件指针
    int n_fread = fread(&date2, sizeof(struct Test), 1, fp);
    
    // 输出从文件中读取的结构体成员值
    printf("读取的 a = %d, 读取的 c = %c \n", date2.a, date2.c);
    // 输出写入和读取的结构体个数
    printf("写入的个数 = %d, 读取的个数 = %d\n", n_write, n_fread);
    
    // 关闭文件
    fclose(fp);
    return 0;	
}
12.1. fputc / fputs 函数写一个结构体到文件:

fputc函数是C语言中的一种输入/输出函数,它的主要功能是将指定的字符写入到指定的文件中。

函数的原型为 int fputc(int char, FILE *stream),

其中,char代表需要写入的字符,这个字符可以是字符常量或变量;而stream则是一个指向目标文件的文件指针。

fputc如果成功写入后的返回值是它会返回写入文件字符的对应的ASCII码。如果发生错误,fputc函数将返回EOF,并设置错误标识符。

fputs函数是C语言中的一个标准库函数,定义在头文件 ”stdio.h” 中。它的主要作用是将指定的字符串写入目标文件中。

fputs()函数的原型如下:int fputs(const char *str, FILE *stream);

其中,str代表要输出的字符串,可以是字符数组名或字符指针变量名;而stream则表示向何种流中输出,可以是标准输出流stdout,也可以是文件流。

fputs函数如果成功向文件写入一个字符,返回一个非负整数。如果发生错误,fputs函数将返回EOF,并设置错误标识符。

#include <stdio.h>

int main()
{
     FILE *fp;
  
    fp = fopen("./test1.txt","w+");   // 打开./test1.txt 文件,可读可写
    fputc('a',fp);    // 将字符a 输入/test1.txt 文件
     fclose(fp);      // 关闭 /test1.txt 文件
 
     return 0;
  }

运行结果:

fputs函数如果成功向文件写入一个字符串,返回一个非负整数。如果发生错误,fputs函数将返回EOF,并设置错误标识符。

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

int main()
{
	FILE *fp;  
	int i;
	char *str ="lxl heng shuai o!";  // 字符串指针
	fp = fopen("./test2.txt","w+");  
	int len = strlen(str);
	for(i = 0;i<len;i++){  
	
		fputc(*str,fp);   //使用fputc函数输出结果
		str ++;          // z指针后移
	}
	fclose(fp);     //关闭文件
	
	return 0;
}

代码实现:

12.2. fgetc / fgets 函数

fgetc函数是C语言中的一个标准库函数,用于从文件中读取一个字符。

它的原型如下:int fgetc(FILE *stream);其中,stream是一个指向文件流的指针。

fgetc函数会从指定的文件流中读取一个字符,并将其作为整数值返回。如果到达文件末尾或发生错误,则返回EOF(通常是-1)。

需要注意的是,fgetc函数不会在读取字符后自动将文件流的位置指针向前移动一位,因此需要手动调用fseek或fseeko函数来更新位置指针。

fgets函数是C语言中的一个标准库函数,用于从指定的文件流中读取一行字符串,并将其存储到指定的字符数组中。

fgets函数的原型为:char *fgets(char *str, int n, FILE *stream);

其中,str是一个字符数组指针,用于存储读取到的字符串;n是一个整数,表示要读取的最大字符数(包括空字符'\0');stream是一个指向文件流的指针。

需要注意的是,fgets函数会将读取到的字符串存储到第一个参数指定的字符数组中,并返回该字符数组的地址。当读取 (n-1) 个字符时,或者读取到换行符时,或者到达文件末尾时,fgets函数会停止读取。这意味着如果文件中的一行内容小于n个字符,且以换行符结束,那么fgets函数只会读取这一行的内容。

fgets函数的返回值是一个字符指针,指向读取到的字符串的首地址。如果读取过程中发生了错误(如文件打开失败、读取失败等),则返回NULL。

代码示例:使用fputc和fgetc对文件进行写入和读取

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

int main()
{
	FILE *fp;  
    char c = 'L';
  
  
    fp = fopen("./test3.txt","w+");   // 打开./test1.txt 文件,可读可写
   
    fputc(c,fp);    // 将字符a 输入/test3.txt 文件

    fseek(fp,0,SEEK_SET);
  
    char data =  fgetc(fp);

	
    printf("%c\n",data);
    
   
		
	fclose(fp);     //关闭文件
	
	return 0;
}

代码示例:使用fputs和fgets对文件进行写入和读取

#include "stdio.h"
#include "string.h"
int main()
{
	FILE *fp;
	char *str = "hello world!";
	char readBuf[15] = {0};

	fp = fopen("./test.txt","w+");
	
    fputs(str,fp);
	
	fseek(fp,0,SEEK_SET);
	char *p  = fgets(readBuf,strlen(str)+1,fp);
	if(p != NULL)
	{
		printf("content:%s\n",readBuf);
		printf("end!\n");
    }

	fclose(fp);

	return 0;
}
12.3. feof函数

feof函数是C语言的一个标准库函数,定义在 “stdio.h” 头文件中。它的主要作用是检测文件流是否已经到达了文件末尾(EOF)。

该函数的原型为:int feof(FILE *stream);

其中,参数stream是一个指向FILE类型的指针,代表了需要检测的文件流。

值得注意的是,feof函数只有在确实遇到了文件结束标识符的情况下才会返回非零值,否则返回零。因此,可以通过检查feof函数的返回值来判断当前文件流是否已经读取到了末尾。

下面是关于feof函数使用的一个例子:使用fputs写入字符串,然后使用fgetc读出字符串

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

int main()
{
	FILE *fp;
	char *str = "hello world!";
	char c;

	fp = fopen("./test.txt","w+");
	
    fputs(str,fp);
	
	fseek(fp,0,SEEK_SET);
	while(!feof(fp)){

		c = fgetc(fp);
		printf("%c",c);
	}
	
	
	fclose(fp);
	printf("\nover\n");
	return 0;
}

实现效果:


点个赞吧!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2182482.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

怎么将电脑的“任务栏”设置为“透明”状态?任务栏透明度设置攻略!扩散!

怎么将电脑的“任务栏”设置为“透明”状态? ■ 在Windows系统中&#xff0c;电脑任务栏设置为透明状态的方式有很多种&#xff0c;可以通过电脑自带的个性化设置&#xff0c;或者注册表编辑&#xff0c;还可以通过第三方软件进行任务栏丰富的修改。 在Windows操作系统中&…

【UEFI基础】BIOS下的NVMe

什么是NVMe NVMe全称NonVolatile Memory Express&#xff08;非易失性内存主机控制器接口规范&#xff09;&#xff0c;其官方&#xff08;NVMe官网NVM Express&#xff09;定义将其描述为“一个开放的标准和信息集合&#xff0c;以充分释放非易失性存储在从移动端到数据中心的…

【YOLO系列】YOLOv11正式发布!

Yolov11发布文档 代码链接 了解Ultralytics YOLO11的所有突破性功能&#xff0c;这是我们最新的人工智能模型&#xff0c;具有无与伦比的准确性和效率。 我们很高兴向大家介绍Ultralytics型号的下一次进化&#xff1a;YOLO11&#xff01;YOLO11建立在以前YOLO模型版本令人印象…

【易上手快捷开发新框架技术】用Flet从零开始分步骤循序渐进编程实现购物清单助手手机应用app示例掰烂嚼碎深度讲解源代码IDE运行和调试通过截图为证

传奇开心果编程实例微博文 序言首先&#xff0c;明确任务&#xff0c;任务驱动&#xff1a;其次&#xff0c;开发工具选型考虑&#xff1a;第三&#xff0c;编程思路和应用结构设计&#xff1a; 第一步&#xff1a;从零开始搭建移动应用雏形框架第二步&#xff1a;设置窗口大小…

PCL 移除点云边缘不连续的点

目录 一、概述 1.1原理 1.2实现步骤 1.3应用场景 二、代码实现 2.1关键函数 2.1.1 法向量计算 2.1.2 边界检测和移除 2.1.3 边界检测和移除 2.2完整代码 三、实现效果 PCL点云算法汇总及实战案例汇总的目录地址链接&#xff1a; PCL点云算法与项目实战案例汇总&…

React学习笔记(4.0)

json-server实现数据Mock 1.项目中安装json-server npm i -D json-server 2.准备一个json文件 3.添加启动命令【package.json中配置】 "server":"json-server ./server/data.json --port 8888" 该命令中&#xff0c;路径就是自己创建的json文件路径&…

【C++】BitSet和Bloom_Filter

前言&#xff1a; 在计算机图形学中&#xff0c;位图&#xff08;Bitmap&#xff09;也称为光栅图&#xff0c;是由像素点组成的图像表示方式。在 C 编程中&#xff0c;位图可以通过特定的函数和数据结构来进行处理和操作。 BitMap 位图&#xff08;BitMap&#xff09;是一种数…

uniapp中uni.request的统一封装 (ts版)

文章目录 前言一、我们为什么要去封装&#xff1f;二、具体实现1.创建一个请求封装文件&#xff1a;2.封装 uni.request&#xff1a;3.如何去使用&#xff1f; 总结 前言 在uniapp中如何去更简洁高效的发送我们的请求&#xff0c;下面就介绍了uni.request()二次封装。 一、我们…

超强大的 Nginx 可视化管理工具

今天给大家介绍一款 Nginx 可视化管理界面&#xff0c;非常好用&#xff0c;小白也能立马上手。 nginx-proxy-manager 是一个反向代理管理系统&#xff0c;它基于 NGINX&#xff0c;具有漂亮干净的 Web UI。还可以获得受信任的 SSL 证书&#xff0c;并通过单独的配置、自定义和…

【计算机视觉】ch1-Introduction

相机模型与成像 1. 世界坐标系 (World Coordinate System) 世界坐标系是指物体在真实世界中的位置和方向的表示方式。在计算机视觉和图像处理领域&#xff0c;世界坐标系通常是一个全局坐标系统&#xff0c;描述了摄像机拍摄到的物体在实际三维空间中的位置。它是所有其他坐标…

【嵌入式开发】可编程4k蓝牙摄像头点击器

今天讲解的是一款可编程摄像头蓝牙点击器&#xff0c;支持离线文字识别 外观如下 B站有使用视频 链接: B站使用 下面是具体使用步骤&#xff08;主要根据B站视频使用流程总结&#xff09;&#xff0c;文末附源码 全是干货 执行main代码 代码运行后&#xff0c;检查摄像头…

【电机-概述及分类】

文章目录 第1章1-1 电机的定义1-2 电机的构成要素1-3 电机的分类1-3-1 直流电机1-3-1-1 永磁励磁型直流电机1-3-1-2 电磁铁励磁型直流电机 第1章 重新认识电机的体系 电机包括许多种类。换个角度来看&#xff0c;并没有完美的电机&#xff0c;某种电机具有所谓A的优点&#xf…

docker 搭建minimalist-web-notepad

亲测可用 服务器 ubuntu sudo mkdir -p /root/docker/note# 切换到rootsu -chmod -R 755 /root/docker/notedocker pull carlosa/minimalist-web-notepad# 容器端口80 映射到 服务器7001端口&#xff0c;记得开防火墙 docker run -d --name notepad --restart always -p 7001…

u盘格式化后数据能恢复吗?2024年Top4恢复神器来帮忙

在这个电脑和手机满天飞的时代&#xff0c;U盘是我们用来存东西和传文件的得力助手&#xff0c;特别重要。但是&#xff0c;有时候U盘可能会不小心被格式化了&#xff0c;里面的重要文件就不见了。那么&#xff0c;U盘格式化后的数据还能恢复吗&#xff1f;当然可以。今天会告诉…

揭秘一下平时我们下载的python库跑到哪里了呢???

&#xff08;阅读之前&#xff0c;祝福大家国庆假期快乐&#xff0c;以及真诚的祝福我们的祖国越来越强大&#xff09;在那天的课上&#xff0c;老师问了我们这样一个问题&#xff1a;你们知道你们平时pip install下载库&#xff0c;下载好了&#xff0c;你们的库是下载到哪里了…

智邦国际ERP系统存在SQL注入漏洞

漏洞描述 智邦国际ERP系统是一款全面的企业资源规划工具&#xff0c;其主要目标是协助组织实现业务流程自动化和提升效率。此系统具备多种功能模块&#xff0c;包括但不限于财务管理、采办管理、销售操作、库存控制和生产过程管理&#xff0c;都能够满足企业多元化的业务需求。…

Ubuntu Server 20.04 64bit定时备份MySQL8.0.36数据库数据

一、编写sh脚本 常见备份命令介绍 我选用的是mysqldump命令&#xff0c;命令使用简介 [root]> mysqldump -helpUsage: mysqldump [OPTIONS] database_name [tables] OR mysqldump [OPTIONS] --databases [OPTIONS] DB1 [DB2 DB3...] OR mysqldump [OPTIONS] --all…

朝天椒USB Server为赛力斯集团解决网银U盾难题

近期&#xff0c;赛力斯集团的财务部门引进了朝天椒USB Server&#xff0c;用几台USB服务器解决了集团困扰多年的银行U盾管理难题。 一、背景 赛力斯集团是一家以新能源汽车为核心业务的科技制造企业&#xff0c;A股上市公司&#xff0c;中国企业500强。赛力斯集团作为一家大型…

基于JAVA+SpringBoot+Vue的电商平台的设计与实现

基于JAVASpringBootVue的电商平台的设计与实现 前言 ✌全网粉丝20W,csdn特邀作者、博客专家、CSDN[新星计划]导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末附源码下载链接&#x1f345…

如何利用 StarRocks 加速 Iceberg 数据湖的查询效率

数据湖作为一种存储各种类型数据的集中式存储系统&#xff0c;以其灵活性、可扩展性和低成本的优势受到越来越多企业的青睐。然而&#xff0c;数据湖虽然降低了数据存储成本&#xff0c;但在数据分析尤其是实时数据分析场景下&#xff0c;其性能仍存在一定瓶颈。 本文将探讨如何…