文件io
Linux 一切皆是文件
*int open(const char pathname , int flags);
//(参数1:字符指针 指向文件路径 , 参数2:整型数权限)
*int open(const char pathname , int flags , mode_t mode);
//open 返回值是文件描述符——没有文件返回值就无法用write(); 和 read();
*pathname : //要打开的文件名(含路径,缺省为当前路径)
flags :
O_RDONLY 只读打开
O_WRONLY 只写打开
O_RDWR 可读可写可打卡
fd = open("./file" , O_RDWR | O_CREAT , 0600);
// open 指令 若没有file文件 ” | O_CREAT ” 创建file文件 权限0600 6=4+2 所以是rw 可读可写
O_EXCL 如果同时指定了OCREAT , 而文件已经存在,则打开失败或者返回-1
O_APPEND 每次写时都加到文件的尾端。
O_TRUNC属性去打开文件时,如果这个文件中本来是有内容的,把原先内容全部覆盖掉
IO操作
write
#include<unistd.h>
ssize_t write (int fd , const void *buf , size_t count);
函数功能:将参数buf指向内存写入count个字节到参数fd所指的文件内。
函数参数:1.文件描述符fd;
2.无类型的指针buf,可以存放要写的内容;
3.多少大小的字节数;
返回值:如果顺利write()会返回实际写入的字节数(len)。当有错误发生时则返回-1,错误代码存入errno中。
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
#include<string.h>
#include<unistd.h>
int main()
{
int fd;
char *buf = "你今天真好看";
fd = open("./file",O_RDWR);
printf("fd=%d\n",fd);
if(fd == -1){
printf("open file failed\n");
fd = open("./file",O_RDWR | O_CREAT , 0600);
if(fd>0){
printf("creat file success\n");
}
}
//ssize_t write(int fd , const void *buf , size_t count);
//将缓冲区buf指针指向内存数据,写conut大小到fd。
write(fd,buf,strlen(buf));
printf("fd=%d\n",fd);
close(fd); //关闭fd文件
return 0;
}
read
ssize_t read(int fd , const void *buf , size_t count);
函数功能:将参数fd所指的文件读取count个字节放到buf里面。
函数返回值:若读取成功,读多少个字节返回多少个字节,若读到尾什么都没读到返回0,读取失败返回-1
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main()
{
int fd;
char *buf ="你今天真好看";
fd= open("./file",O_RDWR);
printf("fd=%d\n",fd);
if(fd == -1){
printf("open file failed\n");
fd = open("./file", O_RDWR | O_CREAT | ,0600);
if(fd>0){
printf("create file success\n");
}
}
int n_write = write(fd,buf,strlen(buf));
if(n_write != -1){
printf("write%d byte to file\n",n_write); //若读取成功,返回读到的 ite
}
//close(fd); //写完数据后关闭文件
//fd = open("./file",O_RDWR); //重新打开
char *readBuf;
readBuf = (char*)malloc(sizeof(char)*n_write+1);
int n_read = read(fd,reafBuf , n_write);
printf("read=%d , context:%s\n",n_read,readBuf);
close(fd);
return 0;
}
因为写完数据后光标会移动到文字末尾,读取的时候只会读取光标之后的位置,所以什么也没有所以读取不到。
文件光标移动
lseek
① 对文件进行操作时,该文件是动态文件,动态文件在内存中的形态就是文件流的形式。
②在动态文件中,通过文件指针来表征这个正在操作的位置,文件指针是vnode中的一个元素。
③该指针不能直接访问,Linux提供lseek函数访问这个文件指针。
④打开一个文件时,默认下,文件指针只想文件流的开始,这时候write是从文件开始位置写入的,write与read自带移动文件指针的功能,write写入n字节后,文件指针会自动往后移动n位。
⑤ man 2 lseek(lseek是一种文件读写的API)
off_t lseek(int fd , off_t offset , int whence);
函数功能:将文件读写指针相对 whence 移动 offset 个字节
SEEK_SET //指向文件的头
SEEK_CUR //指向当前光标位置
SEEK_END //指向文件的尾
用lseek计算文件长度
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int cal_len(const char *pathname)
{
int fd = -1; // fd 就是file descriptor,文件描述符
int ret = -1;
// 第一步:打开文件
fd = open(pathname, O_RDONLY);
if (-1 == fd) // 有时候也写成: (fd < 0)
{
//printf("\n");
perror("文件打开错误");
// return -1;
return -1;
}
//else
//{
//printf("文件打开成功,fd = %d.\n", fd);
//}
// 此时文件指针指向文件开头
// 我们用lseek将文件指针移动到末尾,然后返回值就是文件指针距离文件开头的偏移量,也就是文件的长度了
ret = lseek(fd, 0, SEEK_END);
return ret;
}
int main(int argc, char *argv[])
{
int fd = -1; // fd 就是file descriptor,文件描述符
int ret = -1;
if (argc != 2)
{
printf("usage: %s filename\n", argv[0]);
_exit(-1);
}
printf("文件长度是:%d字节\n", cal_len(argv[1]));
return 0;
}
用lseek创建一个空洞文件
① 空洞文件就是这个文件有一段是空的;
② 普通文件中间不能有空,write文件时从前往后移动文件指针,依次写入;
③ 用lseek往后跳过一段,就形成空洞文件;
④ 空洞文件对多线程共同操作文件非常有用。需要创建一个很大文件时,从头开始依次创建时间很长,可以将文件分成多段,多个线程操作每个线程负责其中一段的写入。
ret = lseek(fd, 10, SEEK_SET);
文件描述符
对于内核而言,所有打开文件都由文件描述符引用。文件描述符是一个非负整数。当打开一个现存文件或者创建一个新文件时,内核向进程返回一个文件描述符。当读写一个文件时,用open和creat返回的文件描述符标识该文件,将其作为参数传递给read和write。
按照惯例,UNIX shell使用文件描述符0与进程的标准输入相结合,文件描述符1与标准输出相结合,文件描述符2与标准错误输出相结合。
STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO这几个宏代替了0,1,2这几个数。
文件描述符,这个数字在一个进程中表示一个特定含义,当我们open一个文件时,操作系统在内存中构建了一些数据结构来表示这个动态文件,然后返回给应用程序一个数字作为文件描述符,这个数字就和我们内存中维护的这个动态文件的这些数据结构绑定上了,以后我们应用程序如果要操作这个动态文件,只需要用这个文件描述符区分。
文件描述符的作用域就是当前进程,除了这个进程文件描述符就没有意义了,open函数打开文件,打开成功返回一个文件描述符,打开失败,返回-1。
小练习 实现cp指令代码
cp ( src.c //源文件 ,dest.c //复制到的目标文件)
思路:
- 打开src.c
- 读src到buf
- 创建/打开dest.c
- 将buf写入dest.c
- close两个文件
#include<sys/types.h>
#include<sys/stat.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
int main(int argc ,char **argv){
int fdSrc;
int fdDest;
char *readBuf = NULL; //置空,防止野指针;
if(argc != 3){
printf("param error\n");
exit(-1);
}
fdSrc = open(argv[1],O_RDWR); //1. 打开fdSrc.c(源文件)
int size = lseek(fdSrc ,0, SEEK_END); //光标记录文件大小
lseek(fdSrc , 0, SEEK_SET); //!!千万记得把 光标回到头
readBuf = (char *)malloc(sizeof(char) *size+8); //开辟readBuf空间大小
int n_read = read(fdSrc,readBuf,size); //2. 读取fdSrc(源文件)到readBuf缓冲区
fdDest = open(argv[2],O_RDWR | O_CREAT | O_TRUNC ,0600); //3. 打开创建fdDest.c
int n_write = write(fdDest,readBuf,strlen(readBuf)); //4. 将readbuf写入到fdDest.c
close(fdSrc); //5.关闭两个文件
close(fdDest);
return 0;
}
容易出现的小问题—实现优化:
1.用 lseek来光标计算size数组
char *readBuf = NULL;
int size = lseek(fdSrc,0,SEEK_END);
lseek(fdSrc , 0 ,SEEK_SET);
readBuf = (char *)malloc(sizeof(char)*size + 8);
2.若要是拷贝大于1024的文件就不行
3.目标文件存在并且存在一些数据,拷贝就会覆盖了原来数据的一部分
解决方法: O_TRUNC 属性去打开文件时,如果这个文件中本来是有内容的,而且为只读或者只写成功打开,则将其长度截断为0
修改程序的配置文件
void *changefile(int fd,char *readbuf,char* f,char t){
char *p = strstr(readbuf,f);
if(p == NULL){
printf("no found\n");
exit(-1);
}
p = p + strlen(f);
*p = t;
}
int main(int argc,char **argv)
{
changefile(fdSrc,readBuf,"LENG=",'6');
}
fopen(); fwrite(); fread(); 方式写入数据
FILE *fopen (const char *path ,const char *mode);
size_t fwrite (const void *ptr , size_t size , size_t nmemb , FILE *stream);
size_t fread (const void *ptr ,size_t size ,size_t nmemb ,FILE *stream);
int fseek (FILE *stream , long offset ,int whence);
#include<stdio.h>
#include<string.h>
int main()
{
FILE *fp;
char *str = "Refuel.CONG";
char readBuf[128]={0};
fp = fopen("./CONG.txt","w+"); //可读可写方式创建一个文本文件
fwrite(str sizeof(char),strlen(str),fp);
//一次性写一个char 写str个字节,到fp里
fseek(fp,0,SEEK_SET);
fread(readBuf,sizeof(char),strlen(str),fp);
//从fp里 一次读一个char 读str个 读到readBuf里去
printf("read data:%s\n",readBuf);
fclose(fp);
return 0;
}
fgetc(); fputc(); feof() 的使用方法
fgetc()
作用:从指定的文件中读一个字符
fgets(字符数组名,n,文件指针)
作用:从指定的文件读取一个字符串到字符数组中。
int fputc(int c , FILE *stream)
作用:把 c 写入 文件stream里
int feof (FILE *stream )
作用:(是否到尾巴的位置):测试在没到达文件尾巴返回0,到达尾巴返回 非0