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套接字):
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
函数用于打开指定路径名的文件。
- 语法:
int open(const char *pathname, int flags);
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. 源代码详解:
- 头文件提供了所需的函数和类型定义:
sys/types.h
和sys/stat.h
提供文件相关的系统数据类型。fcntl.h
提供文件控制选项和函数,如open
。stdio.h
提供标准输入输出函数,如printf
。
- 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); // OFFreturn 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); // OFFreturn 0;
}
fd = open("./file1", O_RDWR | O_TRUNC);
:
open
函数用于打开文件file1
。- 标志参数:
-
O_RDWR
:以读写模式打开文件。O_TRUNC
:如果文件存在,打开文件时会将其长度截断为 0 字节,即清空文件内容。
- 如果文件成功打开,
open
函数返回一个文件描述符(非负整数);如果失败,返回-1
。 - 总结:
这段代码执行以下操作:
- 打开文件
file1
。如果文件存在,以读写模式打开,并使用O_TRUNC
标志将文件内容清空(截断为 0 字节)。如果文件不存在,则打开失败,open
返回-1
。 - 如果文件成功打开,将字符串
"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 函数有三个描述符
-
- int fd 文件描述符
open函数打开文件之后,都会有一个文件描述符,后续通过文件描述符对文件进行操作
-
- const void *buf 无类型的指针 (缓冲区)
指向要写入数据的缓冲区的指针。它通常是一个 void
类型的指针,因此可以指向任何类型的数据。
-
- 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!!!"写入到文件中
- 注意 :
- 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> // mallocint 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.h
和sys/stat.h
:提供文件相关的系统数据类型和函数。fcntl.h
:提供文件控制选项和函数,如open
。stdio.h
:提供标准输入输出函数,如printf
。unistd.h
:提供POSIX操作系统API,如read
和write
。string.h
:提供字符串操作函数,如strlen
。stdlib.h
:提供通用工具函数,如malloc
和free
。
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打开文件file1int 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指令的编程思路:
-
- 打开源文件src.c
- 读源文件src.c到buf
- 打开/创建目标文件des.c
- 将buf写入到目标文件des.c
- 关闭两个文件
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. 使用文件编程修改配置文件的指定内容:
- 编程思路:
-
- 打开配置文件
- 读出配置文件内容
- 找到LENG字符串的起始位置
- 偏移LENG的长度,偏移到数据位置
- 更改数据位置的值
- 把读出的内容重新写入配置文件
- 关闭文件
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;}// 从文件中读取数据到date2int 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";// 用于读取文件内容的缓冲区,初始化为0char 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";// 用于读取文件内容的缓冲区,初始化为0char 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;
}
实现效果:
点个赞吧!!!