【Linux】基础IO ——文件(上)

简介: 【Linux】基础IO ——文件(上)

1. 预备知识

文件= 内容+属性

对应文件的操作,对内容的操作,对属性的操作

当文件没有被操作的时候,一般在磁盘中

当对文件进行操作的时候,一般在内存中,因为冯诺依曼体系规定

当我们对文件进行操作的时候,文件需要提前加载到内存中,提前加载的是属性

当我们对文件进行操作的时候,文件需要提前加载到内存中,不只有你在load,内存中一定存在大量的不同文件属性


打开文件本质就是将需要的属性加载到内存中,OS内部一定会同时存在大量的被打开的文件,操作系统就会通过先描述,在组织的方式管理这些被打开的文件

先描述,构建在内存中的文件结构体 struct file{ 文件属性,struct

file*next},表明被打开的文件 每一个被打开的文件,都要在OS内对应 文件对象的struct

结构体,可以将所有的struct结构体通过某种数据结构链接起来,在OS内部,对被打开的文件进行管理,就会转换为对链表的增删查改

结论:文件被打开,OS要为被打开的文件,创建对应的内核数据结构,struct file结构体 ,该结构体包含各种属性,各种链接关系


2. 回忆C接口

fopen

FILE *fopen(const char *path, const char *mode);

第一个参数为 打开文件对应的路径

第二个参数为 打开文件对应的权限

如果打开成功,返回FILE指针,否则返回NULL

创建myfile.c文件

E>#include<stdio.h>    
  #define LOG "log.txt"    
E>int main()    
  {    
    FILE*fp= fopen(LOG,"w");    
    //默认写方式 打开文件,如果文件不存在,就创建它                                                                                                                                         
  if(fp==NULL)    
  {    
    perror("fopen");//报错    
    return 1;    
  }    
  fclose(fp); //关闭文件    
    return 0;                                                                                                                                                                           
  }  

fputs

int fputs(const char *s, FILE *stream);

你想要写的字符串,写入特定的流当中,成功返回字符串字符个数,失败返回-1

#include<stdio.h>                                                                                                                                                                         
  #define LOG "log.txt"    
int main()    
  {    
    FILE*fp= fopen(LOG,"w");    
    //默认写方式 打开文件,如果文件不存在,就创建它    
  if(fp==NULL)    
  {    
    perror("fopen");//报错    
    return 1;    
  }        
  //进行文件操作    
  const char*msg="bbb\n";      
    fputs(msg,fp);    
  fclose(fp); //关闭文件                          
    return 0;                                     
  } 

将msg字符串中的数据写入fp流中

fprintf

int fprintf(FILE *stream, const char *format, …);

指定文件流,向文件打印

指定文件流fp,而fp打开的文件为log.txt,所以将msg数据打印到log.txt文件中因为Linux中一切皆文件,所以也可以传入stdout(标准输出流)中,stdout也对应一个文件,即显示器文件


运行可执行程序,结果显示到显示器了

snprintf

int snprintf(char *str, size_t size, const char *format, …);

通过格式化流的方式,把字符串信息自定义格式化到字符串缓冲区中,并规定大小

将msg中的数据打印到buffer字符串中,同时使用fputs将buffer中的数据写入刚刚打开的文件log.txt中

追加方式—— a

追加,不会清空文件,而是每一次写入都是从文件尾部写入的

修改myfile.c文件内容

#include<stdio.h>    
#define LOG "log.txt"    
int main()    
{    
  FILE*fp= fopen(LOG,"a");    
  //默认写方式 打开文件,如果文件不存在,就创建它    
if(fp==NULL)    
{    
  perror("fopen");//报错    
  return 1;    
}    
//进行文件操作    
const char*msg="bbb\n";    
 int cnt=1;    
 while(cnt)    
{    
  fputs(msg,fp);    
  cnt--;    
}    
fclose(fp); //关闭文件    
  return 0;                                                                                                                                                                           
}    

多次运行可执行程序,发现可追加

以读方式—— r

char *fgets(char *s, int size, FILE *stream);

从特定的文件流中按行所取对应的文件,将读到的内容放到缓冲区中

修改myfile.c文件内容

#include<stdio.h>    
#define LOG "log.txt"    
int main()    
{    
  FILE*fp= fopen(LOG,"r");                                                                                                                                                                  
if(fp==NULL)    
{    
  perror("fopen");//报错    
  return 1;    
}    
//进行文件操作    
while(1)    
{    
  char line[128];    
  if(fgets(line,sizeof(line),fp)==NULL)    
    break;    
  else    
  {    
    printf("%s",line);    
  }    
}    
fclose(fp); //关闭文件    
  return 0;    
}  

从fp中读取到line中,如果当前读取返回NULL说明读取失败 ,返回break

此时运行可执行程序,即可看到对应文件中的内容

3.操作系统如何进行读写文件操作

open

#include

#include

#include

int open(const char *pathname, int flags);

第一个参数代表 文件路径+文件名

第二个参数 代表 文件对应的选项(选项的问题后面会提)

如果打开成功了,就会返回新的文件描述符,如果打开失败,返回 -1

操作系统是如何让用户给自己传递标志位的

1. 我们怎么做

通过写一个 函数 若 int XXX (int flag) ,传递参数flag flag=1/2/3 ,将flag进行赋值

2. 系统怎么做

操作系统存在系统调用接口 int YYY (int flag),flag作为一个整数,有32个比特位,可以用一个比特位表示一个标志位

,一个int 就可以同时传递至少32个标记位

此时的flag 就可以看作数据类型 位图 看待

理解标记位的问题

创建test.c文件

#include<stdio.h>    
//分别代表从右向左的每个比特位    
#define ONE  0x1    
#define TWO  0x2    
#define THREE 0x4    
#define FOUR 0x8    
void print(int flag)    
{    
  if(flag & ONE )    
  printf("hello 1\n");//充当函数的不同行为    
   if(flag & TWO)    
  printf("hello 2\n");    
   if(flag & THREE)    
  printf("hello 3\n");    
   if (flag & FOUR)    
  printf("hello 4\n");    
}                                                                    
int main()                                                           
{                                                                    
  printf("-------------------------------------\n");                 
  print(ONE);     //打印one                                                    
  printf("-------------------------------------\n");                 
  print(ONE|TWO); //打印one two                                      
  printf("-------------------------------------\n");                 
  print (ONE|TWO|THREE);//打印 one two three                         
  printf("-------------------------------------\n");                 
  print (ONE|TWO|THREE|FOUR);  //打印 one two three four                                    
  printf("-------------------------------------\n");                 
  return 0;                                                          
} 

通过整数flag 一次传递多个标志位

0000 0000

将最后的四个比特位通过宏的方式分别记录下来

再与flag 按位与 ,若为真,则说明传递的flag 是 这4个比特位中的其中一个


执行可执行程序,此时分别打印出了 one two three four的运行结果

类比上述 open的第二个参数flag ,存在多个标志位,同通过宏来实现,每一个标志位都代表不同的值

新创建文件权限不正确


  • O_CREAT :文件不存在就打开,不存在就创建一个文件
  • O_WRONLY: 以写方式打开文件
  • 在myfile.c文件中重新输入代码
#include<sys/stat.h>    
#include<fcntl.h>    
#include<stdio.h>    
#include<unistd.h>    
#define LOG "log.txt"    
int main()    
{    
  int fd=open(LOG, O_WRONLY| O_CREAT);//打开一个文件,若文件不存在则重新创建一个
  if(fd==-1)//说明打开失败
  {
    printf("fd:%d,errno:%d,errstring:%s\n",fd,errno,strerror(errno));//打印出错误信息
  }
  else                                                                                                                                                                                      
  printf("fd :%d\n",  fd);                                   
  close(fd); //关闭文件                                      
  return 0;                                                  
} 

运行可执行程序,发现


假设log.txt文件不存在,通过创建文件并打开文件,发现新文件的权限不正常

因为在Linux中创建一个文件需要有对应的权限的

int open(const char *pathname, int flags, mode_t mode);

所以在文件不存在时,一般采用有三个参数接口的open

mode代表权限

修改myfile.c文件的内容

  #include<sys/types.h>    
  #include<sys/stat.h>    
  #include<fcntl.h>    
  #include<stdio.h>    
  #include<unistd.h>    
  #include<errno.h>
  #define LOG "log.txt"    
  int main()    
  {    
    int fd=open(LOG, O_WRONLY| O_CREAT,0666);//打开一个文件,若文件不存在则重新创建一个                                                                                                      
    if(fd==-1)//说明打开失败    
    {    
E>    printf("fd:%d,errno:%d,errstring:%s\n",fd,errno,strerror(errno));//打印出错误信息    
    }    
    else    
    printf("fd :%d\n",  fd);    
    close(fd); //关闭文件    
    return 0;    
  } 

0666 :拥有者 所属组 other都有读写权限


此时log.txt文件拥有正常的权限

但是输入的是666 ,显示的却是664,即other没有写权限

因为创建一个文件时,默认权限受到umask的影响

解决 umask的权限

使用 man 2 umask 查看

#include

#include

mode_t umask(mode_t mask);

可以影响当前进程启动时,属于自己的umask,采取就近原则,因为自己设置离的更近所以使用自己设置的umask

而不是系统的umask


修改myfile.c文件的内容

  #include<sys/types.h>    
  #include<sys/stat.h>    
  #include<fcntl.h>    
  #include<stdio.h>    
  #include<unistd.h>    
  #include<errno.h>    
  #define LOG "log.txt"    
  int main()    
  {    
    umask (0);//将权限掩码设置成0                                                                                                                                                           
    int fd=open(LOG, O_WRONLY| O_CREAT,0666);//打开一个文件,若文件不存在则重新创建一个                                
    if(fd==-1)//说明打开失败                                                                                          
    {                                                                                                                 
    printf("fd:%d,errno:%d,errstring:%s\n",fd,errno,strerror(errno));//打印出错误信息                               
    }                                                                                                                 
    else                                                                                                              
    printf("fd :%d\n",  fd);                                                                                          
    close(fd); //关闭文件                                                                                             
    return 0;                                                                                                         
  }  

使用umask (0) 将权限掩码设置成0



此时log.txt文件的权限为 666

write

通过 man 2 write 查看文件写入接口

ssize_t write(int fd, const void *buf, size_t count);

fd代表文件描述符

buf代表 缓冲区

count代表 缓冲区大小

write将缓冲区的count大小的数据写入 fd中

返回值代表实际写入多少字节

修改myfile.c文件内容

include<sys/types.h>    
#include<sys/stat.h>    
#include<fcntl.h>    
#include<stdio.h>    
#include<unistd.h>    
#include<errno.h>    
#include<string.h>    
#define LOG "log.txt"    
int main()    
{    
  umask (0);//将权限掩码设置成0    
  int fd=open(LOG, O_WRONLY| O_CREAT,0666);//打开一个文件,若文件不存在则重新创建一个    
  if(fd==-1)//说明打开失败    
  {    
    printf("fd:%d,errno:%d,errstring:%s\n",fd,errno,strerror(errno));//打印出错误信息    
  }    
  else    
  printf("fd :%d\n",  fd);    
  const char* msg="hello world";                                                                                                                                                            
  int cnt=5;    
  while(cnt)    
  {    
    char line[128];    
    snprintf(line,sizeof(line),"%s,%d\n",msg,cnt);//将msg和cnt写入line缓冲区中    
    write(fd,line,strlen(line)+1);//将line写入fd中    
    cnt--;    
  }    
  close(fd); //关闭文件    
  return 0;    
}    

若 strlen(line)+1 ,则打开log.txt文件时发现出现乱码,因为数字0在ASCII表中属于不可显示字符

所以为了不出现乱码,所以strlen(line) 不应该+1,因为\0是c语言的规定,不是文件的规定

修改myfile.c文件内容

#include<sys/types.h>    
#include<sys/stat.h>    
#include<fcntl.h>    
#include<stdio.h>    
#include<unistd.h>    
#include<errno.h>    
#include<string.h>    
#define LOG "log.txt"    
int main()    
{    
  umask (0);//将权限掩码设置成0    
  int fd=open(LOG, O_WRONLY| O_CREAT,0666);//打开一个文件,若文件不存在则重新创建一个    
  if(fd==-1)//说明打开失败                                              
  {                                                                     
    printf("fd:%d,errno:%d,errstring:%s\n",fd,errno,strerror(errno));//打印出错误信息    
  }                                                                     
  else                                                                  
  printf("fd :%d\n",  fd);                                              
  const char* msg="hello world";                                        
  int cnt=5;                                                            
  while(cnt)                                                            
  {                                                                     
    char line[128];                                                     
    snprintf(line,sizeof(line),"%s,%d\n",msg,cnt);//将msg和cnt写入line缓冲区中    
    write(fd,line,strlen(line));//将line写入fd中 strlen不要+1                                                                                                                               
    cnt--;                                                          
  }                                                                 
  close(fd); //关闭文件                                             
  return 0;    
} 

此时strlen不加1

默认不会对原始文件清空

修改myfile.c文件内容,msg和cnt的数据内容

#include<sys/types.h>    
#include<sys/stat.h>    
#include<fcntl.h>    
#include<stdio.h>    
#include<unistd.h>    
#include<errno.h>    
#include<string.h>    
#define LOG "log.txt"    
int main()    
{    
  umask (0);//将权限掩码设置成0    
  int fd=open(LOG, O_WRONLY| O_CREAT,0666);//打开一个文件,若文件不存在则重新创建一个    
  if(fd==-1)//说明打开失败    
  {    
    printf("fd:%d,errno:%d,errstring:%s\n",fd,errno,strerror(errno));//打印出错误信息    
  }    
  else    
  printf("fd :%d\n",  fd);    
  const char* msg="aaaaa";    
  int cnt=1;                                                                                                                                                                                
  while(cnt)                              
  {                                       
    char line[128];    
    snprintf(line,sizeof(line),"%s,%d\n",msg,cnt);//将msg和cnt写入line缓冲区中    
    write(fd,line,strlen(line));//将line写入fd中 strlen不要+1    
    cnt--;    
  }    
  close(fd); //关闭文件    
  return 0;    
}  

O_WRONLY | O_CREAT 默认不会对原始文件清空


O_TRUNC : 将文件做清空

修改myfile.c文件内容

#include<sys/types.h>    
#include<sys/stat.h>        
#include<fcntl.h>        
#include<stdio.h>        
#include<unistd.h>        
#include<errno.h>        
#include<string.h>        
#define LOG "log.txt"        
int main()        
{        
  umask (0);//将权限掩码设置成0        
  int fd=open(LOG, O_WRONLY| O_CREAT| O_TRUNC,0666 );//打开一个文件,若文件不存在则重新创建一个        
  if(fd==-1)//说明打开失败        
  {                                                                                                                                                                                         
    printf("fd:%d,errno:%d,errstring:%s\n",fd,errno,strerror(errno));//打印出错误信息                  
  }                                                                                                    
  else                                                                                                 
  printf("fd :%d\n",  fd);                                                                             
  const char* msg="aaaaa";                                                                             
  int cnt=1;                                                                                                                                                                                  while(cnt)                                                                                           
  {                                                                                                    
    char line[128];                                                                                    
    snprintf(line,sizeof(line),"%s,%d\n",msg,cnt);//将msg和cnt写入line缓冲区中                         
    write(fd,line,strlen(line));//将line写入fd中 strlen不要+1                                          
    cnt--;                                                                                             
  }                                                                                                    
  close(fd); //关闭文件                                                                                
  return 0;                                                                                            
}

此时再次调用log.txt文件,就会将之前文件中内容清空’

系统层面追加

O_APPEND 追加

O_WRONLY: 以写方式打开文件

O_WRONLY | O_APPEND | O_CREAT 若文件存在就以写的方式追加,若文件不存在则创建

修改myfile.c文件内容

#include<sys/types.h>    
#include<sys/stat.h>    
#include<fcntl.h>    
#include<stdio.h>    
#include<unistd.h>    
#include<errno.h>    
#include<string.h>    
#define LOG "log.txt"        
int main()    
{    
  umask (0);//将权限掩码设置成0        
  int fd=open(LOG,O_WRONLY |  O_CREAT | O_APPEND  ,0666 );//打开一个文件,若文件不存在则重新创建一个                                                                                         
  if(fd==-1)//说明打开失败                                                                             
  {                                                                                                    
    printf("fd:%d,errno:%d,errstring:%s\n",fd,errno,strerror(errno));//打印出错误信息                  
  }                                                                                                    
  else                                                                                                 
  printf("fd :%d\n",  fd);                                                                             
  const char* msg="bbb";                                                                                                                                                                      int cnt=1;                                                                                                                                                                                  while(cnt)                                                                                           
  {                                                                                                    
    char line[128];                                                                                    
    snprintf(line,sizeof(line),"%s,%d\n",msg,cnt);//将msg和cnt写入line缓冲区中                         
    write(fd,line,strlen(line));//将line写入fd中 strlen不要+1                                          
    cnt--;                                                                                             
  }                                                                                                    
  close(fd); //关闭文件                                                                                
  return 0;                                                                                            
} 

此时重复运行可执行程序,打开log.txt文件 ,发现可以追加

系统层面 读取

O_RDONLY : 读取

输入 man 2 read

ssize_t read(int fd, void *buf, size_t count);

从文件描述符fd中将我们想要的数据,按照数据块的方式读取出来

返回值代表多少字节,读取到文件结尾为0,失败为-1

#include<sys/types.h>    
#include<sys/stat.h>    
#include<fcntl.h>    
#include<stdio.h>    
#include<unistd.h>    
#include<errno.h>    
#include<string.h>    
#define LOG "log.txt"        
int main()    
{    
  umask (0);//将权限掩码设置成0        
  int fd=open(LOG, O_RDONLY  );//打开一个文件,若文件不存在则重新创建一个        
  if(fd==-1)//说明打开失败        
  {    
    printf("fd:%d,errno:%d,errstring:%s\n",fd,errno,strerror(errno));//打印出错误信息        
  }    
  else    
  printf("fd :%d\n",  fd);                                                                                                                                                                  
 char buffer[1024];    
 ssize_t n= read(fd,buffer,sizeof(buffer)-1);//使用系统接口来进行IO的时候,一定要注意\0的问题    
 if(n>0)//成功了,实际读到了多少字节    
 {    
  buffer[n]='\0';    
  printf("%s\n",buffer);    
 }    
  close(fd); //关闭文件        
  return 0;    
}

运行可执行程序,可以直接读取到数据

总结

操作系统不相信任何用户,所以操作系统给用户提供系统调用

程序员调用库的接口,而库的接口必定要调用系统调用

打开文件的本质是文件相关的内容加载到内存里

把数据触发从磁盘到内存

把数据从自己的程序写入硬盘上,一定会涉及到对硬件的访问

用户不能使用c/c++库绕过操作系统去访问

软硬件各种资源属于操作系统的,操作系统是硬件的管理者


相关文章
|
7天前
|
Linux 开发工具 Perl
在Linux中,有一个文件,如何删除包含“www“字样的字符?
在Linux中,如果你想删除一个文件中包含特定字样(如“www”)的所有字符或行,你可以使用多种文本处理工具来实现。以下是一些常见的方法:
32 5
|
7天前
|
安全 Linux 数据安全/隐私保护
在 Linux 系统中,查找文件所有者是系统管理和安全审计的重要技能。
在 Linux 系统中,查找文件所有者是系统管理和安全审计的重要技能。本文介绍了使用 `ls -l` 和 `stat` 命令查找文件所有者的基本方法,以及通过文件路径、通配符和结合其他命令的高级技巧。还提供了实际案例分析和注意事项,帮助读者更好地掌握这一操作。
24 6
|
7天前
|
Linux
在 Linux 系统中,`find` 命令是一个强大的文件查找工具
在 Linux 系统中,`find` 命令是一个强大的文件查找工具。本文详细介绍了 `find` 命令的基本语法、常用选项和具体应用示例,帮助用户快速掌握如何根据文件名、类型、大小、修改时间等条件查找文件,并展示了如何结合逻辑运算符、正则表达式和排除特定目录等高级用法。
33 6
|
8天前
|
监控 Linux Perl
Linux 命令小技巧:显示文件指定行的内容
在 Linux 系统中,处理文本文件是一项常见任务。本文介绍了如何使用 head、tail、sed 和 awk 等命令快速显示文件中的指定行内容,帮助你高效处理文本文件。通过实际应用场景和案例分析,展示了这些命令在代码审查、日志分析和文本处理中的具体用途。同时,还提供了注意事项和技巧,帮助你更好地掌握这些命令。
22 4
|
14天前
|
网络协议 Linux
linux系统重要文件目录
本文介绍了Linux系统中的重要目录及其历史背景,包括根目录、/usr、/etc、/var/log和/proc等目录的结构和功能。其中,/etc目录下包含了许多关键配置文件,如网卡配置、DNS解析、主机名设置等。文章还详细解释了各目录和文件的作用,帮助读者更好地理解和管理Linux系统。
38 2
|
13天前
|
缓存 监控 Linux
|
16天前
|
Linux Shell 数据库
文件查找是Linux用户日常工作的重要技能介绍了几种不常见的文件查找方法
文件查找是Linux用户日常工作的重要技能。本文介绍了几种不常见的文件查找方法,包括使用`find`和`column`组合、`locate`和`mlocate`快速查找、编写Shell脚本、使用现代工具`fd`、结合`grep`搜索文件内容,以及图形界面工具如`Gnome Search Tool`和`Albert`。这些方法能显著提升文件查找的效率和准确性。
38 2
|
20天前
|
Linux 数据库
linux 全局搜索文件
在 Linux 系统中,全局搜索文件常用 `find`、`locate` 和 `grep` 命令。`find` 根据文件名、类型、大小、时间戳等条件搜索;`locate` 通过预构建的数据库快速查找文件;`grep` 在文件中搜索特定文本,常与 `find` 结合使用。选择合适的命令取决于具体需求。
|
23天前
|
Linux 开发工具 Perl
Linux命令替换目录下所有文件里有"\n"的字符为""如何操作?
【10月更文挑战第20天】Linux命令替换目录下所有文件里有"\n"的字符为""如何操作?
35 4
|
22天前
|
运维 安全 Linux
Linux文件清空的五种方法总结分享
每种方法各有优势,选择最合适的一种或几种,可以极大提高您的工作效率。更多有关Linux系统管理的技巧与资源,欢迎访问,持续提升您的运维技能。
61 1