十、关于光标位置相关的函数(fseek、ftell、rewind)
#include <stdio.h>
int fseek(FILE *stream, long offset, int whence);
功能:修改文件光标位置
参数1:要修改光标的文件
参数2:偏移量
>0:表示向后偏移
=0:不偏移
<0:表示向前偏移
参数3:偏移的起始位置
SEEK_SET:文件的开头
SEEK_CUR:当前位置
SEEK_END:文件结尾
返回值:成功返回0,失败返回-1并置位错误码
eg: fseek(fp, 10, SEEK_SET); //从开头向后移动10个字节
fseeke(fp, 10, SEEK_CUR); //从当前位置向后移动
fseeke(fp, -10, SEEK_CUR); //从当前位置向前移动
fseek(fp, 0, SEEK_END); //将光标定位到结尾
long ftell(FILE *stream);
功能:获取光标当前位置
参数:文件指针
返回值:成功返回文件当前位置,失败返回-1,置位错误码
eg:
fseek(fp,0,SEEK_END);
ftell(fp); ===>这两条语句相当于求出文件大小
void rewind(FILE *stream);
功能:将光标定位到开头
参数:文件指针
返回值:无
等价于:fseek(fp, 0, SEEK_SET)
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
int main(int argc, const char *argv[])
{
FILE *fp;
char buf[10] = "";
//以读写的形式打开文件
if((fp=fopen("./Test.txt", "w+")) == NULL)
{
perror("open file ");
return -1;
}
//向文件中写入数据
fprintf(fp, "hello world\n");
fprintf(fp, "Good morning...\n");
//将光标移动到开头
//fseek(fp, 0, SEEK_SET);//将光标定位在从开始偏移0字节
fseek(fp, 12, SEEK_SET);//将光标定位在从开始偏移0字节
//读取数据
fgets(buf, sizeof(buf), fp);
//关闭文件
fclose(fp);
printf("buf = %s\n", buf);
return 0;
}
练习:通过代码读取一张图片,对图片进行操作
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
int main(int argc, const char *argv[])
{
FILE *fp;
//以r+的形式打开文件
if((fp = fopen("./milaoshu.bmp", "r+")) == NULL)
{
perror("open file");
return -1;
}
//想要获取文件大小
unsigned int img_size;
//将文件光标偏移2字节,前两字节是文件类型
fseek(fp, 2, SEEK_SET);
//读取文件大小
fread(&img_size, 4, 1, fp);
//输出图片大小
printf("img_size = %d\n", img_size);
//向后偏移54字节,得到数据
fseek(fp, 54, SEEK_SET);
//定义一个颜色
unsigned int color[3] = {0, 0xff, 0};
//蓝色 绿色 红色
//遍历图片
for(int i=0; i<100; i++)
{
for(int j=0; j<500; j++)
{
fwrite(color, 4, 3, fp);
}
}
//关闭文件
fclose(fp);
return 0;
}
思考:如果以追加的方式打开文件,使用fseek能不能更改光标?
如果以“a”的形式进行追加,fseek不能修改光标的位置
如果以“a+”的形式追加,fseek不能修改写指针的光标,但是可以更改读指针的光标
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
int main(int argc, const char *argv[])
{
FILE *fp;
//以追加的形式打开文件
//if((fp = fopen("./Test.txt", "a")) == NULL)
if((fp = fopen("./Test.txt", "a+")) == NULL)
{
perror("open file");
return -1;
}
//将指针定位到开头
fseek(fp, 12, SEEK_SET);
char buf[10] = "";
fread(buf, 1, sizeof(buf), fp);
printf("buf = %s\n", buf);
//fprintf(fp,"lalalala...\n");
//关闭文件
fclose(fp);
return 0;
}
十一、文件IO
基于系统调用的方式,只要调用文件IO的接口,程序就会从用户空间向内核空间进行一次交互,效率相比于标准IO较低,原因是没有缓冲区
接口:open、read、write、close、lseek。。。
11.1 文件描述符
1> 文件描述符是一个大于等于0的非负整数,使用open打开一个文件后,就会返回该文件的描述符,在之后想要操作文件,只需通过该文件描述符即可
2> 在一个正在执行的程序中,默认打开了三个文件描述符0(stdin)、1(stdout)、2(stderr)
3> 文件描述符的使用原则:最小未分配原则
4> 一个程序中的文件描述符的最大承载量默认为1024
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
int main(int argc, const char *argv[])
{
//默认提供的文件指针对应的文件描述符
printf("fileno of stdin = %d\n", stdin->_fileno);
printf("fileno of stdout = %d\n", stdout->_fileno);
printf("fileno of stderr = %d\n", stderr->_fileno);
return 0;
}
11.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);
功能:打开一个文件
参数1:文件路径,跟fopen的第一个参数一致
参数2:打开方式
O_RDONLY :只读
O_WRONLY:只写
O_RDWR:读写
====================以上三个必须选择其中一个,下面的可以作为选加===================
O_CREAT:如果文件不存在,创建一个文件,如果加了该选项,那么第三个参数也必须要加
O_APPEND:追加写
O_TRUNC:如果文件存在,则清空内容
O_EXCL:判断文件是否已经存在,如果已经存在了,则open函数会置位错误码为:EEXIST,跟O_CREAT
例如:"w+" : O_RDWR | O_CREAT | O_TRUNC //多个值通过位或连接
参数3:文件权限,创建文件时的权限
目录的最大权限:0777
普通文件的最大权限是:0666
最终的权限是给定的(mode & ~umask) //0664 & (~umask) ->0664
umask:是系统给定的默认的掩码
更改掩码的指令:umask 数字
返回值:成功返回打开文件的文件描述符,失败返回-1,并置位错误码
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, const char *argv[])
{
int fd; //定义一个文件描述符
umask(0002); //将掩码设置成0
//以读写的模式打开文件
if((fd=open("./open.txt", O_RDWR|O_CREAT|O_TRUNC, 0664)) == -1)
{
perror("open file");
return -1;
}
//关闭文件
close(fd);
return 0;
}
使用O_EXCL创建文件
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include<errno.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{
int fd; //文件描述符
//打开文件
if((fd = open("./open.txt", O_RDWR|O_CREAT|O_EXCL)) == -1)
{
//判断错误原因
if(errno == EEXIST)
{
printf("文件已存在,无需重新创建,直接打开即可。。。\n");
fd = open("./open.txt", O_RDWR);
}else
{
perror("open file");
return -1;
}
}
return 0;
}
11.3 umask
1> 创建文件时的掩码,默认问0002,表示其他用户对该文件没有写功能
2> 终端上查看掩码:umask
3> 更改掩码:
终端更改:umask 数字
代码更改:umask(数值);
11.4 close的使用
#include<myhead.h>
int main(int argc, const char *argv[])
{
int fd; //文件描述符
//打开文件
if((fd = open("./open.txt", O_RDWR|O_CREAT|O_EXCL)) == -1)
{
//判断错误原因
if(errno == EEXIST)
{
printf("文件已存在,无需重新创建,直接打开即可。。。\n");
fd = open("./open.txt", O_RDWR);
}else
{
perror("open file");
return -1;
}
}
//关闭文件
if(close(fd) == 0)
{
printf("关闭成功\n");
}else
{
printf("关闭失败\n");
}
close(0); //关闭标准输入文件
close(1);
close(2);
return 0;
}
11.5 read函数的使用
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
功能:从给定文件中读取count个数据到buf中
参数1:被读取的文件
参数2:读取后数据存放的容器指针,是一个万能指针,也就是可以读取任意类型的数据
参数3:读取的个数
返回值:
>0:读取字符的个数
=0:说明文件读取结束
<0:失败,并置位错误码
11.6 write函数的使用
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
功能:将buf中的数据写入到文件中,一共写count个字节
参数1:被读取的文件
参数2:读取后数据存放的容器指针,是一个万能指针,也就是可以读取任意类型的数据
参数3:读取的个数
返回值:成功返回写入的个数,如果啥也没写,就返回0
失败返回-1,并置位错误码
#include<myhead.h>
int main(int argc, const char *argv[])
{
//向标准出错中写数据
write(2, "hello world\n", strlen("hello world\n"));
//从标准输入中读取数据
char buf[50] = "";
read(0, buf, sizeof(buf));
//printf("buf = %s\n", buf);
//向标准输出中放数据
write(1, buf, sizeof(buf));
//定义文件描述符
int fd;
//以读写的形式打开文件
if((fd = open("./open.txt", O_RDWR|O_CREAT|O_TRUNC, 0664)) == -1)
{
perror("open file");
return -1;
}
printf("fd = %d\n", fd); //3
//向文件中写入数据
write(fd, buf, sizeof(buf));
//关闭文件
close(fd);
return 0;
}
作业
使用read、write实现两个文件的拷贝
11.7 文件IO中的lseek函数
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
功能:移动光标位置
参数1:文件描述符
参数2:偏移量
>0:向后偏移
<0:向前偏移
=0:不偏移
参数3:偏移的起始位置
SEEK_SET:文件开头
SEEK_CUR:当前位置
SEEK_END:文件结尾
返回值:
成功返回光标当前位置到文件开头的偏移量
失败返回(off_t)-1,并置位错误码
lseek = fseek + ftell
#include<myhead.h>
int main(int argc, const char *argv[])
{
//定义文件描述符
int fd;
//以只读的形式打开文件
if((fd = open("./milaoshu.bmp", O_RDONLY)) == -1)
{
perror("open file");
return -1;
}
//将光标偏移到文件结尾,返回值为从当前位置到文件开头的字节数
printf("size of file = %ld\n", lseek(fd, 0, SEEK_END)); //输出的就是文件大小
//关闭文件
close(fd);
return 0;
}
十二、文件描述符拷贝问题
12.1 文件描述符的直接拷贝
使用变量拷贝文件描述符,其实只是拷贝变量的值,并没有产生新的文件描述符,只是在程序中多了一个变量,记录该文件描述符而已,所以无论操作哪个变量,都是对该文件描述符的操作,使用的是同一个光标
#include<myhead.h>
int main(int argc, const char *argv[])
{
int fd1;
//以只读的形式打开一个文件
if((fd1 = open("./open.txt", O_RDONLY)) == -1)
{
perror("open file");
return -1;
}
//定义容器
char buf[10] = "";
int ret = read(fd1, buf, sizeof(buf));
write(1, buf, ret); //向终端打印数据
printf("\n");
int fd2 = fd1; //将文件描述符直接拷贝
printf("fd1 = %d, fd2 = %d\n", fd1, fd2);
char buf1[10];
ret = read(fd2, buf1, sizeof(buf1));
write(1, buf1, ret); //如果终端上输出的跟fd1输出结果一致
//说明两个文件使用的是不同光标
//如果不一致,说明两个文件使用的是同一光标
return 0;
}
12.2 文件描述符拷贝之dup函数
#include <unistd.h>
int dup(int oldfd);
功能:通过给定的旧文件描述符,拷贝出一个新文件描述符
参数:旧文件描述符
返回值:成功返回:新文件描述符,使用最小未使用原则
失败返回-1并置位错误码
#include<myhead.h>
int main(int argc, const char *argv[])
{
int fd1,fd2;
//以只读的形式打开文件
if((fd1 = open("./open.txt", O_RDONLY)) == -1)
{
perror("open file");
return -1;
}
//读取数据
char buf1[10];
int ret = read(fd1, buf1, sizeof(buf1)); //从文件中读取数据
write(1, buf1, ret); //将数据打印在终端上
//使用dup函数拷贝一个文件描述符
fd2 = dup(fd1);
printf("fd1 = %d, fd2 = %d\n", fd1, fd2);
//通过fd2读取数据
char buf2[10];
ret = read(fd2, buf2, sizeof(buf2));
write(1, buf2, ret);
//关闭文件
if(close(fd1))
{
perror("close fd1");
}
ret = read(fd2, buf2, sizeof(buf2));
write(1, buf2, ret);
if(close(fd2))
{
perror("close fd2");
}
return 0;
}
作业:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#define BUF_SIZE 1024
int main(int argc, char *argv[])
{
int src_fd, dest_fd, rd_size, wt_size;
char buf[BUF_SIZE];
if (argc != 3)
{
fprintf(stderr, "Usage: %s <src_file> <dest_file>\n", argv[0]);
exit(EXIT_FAILURE);
}
src_fd = open(argv[1], O_RDONLY);
if (src_fd == -1)
{
perror("open");
exit(EXIT_FAILURE);
}
dest_fd = open(argv[2], O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (dest_fd == -1)
{
perror("open");
exit(EXIT_FAILURE);
}
while ((rd_size = read(src_fd, buf, BUF_SIZE)) > 0)
{
wt_size = write(dest_fd, buf, rd_size);
if (wt_size != rd_size)
{
perror("write");
exit(EXIT_FAILURE);
} }
if (rd_size == -1)
{ perror("read"); exit(EXIT_FAILURE); }
if (close(src_fd) == -1)
{ perror("close");
exit(EXIT_FAILURE); }
if (close(dest_fd) == -1)
{
perror("close");
exit(EXIT_FAILURE);
}
return EXIT_SUCCESS;
}