Linux文件操作(API)
两种操作文件的方式:
? 1、系统I/O:系统调用接口,open(), read(), write(), lseek(), close()。是操作系统直接提供的编程接口(API)。
? 2、标准/IO:标准库的I/O函数,fopen(), fread(), fwrite(), fseek(), fclose(),是对系统调用接口进一步封装。
? 系统I/O常用于硬件级别,可以设置读缓冲区,一般没有写缓冲区;
? 标准I/O常用于软件级别,自带读写缓冲区。
文件操作流程:
? 打开/创建文件—>读取文件/写入文件—>关闭文件
? ①在Linux中要操作一个文件,一般是先open打开一个文件,得到文件描述符,然后对文件进行读写操作(或其他操作),最后是close关闭文件即可。
? ②强调一点:我们对文件惊醒操作时,一定要先打开文件,打开成功才能操作,打开失败则无法进行操作,最后完成操作后一定要关闭文件,否则容易造成文件损坏。
? ③文件平时是存放在块设备中的文件系统文件中的,这种文件叫静态文件,当去open打开一个文件时,Linux内核做的操作包括:内核在进程中建立一个打开文件的数据结构,记录下我们打开的这个文件;内核在内存中申请一段内存,并且将静态文件的内容从块设备中读取到内核中特定地址管理存放(叫动态文件)
? ④打开文件以后,对这个文件的读写操作,都是针对内存中的这个动态文件的,并不是针对静态文件的。当对动态文件进行读写以后,此时内存中动态文件和块设备文件中的静态文件就不同步了,当close关闭动态文件时,close内部内核将内存中的动态文件的内容去更新同步块设备中的静态文件。
? ⑤不直接对块设备进行直接操作的原因是:块设备本身读写非常不灵活,是按块读写的,而内存是按字节单位操作的,而且可以随机操作,很灵活。
使用文件(Linux下)需要包含的库有:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
可以使用指令 “man 2 open” 在在linux下查看文件的description(描述)
1.open()
1.功能:
打开一个指定的文件并获得文件描述符,或者创建一个新文件
2.原型:
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
3.参数说明:
? pathname:要打开文件的路径
? flags:
? flags 的各种取值可以用位或的方式叠加起来,例如创建文件的时候需要满足这样的 ? 选项:读写方式打开,不存在要新建,如果存在了则清空。则flags 的取值应该是:O_RDWR | O_CREAT | O_TRUNC。
注意:下面的参数中前面三行的参数只能存在一个
O_RDONLY | 只读方式打开文件 |
---|
O_WRONLY | 只写方式打开文件 | O_RDWR | 读写方式打开文件 | O_CREAT | 如果文件不存在,则创建该文件 | O_TRUNC | 如文件已经存在,则删除文件中原有数据 | O_APPEND | 以追加方式打开文件 | O_EXCL | 用O_CREAT创建文件时判断文件是否已经存在,存在则出错 |
? mode:如果文件被新建,指定其权限为mode(八进制表示法)
4.返回值:
成功 | 大于等于0 的整数(即文件描述符) |
---|
失败 | -1,并且errno会被设置 |
5.文件描述符:
? 其实是一个数组的下标值,在内核中打开的文件是用 file 结构体来表示的,每一个结构体都会有一个 指针来指向它们,这些指针被统一存放在一个叫做 fd_array 的数组当中,而这个数组被存 放在一个叫做 files_struct 的结构体中,该结构体是进程控制块 task_struct 的重要组成部分。
列子:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
int main()
{
int fd;
fd = open("./text1",O_RDWR);
if(fd == -1)
{
printf("open file failed!\n");
fd = open("./text1", O_RDWR|O_CREAT,0600);
if(fd > 0)
{
printf("file creat successful!\n");
}
}
return 0;
}
输出:
open file failed!
file creat successful!
文件信息:
ryan@ryan-virtual-machine:~/direction_test$ ls -l
总用量 20
-rwxrwxr-x 1 ryan ryan 8656 4月 10 17:02 a.out
-rw-rw-r-- 1 ryan ryan 266 4月 10 16:48 demo1.c
-rw-rw-r-- 1 ryan ryan 457 4月 10 17:02 demo2.c
-rw------- 1 ryan ryan 0 4月 10 17:02 text
2.write()
1.功能:
将数据写入指定文件
需要包含头文件:#include <unistd.h>
2.原型:
ssize_t write(int fd, const void *buf, size_t count);
3.参数说明:
fd | 将数据写入到文件fd 中 (fd是某个文件的描述符) |
---|
buf | 指向即将要写入的数据,(要写的数据的内存首地址) | count | 要写入的字节数 |
4.返回值:
注意:实际写入的字节数 <= count
3.close()
1.功能:
? 关闭文件并释放相应资源
2.原型:
int close(int fd);
3.参数说明:
? fd 要关闭的文件的描述符
4.返回值:
结合以上3点的例子:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main()
{
int fd;
char *buf = "RyanFate is handsome!";
fd = open("./text1",O_RDWR);
if(fd == -1)
{
printf("open file failed!\n");
fd = open("./text1", O_RDWR|O_CREAT,0600);
if(fd > 0)
{
printf("file creat successful!\n");
}
}
printf("open success : fd = %d\n",fd);
write(fd,buf,strlen(buf));
close(fd);
return 0;
}
输出:
open file failed!
file creat successful!
open success : fd = 3
且文件text1中的内容显示为:
RyanFate is handsome!
4.read()
1.功能:
? 从指定文件中读取数据
2.原型:
ssize_t **read**(int fd, void *buf, size_t count);
注意:ssize_t :是类型重定义,为了跨平台兼容。比如说long在32位系统可能是4字节,64位系统可能是8字节,嵌入式开发有的只有16位,那么int只有2个字节。
3.参数说明:
fd | 从文件 fd 中读数据,(fd是某个文件的描述符) |
---|
buf | 指向存放读到的数据的缓冲区,(就是放数据的内存首地址) | count | *想要从文件 fd 中读取的字节数 |
4.返回值:
注意:文件阅读的时候需要注意光标的问题
5.lseek()光标函数
1.功能:
? 调整文件位置偏移量
2.原型:
off_t lseek(int fd, off_t offset, int whence);
3.参数说明:
fd | 要调整位置偏移量的文件的描述符 |
---|
offset | 相对基准点的偏移大小(正数往后偏移,负数往前偏移) |
whence:
SEEK_SET | 文件开头处 |
---|
SEEK_CUR | 当前位置 | SEEK_END | 文件末尾处 |
4.返回值:
成功 | 新文件位置偏移量(相对于文件开头的偏移) |
---|
失败 | -1 |
可以用 **filesize = lseek(fd,0,SEEK_END);**来测量文件大小
例子
#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 = "RyanFate is handsome!";
fd = open("./text1",O_RDWR);
if(fd == -1)
{
printf("open file failed!\n");
fd = open("./text1", O_RDWR|O_CREAT,0600);
if(fd > 0)
{
printf("file creat successful!\n");
}
}
printf("open success : fd = %d\n",fd);
int nWrite = write(fd,buf,strlen(buf));
if(nWrite != -1)
{
printf("write %d byte in file\n",nWrite);
}
char *readBuf;
readBuf = (char*)malloc(sizeof(char)*nWrite);
lseek(fd,0,SEEK_SET);
int nRead = read(fd, readBuf, nWrite);
printf("read %d ,context:%s\n",nRead,readBuf);
close(fd);
return 0;
}
输出:
open success : fd = 3
write 21 byte in file
read 21 ,context:RyanFate is handsome!
6.对文件操作实现CP功能
? 代码:
#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 argc, char **argv)
{
int fdSource;
int fdDestination;
char *readBuf;
if(argc != 3)
{
printf("param error!\n");
exit(-1);
}
fdSource = open(argv[1],O_RDWR);
int sizeFdSource = lseek(fdSource,0,SEEK_END);
readBuf = (char*)malloc(sizeof(char) * sizeFdSource);
lseek(fdSource,0,SEEK_SET);
int nRead = read(fdSource,readBuf,sizeof(char) * sizeFdSource);
fdDestination = open(argv[2],O_RDWR|O_CREAT|O_TRUNC,0600);
int n_write = write(fdDestination, readBuf, strlen(readBuf));
close(fdSource);
close(fdDestination);
return 0;
}
输出:
ryan@ryan-virtual-machine:~/direction_test$ ls
a.out demo1.c demo2.c demo3.c demo4.c demo5.c demo6.c demo7.c demo8.c text1
ryan@ryan-virtual-machine:~/direction_test$ cat text1
Ryanfate is handsome!
ryan@ryan-virtual-machine:~/direction_test$ ./a.out text1 text2
ryan@ryan-virtual-machine:~/direction_test$ ls
a.out demo1.c demo2.c demo3.c demo4.c demo5.c demo6.c demo7.c demo8.c text1 text2
ryan@ryan-virtual-machine:~/direction_test$ cat text2
Ryanfate is handsome!
7.修改文件中的参数
? 注意:主要是要获取到参数的首地址,利用 strstr() 函数获取某个字符串的起始位置,定义好一个指针参数指向该位置,加上字符串的长度用 strlen() 函数,得到需要修改参数的位置,再对指针进行取地址后修改参数。
例子:
#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 argc, char **argv)
{
int fdSource;
char *readBuf;
if(argc != 2)
{
printf("param error!\n");
exit(-1);
}
fdSource = open(argv[1],O_RDWR);
int sizeFdSource = lseek(fdSource,0,SEEK_END);
readBuf = (char*)malloc(sizeof(char) * sizeFdSource);
lseek(fdSource,0,SEEK_SET);
int nRead = read(fdSource,readBuf,sizeof(char) * sizeFdSource);
char *p = strstr(readBuf,"LENG=");
if(p == NULL)
{
printf("Not found the argument!\n");
exit(-1);
}
p = p + strlen("LENG=");
sprintf(p,"8\n");
lseek(fdSource,0,SEEK_SET);
int n_write = write(fdSource, readBuf, strlen(readBuf));
close(fdSource);
return 0;
}
原始文件中的数据:
SPEED=3
LENG=5
CORE=9
LEVEL=5
输出结果:
SPEED=3
LENG=8
SCORE=9
LEVEL=5
修改了 LENG 的值,但是缺点是:只能修改单个字符如果将 LENG 换成 10 的话会覆盖下一行的一个字符。
8.写入整型数据到文件中
#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 argc, char **argv)
{
int fdSource;
char *readBuf;
int data = 100;
int data2 = 0;
fdSource = open("./text1",O_RDWR);
int n_write = write(fdSource,&data,sizeof(int));
lseek(fdSource,0,SEEK_SET);
int n_read = read(fdSource,&data2,sizeof(int));
printf("read number = %d\n",data2);
close(fdSource);
return 0;
}
输出结果:
ryan@ryan-virtual-machine:~/direction_test$ ./a.out
read number = 100
文件对数据的读取是没有问题的,但是可以看到文件中对于 int类型 数据的描述是 d@@^@ 文件读取时是可以正常读取整型数据的。
9.写入结构体数据到文件中
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
struct Test
{
int a;
char b;
};
int main(int argc, char **argv)
{
int fdSource;
char *readBuf;
struct Test data = {10,'a'};
struct Test data2;
fdSource = open("./text1",O_RDWR);
int n_write = write(fdSource,&data,sizeof(struct Test));
lseek(fdSource,0,SEEK_SET);
int n_read = read(fdSource,&data2,sizeof(struct Test));
printf("read number = %d,char = %c\n",data2.a,data2.b);
close(fdSource);
return 0;
}
输出结果:
ryan@ryan-virtual-machine:~/direction_test$ ./a.out
read number = 10,char = a
10.关于标准C语言的文件操作
1.来源的区别:
? open是 UNIX 系统调用函数(包括Linux等),返回的是文件描述符(File Descriptor),它是文件在文件描述符表里的索引。
? fopen 是 ANSIC 标准中的C语言库函数,在不同的系统中应该调用不同的内核API。返回的是一个指向文件结构的指针。
2.适用范围:
? open的操作对象是文件描述符,文件描述符是UNIX系统下的一个重要概念,因为UNIX下的一切设备都是以文件的形式操作,如网络套接文字,硬件设备等。包括 操作普通正规文件。
? fopen用来操作 普通正规文件。
3.缓冲
? 缓冲文件系统:是借助 文件结构体指针 来对文件进行管理。是在内存中开辟一个 缓冲区 ,为程序中的每一个文件使用;当进行读和写操作时系统会把文件的数据先存到缓冲区,待完成数据操作后再从缓冲区中读出来。内存缓冲区越大,执行效率就越高。(如:fopen,fclose,fwrite,fgetc等)
? 非缓冲文件系统:依赖于操作系统,是通过操作系统的功能对文件进行操作,不设有结构体指针,只能读写二进制文件,效率高,速度快,但是ANSI标准不再包括非缓冲文件系统。(如:open,close,write,getc等)
11.fopen()
1.功能:
? 创建一个新的文件或者打开一个已有的文件,这个调用会初始化类型 FILE 的一个对象,类型 FILE 包含了所有用来控制流的必要的信息。
2.原型:
FILE *fopen( const char * filename, const char * mode );
3.参数说明:
filename 是字符串,用来命名文件,访问模式 mode 的值可以是下列值中的一个:
r | 打开一个已有的文本文件,允许读取文件。 |
---|
w | 打开一个文本文件,允许写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会从文件的开头写入内容。如果文件存在,则该会被截断为零长度,重新写入。 | a | 打开一个文本文件,以追加模式写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会在已有的文件内容中追加内容。 | r+ | 打开一个文本文件,允许读写文件。 | w+ | 打开一个文本文件,允许读写文件。如果文件已存在,则文件会被截断为零长度,如果文件不存在,则会创建一个新文件。 | a+ | 打开一个文本文件,允许读写文件。如果文件不存在,则会创建一个新文件。读取会从文件的开头开始,写入则只能是追加模式。 |
如果处理的是二进制文件,则需使用下面的访问模式来取代上面的访问模式:
"rb", "wb", "ab", "rb+", "r+b", "wb+", "w+b", "ab+", "a+b"
4.返回值:
返回值为一个文件类型的指针。
12.fclose()
1.功能:
关闭文件
2.原型:
int fclose( FILE *fp );
3.参数说明:
文件的指针
4.返回值:
这个函数实际上,会清空缓冲区中的数据,关闭文件,并释放用于该文件的所有内存。EOF 是一个定义在头文件 stdio.h 中的常量。
13.fwrite()
1.功能:
? 将缓存区中的数据写入到文件中
2.原型:
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
3.参数说明:
ptr | 指向要被写入的元素数组的指针 |
---|
size | 要被写入的每个元素的大小,以字节为单位 | nmemb | 这是元素的个数,每个元素的大小为 size 字节 | stream | 这是指向 FILE 对象的指针,该 FILE 对象指定了一个输出流 |
4.返回值:
如果成功,该函数返回一个 size_t 对象,表示元素的总数,该对象是一个整型数据类型。如果该数字与 nmemb 参数不同,则会显示一个错误。
14.fseek()
1.功能:
确定光标的位置
2.原型
int fseek(FILE *stream, long int offset, int whence)
3.参数说明:
stream | 这是指向 FILE 对象的指针,该 FILE 对象标识了流 |
---|
offset | 这是相对 whence 的偏移量,以字节为单位 | whence | 是表示开始添加偏移 offset 的位置 |
whence为以下指定常量之一:
SEEK_SET | 文件的开头 |
---|
SEEK_CUR | 文件指针的当前位置 | SEEK_END | 文件的末尾 |
4.返回值:
15.fread()
1.功能:
读取缓存区中的文件数据
2.原型:
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
3.参数说明:
ptr | 是指向带有最小尺寸 size*nmemb 字节的内存块的指针 |
---|
size | 是要读取的每个元素的大小,以字节为单位 | nmemb | 是元素的个数,每个元素的大小为 size 字节 | stream | 是指向 FILE 对象的指针,该 FILE 对象指定了一个输入流 |
4.返回值:
成功读取的元素总数会以 size_t 对象返回,size_t 对象是一个整型数据类型。如果总数与 nmemb 参数不同,则可能发生了一个错误或者到达了文件末尾。
16.fgetc()
1.功能:
? 从缓存中获取一个字符并把光标位置前移
2.原型:
int fgetc(FILE *stream)
3.参数说明:
参数 | 说明 |
---|
stream | 指向 FILE 对象的指针,该 FILE 对象标识了要在上面执行操作的流 |
4.返回值:
该函数以无符号 char 强制转换为 int 的形式返回读取的字符,如果到达文件末尾或发生读错误,则返回 EOF。
17.fputc()
1.功能:
? 将指定字符写入到缓存区中并把光标前移
2.原型:
int fputc(int char, FILE *stream)
3.参数说明:
char | 这是要被写入的字符。该字符以其对应的 int 值进行传递 |
---|
stream | 指向 FILE 对象的指针,该 FILE 对象标识了要被写入字符的流 |
4.返回值:
如果没有发生错误,则返回被写入的字符。如果发生错误,则返回 EOF,并设置错误标识符。
18.feof()
1.功能:
? 文件结束标识符
2.原型:
int feof(FILE *stream)
3.参数说明:
参数 | 说明 |
---|
stream | 是指向 FILE 对象的指针,该 FILE 对象标识了流 |
4.返回值:
当设置了与流关联的文件结束标识符时,该函数返回一个非零值,否则返回零。
|