目录
1.文件打开及创建
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
int main()
{
int fd;
fd = open("./file1",O_RDWR); //open函数,打开当前路径(./)下的文件,O_RDONLY(只读),O_WRONLY(只写),O_RDWR(可读可写) open的返回类型为指针
if(fd == -1){
printf("Open file1 failed\n");
fd = open("./file1",O_RDWR|O_CREAT,0600); //0600 表示权限可读可写,因为可读是4可写是2,4+2=6
if(fd > 0){
printf("create file1 succeed\n");
}
}
if(fd > 0){
printf("Open file1 succeed\n");
}
return 0;
}
2.文件写入操作编程
语法
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
一个write()调用从文件描述符fd引用文件的当前位置开始,将buf中至多count个字节写入文件中。不支持定位的文件(像字符设备)总是从“开头”开始写。
成功时,返回写入字节数,并更新文件位置。错误时,返回-1,并将errno设置为相应的值。一个write()可以返回0,但这种返回值没有任何特殊含义;只是表示写入了零个字节。
const char *buf = "Leo is handsome";
ssize_t nr;
/* write the string in 'buf' to 'fd' */
nr = write(fd, buf, strlen(buf));
if(nr == -1)
/* errno */
这种简单的用法并不大合适。调用者也需要检查出先部分写的各种可能情况。
unsigned long word = 1720;
size_t count;
ssize_t nr;
count = sizeof(word);
nr = write(fd, &word, count);
if(nr == -1)
/* errno, check errno */
else if(nr != count)
/* possible errno, but 'errno' not set */
实例:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int fd;
char *buf = "Leo is handsome!\n";
fd = open("./file1",O_RDWR);
if(fd == -1){
//如果file1文件不存在,则fd的整型值为-1
printf("Open file1 failed\n");
fd = open("./file1",O_RDWR|O_CREAT,0600); //0600 表示权限可读可写,因为可读是4可写是2,4+2=6
if(fd > 0){
printf("create file1 succeed\n");
}
}
printf("Open file1 succeed,fd = %d\n",fd);
/* ssize_t write(int fd, const void *buf, size_t count); 这是写入文件指令*/
// write(fd,buf,sizeof(buf)); //这样写只能打印八个字节,因为buf为指针只有8个字节,所以无法将buf里的内容全部打印
write(fd,buf,strlen(buf)); //用strlen()
close(fd); //关闭文件
return 0;
}
部分写
相对于read()的返回部分读的情况,write不太可能返回一个部分写的结果,而且,对于write()系统调用 来说没有EOF情况,对于普通文件,除非发生一个错误,否则write()将保证写入所有的请求。
那么,对于普通文件,你就不需要进行循环写了。然而对于其他类型,例如套接字,大概得有个循环来保证你真的写入了所有请求的字节。使用循环的另一个好处是,第二次write()调用可能会返回一个错误值用来说明第一次调用为什么进行一次部分写(尽管这种情况不大常见)。
ssize ret, nr;
ret = write(fd, buf, len);
while(len != 0 && ret ! = 0){
if(ret == -1){
if(errno == EINTR) //EINTR函数被信号中断,该信号被程序中的信号处理程序捕获,并且信号处理程序正常返回
continue;
perror("write");
break;
}
len -= ret;
buf += ret;
}
追加模式(通过指定O_APPEND参数)
当fd在追加模式下打开时(通过指定O_APPEND参数),写操作就不从文件描述符的当前位置开始,而是从当前文件末尾开始。
举例来说,假设有两个进程在向同一个文件写。不使用追加模式的话,如果第一个进程向文件末尾写,而第二个进程也这么做,那么第一个进程的文件位置将不再指向文件末尾,而将指向文件末尾减去第二进程写入的字节数的地方。这意味着多个进程如果不进行显示的同步则不能进行追加操作,因为他们存在着竞争关系。
追加模式避免了这样的问题。它保证文件位置总是指向文件末尾,这样所有的写操作总是追加
3.文件读取操作
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int fd;
char *buf = "Leo is handsome!\n";
fd = open("./file1",O_RDWR);
if(fd == -1){
printf("Open file1 failed\n");
fd = open("./file1",O_RDWR|O_CREAT,0600); //0600 表示权限可读可写,因为可读是4可写是2,4+2=6
if(fd > 0){
printf("create file1 succeed\n");
}
}
printf("Open file1 succeed,fd = %d\n",fd);
// ssize_t write(int fd, const void *buf, size_t count); 这是写入文件指令
int n_write = write(fd,buf,strlen(buf));
if(n_write != -1){
printf("write %d byte to file\n",n_write);
}
//关闭文件后再打开文件,因为在完成写操作后光标已经在字符串最后面,后面的read()操作无法读取。
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;
}
4.文件光标移动操作
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int fd;
char *buf = "Leo is handsome!\n";
fd = open("./file1",O_RDWR);
if(fd == -1){
printf("Open file1 failed\n");
fd = open("./file1",O_RDWR|O_CREAT,0600); //0600 表示权限可读可写,因为可读是4可写是2,4+2=6
if(fd > 0){
printf("create file1 succeed\n");
}
}
printf("Open file1 succeed,fd = %d\n",fd);
// ssize_t write(int fd, const void *buf, size_t count); 这是写入文件指令
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);
// lseek()API,比关闭重新打开文件更优雅
// off_t lseek(int fd, off_t offset, int whence);
/* whence 来移动读写位置的位移数。参数 whence 为下列其中一种:
SEEK_SET 参数 offset 即为新的读写位置。
SEEK_CUR 以目前的读写位置往后增加 offset 个位移量。
SEEK_END 将读写位置指向文件尾后再增加 offset 个位移量。
当 whence 值为 SEEK_CUR 或 SEEK_END 时,参数 offet 允许负值 负数往前走,整数往后走
的出现。
下列是特别的使用方式:
1) 欲将读写位置移到文件开头时:lseek(int fildes,0,SEEK_SET) ;
2) 欲将读写位置移到文件尾时:lseek(int fildes,0,SEEK_END) ;
3) 想要取得目前文件位置时:lseek(int fildes,0,SEEK_CUR) ;
*/
char *readBuf;
readBuf = (char *)malloc(sizeof(char)*n_write + 1); //开辟空间并且强制指针转换
// ssize_t read(int fd, void *buf, size_t count);
lseek(fd,0,SEEK_SET);
int n_read = read(fd,readBuf,n_write);
printf("read %d,context:%s\n",n_read,readBuf);
close(fd);
return 0;
}
off_t lseek(int fd, off_t offset, int whence);
whence 来移动读写位置的位移数。参数 whence 为下列其中一种:
SEEK_SET 参数 offset 即为新的读写位置。
SEEK_CUR 以目前的读写位置往后增加 offset 个位移量。
SEEK_END 将读写位置指向文件尾后再增加 offset 个位移量。
当 whence 值为 SEEK_CUR 或 SEEK_END 时,参数 offet 允许负值 负数往前走,整数往后走
的出现。
下列是特别的使用方式:
1) 欲将读写位置移到文件开头时:lseek(int fildes,0,SEEK_SET) ;
2) 欲将读写位置移到文件尾时:lseek(int fildes,0,SEEK_END) ;
3) 想要取得目前文件位置时:lseek(int fildes,0,SEEK_CUR) ;
5.计算文件大小
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int fd;
char *buf = "Leo is handsome!\n";
fd = open("./file1",O_RDWR);
int filesize = lseek(fd,0,SEEK_END); //光标位置在最后,偏移为0,从而知道文件内容大小
printf("file's size is %d\n",filesize);
close(fd);
return 0;
}
6.文件打开创建的补充
参数说明:
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
Pathname: 要打开的文件名(含路径)
Flags: O_RDONLY只读打开 O_WRONLY只写打开 O_RDWR可读可写打开
当我们附带权限后,打开文件就只能按照这种权限来操作。
以上这三个常数中应当只指定一个,下列常数是可选择的:
O_CREAT 若文件不存在则创建它。使用此选项时,需要同时说明第三个参数mode,用其说明该新文件的存取许可权限。
O_EXCL 如果同时指定了O_CREAT,而文件已经存在,则出错
O_APPEND 每次写时都加到文件的末端。
O_TRUNC 属性去打开文件时,如果这个文件中本来是有内容的,而且为只读或只写成功打开,则将其长度截断为0.
Mode: 一定是在flags中使用O_CREAT标志,mode记录待创建的文件的访问权限。
#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,0600); //O_CREAT,O_EXCL只能指定一个,否则打开失败
if(fd == -1){
printf("Open file1 failed\n");
}
return 0;
}
O_APPEND使用:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int fd;
char *buf = "Leo is handsome!\n";
fd = open("./file1",O_RDWR);
printf("Open file1 succeed,fd = %d\n",fd);
int n_write = write(fd,buf,strlen(buf));
if(n_write != -1){
printf("write %d byte to file\n",n_write);
}
close(fd);
return 0;
}
对file1进行编辑:
运行包含O_APPEND的代码:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int fd;
char *buf = "Leo is handsome!\n";
fd = open("./file1",O_RDWR|O_APPEND);
printf("Open file1 succeed,fd = %d\n",fd);
int n_write = write(fd,buf,strlen(buf));
if(n_write != -1){
printf("write %d byte to file\n",n_write);
}
close(fd);
return 0;
}
运行完之后发现file1修改后的内容会添加到原来的内容之上:
如果没有加上O_APPEND则会出现内容覆盖(覆盖原来字节的位置):
O_APPEND使用:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int fd;
char *buf = "Leo is ugly\n"; //修改内容
fd = open("./file1",O_RDWR|O_TRUNC);
printf("Open file1 succeed,fd = %d\n",fd);
int n_write = write(fd,buf,strlen(buf));
if(n_write != -1){
printf("write %d byte to file\n",n_write);
}
close(fd);
return 0;
}
运行完之后发现 Leo is handsome 变成了 Leo is ugly
7.创建文件create函数
int creat(const char **filename, mode_t mode)
filename:要创建的文件名(包含路径)
mode:创建模式 //可读可写可执行
常见创建模式:
宏表示 数字
S_IRUSR 4 可读
S_IWUSR 2 可写
S_IXUSR 1 可执行
S_IRWXU 7 可读可写可执行
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int fd;
char *buf = "Leo is very handsome!\n";
// int creat(const char *pathname, mode_t mode);
fd = creat("/home/leo/桌面/file2",S_IRWXU); //进行创建
return 0;
}
8.文件操作原理概述
文件描述符:
1.对内核而言,所有打开文件都由文件描述符引用。文件描述符是一个非负整数。当打开一个现存文件或者创建一个新文件是,内核向进程返回一个文件描述符。当读写一个文件时,用open和creat返回的文件描述符标识该文件,将其作为参数传递给read和write。
按照惯例,UNIX shell 使用文件描述符0与进程标准输入相结合,文件描述符1与标准输出相结合,文件描述符2与标准错误输出相结合。STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO这几个宏代替了0、1、2、这几个魔数。
2.文件描述符,这个数字在一个进程中表示一个特定含义, 当我们open一个文件时,操作系统在内存中构建了一些数据结构来表示这个动态文件,然后返回给应用程序一个数字作为文件描述符,这个数字就和我们内存中维护的这个动态文件的这些数据结构绑定上,以后我们应用程序如果要操作这个动态文件,只需要用这个文件描述符区分。
3.文件描述符的作用就是当前进程,出了这个进程文件描述符就没有意义了。
open函数打开文件,打开成功返回一个文件描述符,打开失败,返回-1.
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int fd;
char readBuf[128];
int n_read = read(0, readBuf, 128); //0表示输入,128表示字节
int n_write = write(1,readBuf,strlen(readBuf)); //1表示输出
printf("\n");
return 0;
}
通过输入Leo is handsome 输出了Leo is handsome。
1.在Linux中要操作一个文件,一般是先open一个文件,得到文件描述符,然后对文件进行读写操作(或其他操作),最后是close关闭文件即可。
2.强调一点:我们对文件进行操作时,一定要先打开问价,打开成功之后才能操作,如果打开失败,就不用进行后边的操作了,最后读写完成后,一定要关闭文件,否则会造成文件损坏。
3.文件平时是存放在块设备中的文件系统文件中的,我们把这种文件叫静态文件,当我们去open打开一个文件时,linux内核做的操作包括:内核在进程中建立一个打开文件的数据结构,记录下我们打开的这个文件;内核在内存中中申请一段内存,并且将静态文件的内容从块设备中读取到内核中特定地址管理存放(叫动态文件)。
4.打开文件以后,以后对这个文件的读写操作,都是针对内存中的这一份动态文件的,而并不是针对静态文件的。当然我们对动态文件进行读写以后,此时内存中动态文件和块设备文件中的静态文件就不同步了,当我们close关闭动态文件时,close内部内核将内存中的动态文件的内容去更新(同步)块设备中的静态文件。
5.块设备本身读写非常不灵活,是按块设备读写的,而内存是按字节单位操作的,而且可以随机操作,很灵活。
9.文件操作CP指令
cp 源码编写思路:
1.打开源文件
2.读取源文件
3.打开目标文件
4.将读取的源文件内容写入到目标文件
5.关闭源文件与目标文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main(int argc,char* argv[]){
int fdSrc; //源文件
int fdDes; //目标文件
char* readBuf;
if(argc!=3){
//判断是否有三个参数
printf("program error!\n");
exit(-1);
}
fdSrc=open(argv[1],O_RDWR); //打开源文件
int size = lseek(fdSrc,0,SEEK_END); //利用光标移动函数读取源文件大小
readBuf = (char*)malloc(sizeof(char)*size+8);//分配合适空间大小
lseek(fdSrc,0,SEEK_SET); //再次使光标回到头位置
int n_read = read(fdSrc,readBuf,size); //读取源文件的内容
fdDes = open(argv[2],O_RDWR|O_CREAT|O_TRUNC,0600); //打开目标文件,加入O_TRUNC干掉了原本的内容
int n_write = write(fdDes,readBuf,strlen(readBuf)); //将读取的内容写入目标文件
close(fdSrc); //关闭源文件
close(fdDes); //关闭目标文件
return 0;
}
new.c:
10.修改程序配置文件
配置文件的修改如:
SPEED = 5
LENG = 100
SCORE = 90
LEVEL = 95
1.找到目标位置
2.目标位置往后移,移到需要修改的地方
3.修改内容
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main(int argc,char* argv[]){
int fdSrc;
char *readBuf = NULL;
if(argc!=2){
printf("program error!\n");
exit(-1);
}
fdSrc = open(argv[1],O_RDWR);
int size = lseek(fdSrc,0,SEEK_END);
readBuf = (char*)malloc(sizeof(char)*size+8);
lseek(fdSrc,0,SEEK_SET);
int n_read = read(fdSrc,readBuf,size);
char *p = strstr(readBuf,"LENG=");
if(p == NULL){
printf("no found");
exit(-1);
}
p = p+strlen("LNEG=");
*p = '5';
int n_write=write(fdSrc,readBuf,strlen(readBuf));
close(fdSrc);
return 0;
}
注意:在./a.out后边加上需要修改的源文件
如果不希望另起一行,希望在配置文件重新覆盖:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main(int argc,char* argv[]){
int fdSrc;
char *readBuf = NULL;
if(argc!=2){
printf("program error!\n");
exit(-1);
}
fdSrc = open(argv[1],O_RDWR);
int size = lseek(fdSrc,0,SEEK_END);
readBuf = (char*)malloc(sizeof(char)*size+8);
lseek(fdSrc,0,SEEK_SET);
int n_read = read(fdSrc,readBuf,size);
char *p = strstr(readBuf,"LENG=");
if(p == NULL){
printf("no found");
exit(-1);
}
p = p+strlen("LNEG=");
*p = '5'; //注意:是写成字符而不是数字
lseek(fdSrc,0,SEEK_SET); //设置光标
int n_write=write(fdSrc,readBuf,strlen(readBuf));
close(fdSrc);
return 0;
}
11.写结构体数组到文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
struct Test
{
int a;
char c;
};
int main()
{
int fd;
struct Test data = {
{
100,'a'},{
101,'b'}};
struct Test data2[2];
fd = open("./file1",O_RDWR);
int n_write = write(fd,&data,sizeof(struct Test)*2);
lseek(fd,0,SEEK_SET);
int n_read = read(fd,&data2,sizeof(struct Test)*2);
printf("read %d %c\n",data2[0].a,data2[0].c);
printf("read %%d %c\n",data2[1].a,data2[1].c);
close(fd);
return 0;
}
整数写入文件:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int fd;
int data = 100;
int data2 = 0;
fd = open("./file1",O_RDWR);
int n_write = write(fd,&data,sizeof(int)); //加上&
lseek(fd,0,SEEK_SET);
int n_read = read(fd,&data2,sizeof(int)); //加上&
printf("read %d \n",data2);
close(fd);
return 0;
}
12.标准C库 fopen,fwrite,fread,fseek
#include <stdio.h>
#include <string.h>
int main()
{
//FILE *fopen(const char *pathname, const char *mode);
FILE *fp;
char *str = "Leo is handsome\n";
char readBuf[128] = {
0};
fp = fopen("./file2.txt","w+"); //权限使用双引号
//size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
/*第一个参数ptr: buf;
* 第二个参数size: sizeof char;
* 第三个参数 个数;
* 第四个参数 哪个文件;
*/
fwrite(str,sizeof(char),strlen(str),fp); //一个一个字符写入
//fwrite(str,sizeof(char)*strlen(str),1,fp); //一次性写入,写1次
//int fseek(FILE *stream, long offset, int whence);
fseek(fp,0,SEEK_SET);
//size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
fread(readBuf,sizeof(char),strlen(str),fp);
//fread(readBuf,siezeof(char)*strlen(str),1,fp); //一次性写入
printf("read data: %s\n",readBuf);
close(fp);
return 0;
}
注意一下返回值:
如果fwrite与fread是一次一次的写入和读入,则返回值是看strlen(str)的大小(如果改成100则返回100)。
如果是一次性写入,则是看你的次数,如果是1则返回为1(如果改成100,write会返回100下,但是read则不会)
#include <stdio.h>
#include <string.h>
int main()
{
//FILE *fopen(const char *pathname, const char *mode);
FILE *fp;
char *str = "Leo is handsome\n";
char readBuf[128] = {
0};
fp = fopen("./file2.txt","w+");
//size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
/*第一个参数ptr: buf;
* 第二个参数size: sizeof char;
* 第三个参数 个数;
* 第四个参数 哪个文件;
*/
//int n_write = fwrite(str,sizeof(char),strlen(str),fp); //一个一个字符写入
fwrite(str,sizeof(char)*strlen(str),1,fp); //一次性写入,写1次
//int fseek(FILE *stream, long offset, int whence);
fseek(fp,0,SEEK_SET);
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
//int n_read = fread(readBuf,sizeof(char),strlen(str),fp);
printf("read data: %s\n",readBuf);
printf("read = %d, write = %d\n",n_read,n_write);
close(fp);
return 0;
}
13.标准C库写入结构体到文件
#include <stdio.h>
#include <string.h>
struct Test
{
int a;
char c;
};
int main()
{
//FILE *fopen(const char *pathname, const char *mode);
FILE *fp;
struct Test data = {
100,'a'};
struct Test data2;
fp = fopen("./file2.txt","w+");
int n_write = fwrite(&data,sizeof(struct Test),1,fp); //一次性写入,写1次
fseek(fp,0,SEEK_SET);
int n_read = fread(&data2,sizeof(struct Test),1,fp);
printf("read = %d, %c\n",data2.a,data2.c);
fclose(fp);
return 0;
}
~
14.标准C库fputc,fgetc,feof
#include <stdio.h>
#include <string.h>
int main(){
FILE* fp;
int i;
char* str = "Leo is handsome\n";
int len = strlen(str);
char c;
//int fputc(int c, FILE *stream);
//int fputs(const char *s, FILE *stream);
fp = fopen("./putTest","w+");
for(i=0;i<len;i++){
int put = fputc(*str,fp);
str++;
}
fclose(fp);
/*
fp = fopen("./putTest","r+");
while(!feof(fp)){ //eof :end of file 文件结束符,feof是否到达文件的尾巴
c = fgetc(fp);
printf("%c",c);
}
fclose(fp);
*/
return 0;
}
~