目录
2.4 O_EXCL可用来判断文件是否存在(配合O_CREAT使用)
目录大纲:
1.文件编程概述
本节是Linux下对文件进行的相关编程。也就是操作相关API对文件进行操作。
涉及到的API:(open;read;write;)
2.文件打开及创建
2.1 API函数:open函数
这个函数定义在 POSIX 标准中,通常在 Unix-like 系统的 <fcntl.h>
头文件中声明。
可以在Linux下查看函数基本信息(cmd:man 2 open)进行使用。
使用示例
1.包含头文件:
#include <sys/types.h> // 引入系统类型定义,如打开文件所需的文件描述符类型
#include <sys/stat.h> // 引入用于获取文件状态的函数和宏
#include <fcntl.h> // 引入文件操作相关的函数,如open()
2.函数原型:
1.int open(const char *pathname, int flags);
2.int open(const char *pathname, int flags, mode_t mode);
3.int creat(const char *pathname, mode_t mode);
open()返回一个文件描述符
int open(const char *pathname, int flags, mode_t mode);打开/创建一个新的文件,返回值类型为整形(
-
成功时,
open
函数返回一个新的文件描述符,这是一个非负整数,用于以后对文件的读写操作。 -
出错时,返回
-1
,并设置全局变量errno
来指示错误类型。
)。
1.参数解释
1.const char *pathname:指针(指向字符串),该字符串包含打开/创建的文件的路径名; pathname就是路径名(绝对路径、相对路径都可以)。
2.int flags
:整数;flags表示如何打开文件、对文件的访问类型。
-
它可以是以下值的组合(通过按位或操作
|
):-
O_RDONLY
:只读打开。 -
O_WRONLY
:只写打开。 -
O_RDWR
:读写打开。以上三个常数中一般使用一个,下列常数是可选择的。
-
O_APPEND
:写入时追加到文件末尾。 (从尾端写入) -
O_CREAT
:如果文件不存在,则创建它。 -
O_EXCL
:与O_CREAT
一起使用,如果文件已存在则返回错误。 -
O_TRUNC
:如果文件已存在,则截断它(即清空文件内容)。 -
O_NONBLOCK
或O_NDELAY
:以非阻塞模式打开文件。 -
还有很多其他标志,具体请参考系统手册。
-
3.mode_t mode
:mode_t类型;
这个参数仅在设置了 O_CREAT
标志时才有意义,它指定了如果需要创建文件时,文件的权限模式(权限模式通常是一个八进制数,例如 0644
,表示文件所有者具有读写权限(6
),而组用户和其他用户具有只读权限 )。
2.2 打开文件(open关键:返回文件描述符)
#include <sys/types.h> // 引入系统类型定义,如打开文件所需的文件描述符类型
#include <sys/stat.h> // 引入用于获取文件状态的函数和宏
#include <fcntl.h> // 引入文件操作相关的函数,如open()
#include <stdio.h> // 引入标准输入输出库,用于打印信息
int main() // 程序的主函数
{
int fd; // 定义一个整数变量fd,用于存储文件描述符
fd = open("./file1", O_RDWR); // 调用open()函数,尝试以读写模式打开./file1文件
// O_RDWR标志表示可以读取和写入文件
printf("fd = %d\\n", fd); // 如果open()成功,打印文件描述符fd的值
return 0; // 程序正常退出,返回0
}
2.3 创建并且打开文件(O_CREAT)
#include <sys/types.h> // 引入系统类型定义,如文件描述符类型等
#include <sys/stat.h> // 引入文件状态相关的宏和结构体定义
#include <fcntl.h> // 引入文件控制选项,如open()函数和标志位
#include <stdio.h> // 引入标准输入输出库,用于打印信息
int main() // 程序的主函数
{
int fd; // 定义一个整数变量fd,用于存储文件描述符
// 尝试以读写模式打开当前目录下的file1文件
fd = open("./file1", O_RDWR);
// 检查open()函数的返回值,如果返回-1表示打开文件失败
if(fd == -1){
printf("open file1 failed.\\n"); // 打印错误信息
}
// 如果第一次打开失败,尝试以读写模式创建文件(如果不存在)并设置权限为600(-rw-------)
// O_CREAT标志表示如果文件不存在则创建,第三个参数是文件的权限
fd = open("./file1", O_RDWR|O_CREAT, 0600);
// 打印文件描述符的值,无论打开成功与否
printf("fd = %d\\n", fd);
// 程序结束,但请注意,这里没有关闭文件描述符,实际使用中应该在返回前关闭
return 0;
}
使用命令查看我们创建的文件,
ammy@jammy-virtual-machine:~/File programming$ ls -l
total 24
-rwxrwxr-x 1 jammy jammy 16048 4月 17 20:21 a.out
-rw-rw-r-- 1 jammy jammy 175 4月 17 19:58 demo1.c
-rw-rw-r-- 1 jammy jammy 326 4月 17 20:20 demo2.c
-rw------- 1 jammy jammy 0 4月 17 20:21 file1
2.4 O_EXCL可用来判断文件是否存在(配合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|O_CREAT|O_EXCL);// O_EXCL判断文件是否存在
if(fd == -1){
printf("create file1 failed\n");
}
return 0;
}
2.5 O_APPEND
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main()
{
int fd;
char *buf = "this is buf";
fd = open("./file1",O_RDWR|O_APPEND); //O_APPEND在文件末尾后添加内容
int n_write = write(fd,buf,strlen(buf));
if(n_write != -1){
printf("write sizeof:%d\n ",n_write);
}
lseek(fd,0,SEEK_SET);
char *readbuf;
readbuf = (char *)malloc(sizeof(char) * n_write);
int n_read = read(fd,readbuf,100);
printf("read sizeof:%d ,context :%s\n",n_read,readbuf);
close(fd);
return 0;
}
程序运行前: 程序运行后:
若不加O_APPEND:会覆盖原有内容:
2.6 O_TRUNC(擦除原有内容,重新写入)
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main()
{
int fd;
char *buf = "this is buf";
fd = open("./file1",O_RDWR|O_TRUNC);//O_TRUNC(擦除原有内容,重新写入)
int n_write = write(fd,buf,strlen(buf));
if(n_write != -1){
printf("write sizeof:%d\n ",n_write);
}
lseek(fd,0,SEEK_SET);
char *readbuf; //野指针
readbuf = (char *)malloc(sizeof(char) * n_write);//开辟空间
int n_read = read(fd,readbuf,100);
printf("read sizeof:%d ,context :%s\n",n_read,readbuf);
close(fd);
return 0;
}
程序运行前: 程序运行后:
2.7 creat创建文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
int main()
{
int fd;
fd = creat("./file2",S_IRWXU);//S_IRWXU 可读可写可执行
return 0;
}
查询为:
3.文件--写入操作编程
3.1 写入文件
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <unistd.h> #include <unistd.h> // 注意这里重复包含了 <unistd.h> #include <string.h> // main 函数是程序的入口点 int main() { int fd; // 定义一个文件描述符变量 fd char *buf="jammy very handsome"; // 定义一个指向字符串常量的指针 buf // 尝试以读/写模式打开文件 ./file1 fd = open("./file1", O_RDWR); // 检查文件是否成功打开(如果返回 -1 表示失败) if(fd == -1){ printf("open file1 failed.\\n"); // 打印错误信息 } // 再次尝试打开文件,如果文件不存在则创建它,并设置权限为 0600 // 注意:这里有一个逻辑错误,因为之前已经尝试打开过文件,但没有关闭它 // 如果文件不存在,这里应该不会创建文件,因为 O_CREAT 标志没有设置 fd = open("./file1", O_RDWR|O_CREAT, 0600); printf("fd = %d\\n",fd); // 打印文件描述符的值 // 检查文件描述符是否有效(大于 0 表示成功) if(fd > 0){ printf("create file1 success\\n"); // 打印成功创建文件的信息 } // 使用 write 函数将字符串 buf 写入到文件描述符 fd 指向的文件中 // 注意:这里没有检查 write 的返回值来判断是否写入成功 write(fd,buf,strlen(buf)); // strlen 获取字符串的长度 // 关闭文件描述符 fd,释放资源 close(fd); // main 函数返回 0,表示程序成功执行 return 0; }
4.文件--读操作编程
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <unistd.h> #include <unistd.h> #include <string.h> #include <unistd.h> #include <stdlib.h> int main() { int fd; char *buf="jammy very handsome"; fd = open("./file1",O_RDWR); if(fd == -1){ printf("open file1 failed.\n"); } fd = open("./file1",O_RDWR|O_CREAT,0600); printf("fd = %d\n",fd); if(fd > 0){ printf("create file1 success\n"); } //ssize_t write(int fd, const void *buf, size_t count); int n_write = write(fd,buf,strlen(buf)); //size_t strlen(const char *s); if(n_write != -1){ printf("write %d byte to file1\n",n_write); } close(fd); // 关闭文件描述符 fd fd = open("./file1",O_RDWR); // 再次打开文件以读取内容//用于刷新光标 char *readBuf; readBuf = (char *)malloc(sizeof(char)*n_write); //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); //int close(int fd); close(fd); return 0; } ~
阅读函数,光标移动
lseek
系统调用在 Unix-like 系统中用于改变文件的读/写位置。该函数的原型定义在<unistd.h>
头文件中。函数的三个参数分别是:
fd
:文件描述符,它是之前通过open
系统调用返回的。
offset
:要移动的偏移量,以字节为单位。
whence
:指定offset
的基准位置,它可以是以下三个值之一:
SEEK_SET
:偏移量基于文件的开头(即文件偏移量为 0 的位置)。
SEEK_CUR
:偏移量基于当前文件位置。
SEEK_END
:偏移量基于文件末尾。当
whence
为SEEK_SET
时,lseek
会将文件位置指针设置为offset
指定的位置。如果offset
为 0,文件位置指针将被设置为文件的开头。以下是对
lseek(fd,0,SEEK_SET);
这行代码的注释:lseek(fd, 0, SEEK_SET); // 将文件描述符为 fd 的文件的读/写位置设置为文件的开头。 // 偏移量为 0 表示文件开头,这是文件流的位置被重置为开始位置。这行代码通常用在读取或写入文件之前,以确保文件位置指针位于正确的位置。例如,如果你想从文件的开头开始读取或写入,你可能会在调用
read
或write
之前调用lseek
。
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <unistd.h> #include <unistd.h> #include <string.h> #include <unistd.h> #include <stdlib.h> int main() { int fd; char *buf="jammy very handsome"; fd = open("./file1",O_RDWR); if(fd == -1){ printf("open file1 failed.\n"); } fd = open("./file1",O_RDWR|O_CREAT,0600); printf("fd = %d\n",fd); if(fd > 0){ printf("create file1 success\n"); } //ssize_t write(int fd, const void *buf, size_t count); int n_write = write(fd,buf,strlen(buf)); //size_t strlen(const char *s); if(n_write != -1){ printf("write %d byte to file1\n",n_write); } // close(fd); // 关闭文件描述符 fd //fd = open("./file1",O_RDWR); // 再次打开文件以读取内容//用于刷新光标 lseek(fd, 0, SEEK_SET); //移动光标到开头 char *readBuf; readBuf = (char *)malloc(sizeof(char)*n_write); //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); //int close(int fd); close(fd); return 0; }
5.文件编程应用
5.1文件操作小应用之实现cp指令
这段代码是一个简单的C程序,它接收命令行参数,并打印出传递给程序的参数数量以及前三个参数的内容。
以下是代码的详细解释和注释:
#include <stdio.h>
这是预处理指令,它告诉编译器包含标准输入输出头文件
stdio.h
,该头文件包含了printf
函数等标准I/O函数的定义。int main(int argc, char **argv)
这是主函数
main
的声明。main
函数是每个C程序的入口点。参数解释:
int argc
:这是一个整数参数,表示传递给程序的命令行参数的数量(argument count)。
char **argv
:这是一个指向字符串数组的指针,该数组包含了所有的命令行参数(argument vector)。每个参数都是一个以 null 结尾的字符串。以下是
main
函数体的详细注释:{ // 打印传递给程序的总参数数量 printf("total params :%d\n", argc); // 打印第一个参数,通常是程序的名称 printf("NO.1 params :%s\n", argv[0]); // 打印第二个参数(如果存在) printf("NO.2 params :%s\n", argv[1]); // 打印第三个参数(如果存在) printf("NO.3 params :%s\n", argv[2]); // 程序正常退出,返回0 return 0; }以下是每行的详细注释:
printf("total params :%d\n", argc);
:这行代码使用printf
函数打印出传递给程序的总参数数量。
printf("NO.1 params :%s\n", argv[0]);
:这行代码打印出第一个参数,通常这是程序的名称。
printf("NO.2 params :%s\n", argv[1]);
:如果存在第二个参数,这行代码将打印它。
printf("NO.3 params :%s\n", argv[2]);
:如果存在第三个参数,这行代码将打印它。
return 0;
:这表示程序成功执行并正常退出。在main
函数中,返回0通常表示程序成功结束。需要注意的是,如果传递给程序的参数少于3个,尝试访问
argv[1]
或argv[2]
可能会导致未定义的行为,因为它们可能指向未初始化的内存。在实际的程序中,应该检查argc
的值,以确保不会尝试访问超出范围的数组元素。#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <unistd.h> #include <unistd.h> #include <string.h> #include <stdlib.h> int main() { int fd; char *buf="jammy very handsome"; fd = open("./file1",O_RDWR); if(fd == -1){ printf("open file1 failed.\n"); } fd = open("./file1",O_RDWR|O_CREAT,0600); printf("fd = %d\n",fd); if(fd > 0){ printf("create file1 success\n"); } //ssize_t write(int fd, const void *buf, size_t count); int n_write = write(fd,buf,strlen(buf)); //size_t strlen(const char *s); if(n_write != -1){ printf("write %d byte to file1\n",n_write); } //off_t lseek(int fd, off_t offset, int whence); lseek(fd,0,SEEK_SET); char *readBuf; readBuf = (char *)malloc(sizeof(char)*n_write); //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); //int close(int fd); close(fd); return 0; }
5.2文件编程修改程序的配置文件
这段代码是一个C程序,它接受一个命令行参数,该参数是源文件的路径。程序的功能是打开这个文件,读取其内容,查找字符串 "LENG=",并将其后的第一个字符替换为 '5',然后将整个内容写回到文件中。
以下是代码的详细解释和注释:
#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>
:包含了文件控制选项。
<stdio.h>
:包含了标准输入输出函数。
<unistd.h>
:包含了各种unistd库函数的原型,如read
、write
、lseek
等。
<string.h>
:包含了字符串处理函数。
<stdlib.h>
:包含了标准库函数,如malloc
和exit
。int main(int argc, char **argv)这是主函数的声明。
以下是主函数的详细注释:
{ int fdSrc; // 源文件的文件描述符 int fdDes; // 目标文件的文件描述符(在这个程序中没有使用) char *readBuf = NULL; // 用来存储从文件中读取的数据的缓冲区 if (argc != 2) { // 检查命令行参数的数量是否为2(程序名+文件名) printf("pararm error\n"); // 打印错误信息 exit(-1); // 非正常退出程序 } // 打开源文件,以读写方式 fdSrc = open(argv[1], O_RDWR); if (fdSrc == -1) { // 如果打开文件失败,应该检查返回值,这里没有检查,但是实际应用中应该检查 perror("open source file failed"); // 打印错误信息 exit(-1); // 非正常退出程序 } int size = lseek(fdSrc, 0, SEEK_END); // 获取文件的大小 lseek(fdSrc, 0, SEEK_SET); // 将文件指针移回文件开始位置 // 分配足够的空间来存储整个文件内容加上一些额外的空间 readBuf = (char *)malloc(sizeof(char) * size + 8); if (readBuf == NULL) { // 如果分配失败,应该检查返回值 perror("malloc failed"); // 打印错误信息 exit(-1); // 非正常退出程序 } // 从文件中读取数据到缓冲区 int n_read = read(fdSrc, readBuf, size); // 查找字符串 "LENG=" char *p = strstr(readBuf, "LENG="); if (p == NULL) { // 如果没有找到 printf("not found\n"); // 打印信息 exit(-1); // 非正常退出程序 } // 移动到 "LENG=" 后面的字符 p = p + strlen("LENG="); *p = '5'; // 将找到的字符替换为 '5' // 将文件指针移回文件开始位置 lseek(fdSrc, 0, SEEK_SET); // 将修改后的内容写回到文件中 int n_write = write(fdSrc, readBuf, strlen(readBuf)); // 关闭文件 close(fdSrc); // 释放分配的内存 free(readBuf); return 0; // 正常退出程序 }注意:
程序没有处理
read
和write
返回值,实际应用中应该检查这些函数的返回值以处理可能的错误。程序没有释放
readBuf
分配的内存,这会导致内存泄漏。在return 0;
之前应该添加free(readBuf);
来释放内存。程序没有处理文件打开失败的情况,实际应用中应该检查
open
的返回值。程序假设 "LENG=" 后面至少有一个字符,如果这个假设不成立,那么
*p = '5';
可能会导致未定义行为。