Linux应用(1)——文件IO
一、简介
1.1文件属性
Linux下文件共有'bcd-lsp'七个属性类型
b: block 块设备 (储存设备) 驱动开发中用到的文件属性
c: char 字符设备 驱动开发中用到的文件属性(lcd屏 定时器 通信接口)
d : 目录
- : 普通文件 .c .s .txt .doc ....
l : link 链接文件-- 数据库开发中
s : socket 套件字文件---网络编程开发中
p: pipe 管道文件----进程通信开发中
相对路径:从Linux最初,根路径开始的路径
相对路径:以某一级路径作为参考
1.2Linux平台命令操作
第一类:系统命令
Linux系统把常规要用到的功能已经编写好C代码,并固定编译生成了固定的可执行文件名,全部都存放在linux根目录下 bin 的文件中, 用户直接调取即可
第二类:用户自定义命令
用户自己编写C代码,编译生成可执行文件,运行可执行文件
---用户生成的可执行文件名 是 自定义的
1.2.1 命令格式
- 命令的执行: 在终端上命令, 回车运行
- 终端上一行也可以输入多个命令 使用 ; 隔开,但通常一个命令一行
- 存在几种命令查找方式
方式1:通过命令查询手册
方式2:可以直接在linux下终端上通过命令名 --help方式3:可以直接在linux终端下通过man命令,这是一种英文查找方式, 包含所有的命令
- 命令的格式
命令提示符(命令名) [选项] <参数/文件>
解释说明:
[ ] 都是选项,可有可无,< > 或无括号,必须要添加的
1.3常用的系统命令使用方式
用户常见的系统命令分成四类:
第一类:和系统运行相关的
echo > >> * pwd cd alias grep cal date chmod
第二类:和文件相关的
touch rm cp mv cat gedit gcc g++
第三类:和文件夹(目录)相关的
mkdir rmdir cp mv ls tar
第四类: 和用户使用相关的
useradd su sudo passwd userdel
二、基础命令
2.1与系统相关
1.echo
把echo 空格后的内容 原样显示在终端上
注意事项:
要输出的内容 是否使用 “” 引起来 都可以,效果是一样的, “” 不会输出
如果要想显示” 需要使用 转义字符 \”
命令2: > >>
一般都是和echo 命令配置使用; 输出重定向
命令格式:把要显示的内容存放到文件中
echo 要显示的内容 > 文件名
echo 要显示的内容 >> 文件名
注意事项:
文件如果 不存在 , 就会先自动创建文件再写入
文件如果 存在 :
> 以覆盖方式写入
>> 以追加方式写入
命令3:alias unalias
命令功能:给命令起别名 删除命令别名
--主要针对一些比较长的系统命令
使用方式1: alias 查找系统下以存在的别名
使用方式2:给命令起别名
格式 alias 别名=’命令原名’
注意 是一对单引号’’
命令4: *
命令5: pwd
命令6: cd
命令7: cal
显示日历功能
命令格式1: cal 显示当前月份的日历
命令格式2: cal 年份 显示指定年份所有月份的日历
命令格式3: cal 月份 年份 显示指定月份的日历
命令8: date
显示当前时间
命令9: chmod
2.2与文件相关
新建touch、删除rm、编写 gedit 、编译 gcc 、运行、查看cat、复制cp 、剪切mv
1.用一条指令在桌面上创建1.c 2.c 3.c 4.txt 5.txt 6.txt共计六个文件
2.用一条指令实现:在桌面上创建一个名为test_dir的目录文件,在该目录中创建名为a1的目录文件,在a1中创建名为b2的目录文件
使用 mkdir -p 递归创建目录![]()
mkdir -p
3.用一条指令实现:删除桌面上文件夹test_dir,要求删除时有提示(默认延续1.3.2执行)
以询问方式删除非空目录 rm -ir![]()
rm -ir
4.结合通配符,用一条指令实现:删除桌面上所有的以.c结尾的文件
![]()
rm *.c
5.在桌面上的C_code/0708/ 目录下新建源文件,编写代码实现
步骤1:在linux桌面开一个终端
步骤2: mkdir -p C_code/0708/ 递归创建文件夹
步骤3:cd C_code/0708/ 跳转到文件夹下
步骤4: 新建编写文件 gedit hello.c
步骤5: 编译源文件 gcc hello.c -o hello
步骤6: 运行可执行文件 ./hello
6.使用ls -l,分析目标文件的类型和权限问题
7.如何创建一个名为 test.txt 的空文件,并设置其权限为 rw-r--r--(644)
步骤1: touch test.txt
步骤2: chmod 0644(八进制) test.txt
8.如何将 /home/user/data 目录压缩为 data.tar.gz
步骤1:保证 /home/user/data 存在
mkdir -p /home/user/data
步骤2:在压缩 tar -czvf data.tar.gz /home/user/data
三、文件操作
3.1简介
Linux系统输入输出的对象为应用层 和 驱动层(内核)
以应用层为中心,
input输入:数据从驱动层/文件进入应用层
output输出:数据从应用层到驱动层/文件
Linux文件下存在多种io相关api函数
读函数:就是输入
写函数:就是输出
3.2 分类
分类方式一:按照文件属性
第一类:特殊文件
只有三个: 标准输入(键盘)、标准输出(屏幕)、标准错误
第二类:普通文件: .c .txt .h .s .sh 等等分类方式二:按照API函数特性
第一类: 文件io,内核提供的系统调用,直接操作文件描述符(fd)
第二类: 标准io,C 库(glibc)在用户空间实现的带缓冲的流式 I/O,用 FILE *
第三类: 目录io ,门用来遍历/读取/创建/删除目录的一组函数,同样基于文件描述符或 FILE *,但接口与普通文件不同
3.3文件操作注意事项
- 文件操作的方向及对应要调取的API函数
- 采用文件可以做特殊文件操作,也可以对普通文件操作
对特殊文件的操作:是可以直接读写的
对普通文件的操作:需先打开文件,然后再操作- 文件指针的偏移特性,需要考虑是应用还是要避开
3.4文件io特性
- 文件IO可以对普通文件操作,也可以对特殊文件操作
- 文件会存在文件指针,当open打开文件时,文件指针默认在文件开始处,对文件的读写,都会造成文件指针的偏移(以字节为单位),当close关闭后,文件指针默认在文件末尾
- 文件IO的读写函数没有缓冲区,读写是马上生效的,是一种低速IO
- read 输入--> 文件数据的读取
- write 输出--->数据存放到文件
特殊文件 | 文件IO--文件描述符 | 标准IO--文件流指针 |
标准输入 | 0 | stdin |
标准输出 | 1 | stdout |
标准错误 | 2 | stderr |
普通文件 | 默认从3开始分配 |
四、文件函数
4.1 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);
函数参数:
@param1
const char * pathname: 带路径的文件名 "/home/save.txt"
@param2
int flags : 可以进行按位或 一起的 文件指定打开方式
O_RDONLY : 只读 O_WRONLY : 只写 O_RDWR : 可读可写
O_CREAT :如果文件存在,就直接打开;如果文件不存在,就创建并打开
O_EXCL : 如果文件存在,就直接打开;如果文件不存在,就报错,结束函数
O_TRUNC : 以 覆盖方式打开文件
O_APPEDN : 以追加方式打开文件
O_RDWR | O_CREAT | O_TRUNC : 以覆盖的方式打开一个可读可写的文件,如果文件存在,就直接打开;如果文件不存在,就创建并打开
@param3
mode_t mode: 文件掩码--一个八进制数据(和chmod修改权限命令参数一样),表示文件本身具备什么权限,该参数通常和 O_CREAT 组合应用;也即是说,如果flags参数中存在 O_CREAT,那就填充参数3;如果flags参数中没有O_CREAT,那就不需要参数3
函数返回值:
int 文件描述符 (文件ID),后续通过 文件描述符 对文件进行操
<0: 打开失败
>0: 打开成功-默认从3开始分配
函数功能:
对同一个文件可以多次打开,文件描述符依次递增;
可以利用对同个文件的多次打开,实现把文件指针偏移到文件开始处;同样也可以利用lseek函数实现
4.2 close函数
头文件:#include <unistd.h>
函数原型:
int close(int fd)
函数参数:
@param1
int fd 打开文件的文件描述符
函数返回值:
int 是否关闭成功--通常不用
函数功能:用来关闭文件
函数特性:
文件关闭后, fd 文件描述符就会被系统回收
4.3 read函数
头文件:
#include<unistd.h>
函数原型:
ssize_t read(int fd,void * buf ,size_t count);
函数参数:
@param1
int fd: 文件描述符--表明要对哪个文件操作
@param2
void * buf: 应用层接收 buff
@param3
size_t count: 一次从文件中读取的字节数
函数返回值:
ssize_t :实际从文件中读取的字节数,通常会作为文件是否结束(尾部)的判断依据
函数功能:一次从fd文件 中读取count字节到buf中
读函数--输入---数据从文件进入应用层(内存buf中)----数据的取操作
函数特性:
读操作同样会改变文件指针的偏移
char buf[20];
scanf("%s",buf); 等价于 read(0,buf,20);
4.4 write函数
头文件:
#include<unistd.h>
函数原型:
ssize_t write (int fd,const void * buf,size_t count);
函数参数:
@param1
int fd: 文件描述符--要写到哪个文件中
@param2
const void * buf:要写入的数据
@param3
size_t count: 要写入的字节数
函数返回值:
ssize_t 实际写入的字节数--应用不多
函数功能:
把内存buf中count字节写入到fd文件中
写函数--输出--数据从应用层(内存buf)写到文件中--数据的存操作
函数特性:
1.写操作同样会改变文件指针的偏移
printf("hello\n"); 等价于 write (1,"hello\n",7);
4.5 Iseek函数
头文件
#include<sys/types.h>
#include<unistd.h>
函数原型:
off_t lseek(int fildes,off_t offset ,int whence);
函数参数:
@param1
int fildes: fd 文件描述符--要调整哪个文件
@param2
off_t offset: 偏移量--单位 字节;>0,往右偏移;<0,往左偏移
@param3
int whence: 参考位置--文件指针从哪个位置开始
系统提供了三个参考位置宏表示
SEEK_SET : 文件开始处
SEEK_CUR : 文件当前处
SEEK_END : 文件尾部
lseek(fd,0 ,SEEK_SET);// 实际上就是文件开始处
函数返回值:
是否偏移成功--使用不多
函数功能:
把fd文件的文件指针从whence开始偏移offset
调整文件指针的位置
函数特性---重新调整文件指针的位置
---用户就需要考虑什么场景使用???--因为读写改变文件指针的位置是一种默认特性
常用于需要重新调整文件指针的场景
场景1: 信息的查找--每次都是从头开始
场景2; 函数中多次对同一个文件读写操作
4.6 练习
/*2.首先新建一个文件 比如 1.txt 写入一串任意字符串 比如: "sdg23542sdgfDSAsGA",编写一个函数,读取1.txt文件内容,一次读取4个字节,查找指定字符串。是否出现和出现的次数: 比如 "sdgf"*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main()
{int fd ;//定义文件描述符char buf[4]={0} ;//定义接收数组int num ;int count=0 ;//次数fd=open("./1.txt",O_RDONLY);//以只读的方式打开文件if(fd<0) //打开失败{printf("open error\n") ;return -1 ;}printf("fd=%d\n",fd) ;//输出文件描述符while(1){num= read(fd,buf,4) ;//读取fd对应文件中四个字节到buf,并返回实际读到的字节数if(num==4){if(strncmp(buf,"sdgf",4)==0){count++ ;memset(buf,0,4) ;//将4个0写入到buf中}}else if(num<4)break ;}printf("共出现了%d次",count);return 0 ;
}
/*编写一个函数, 从键盘得到3个数据信息,写入到 2.txt文件中,其中每个信息占用2.txt中的一行*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>int main()
{int fd;char buf[20];//用于接收fd=open("./2.txt",O_RDWR | O_CREAT | O_TRUNC,0644);// 以覆盖的方式打开一个可读可写的文件,若文件不存在,则创建后打开if(fd<0){write(1,"open error\n",11);//往标准输出中写入return -1;}printf("fd=%d",fd);for(int i=0;i<3;i++){printf("请键盘输入第%d行\n",i+1);scanf("%s",buf);strcat(buf,"\n");write(fd,buf,strlen(buf));}return 0;
}
/*用文件io实现 往 4.txt文件中写入3行数据,并读取文件中的数据显示*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>int main()
{int fd;char buf[20];//用于接收char revbuf[20];fd=open("./4.txt",O_RDWR | O_CREAT | O_TRUNC,0644);// 以覆盖的方式打开一个可读可写的文件,若文件不存在,则创建后打开if(fd<0){write(1,"open error\n",11);//往标准输出中写入return -1;}printf("fd=%d",fd);for(int i=0;i<3;i++){printf("请键盘输入第%d行\n",i+1);scanf("%s",buf);strcat(buf,"\n");write(fd,buf,strlen(buf));}lseek(fd,0,SEEK_SET);//将文件指针偏移到最开始处read(fd,revbuf,50);printf("读到的数据为:\n%s",revbuf);return 0;
}
/*利用文件io实现 cp 拷问文件命令的重写 cp 1.c 2.c */
//运行格式 ./可执行命令 源文件 目标文件
//带参主函数 ./mycp 1.txt 2.txt
//int argc 终端运行时参数个数
//char*argv[] 每一个参数
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>int main(int argc,char*argv[])
{int srcfd,objfd;//源文件,目标文件描述符char revbuf[20];int num,count=20;//num为实际读到的字节数,count为计划读到的字节数if(argc!=3){printf("请以%s srcfile objfile 形式运行\n",argv[0]);return -1;}//先打开源文件srcfd=open(argv[1],O_RDONLY|O_EXCL);//以只读的方式打开源文件,如果文件不存在,则报错返回
if(srcfd<0)
{printf("%s open error\n",argv[1]);return -1;
}
printf("srcfd=%d\n",srcfd);
//在打开目标文件objfd=open(argv[2],O_RDWR | O_CREAT | O_TRUNC,0644);//以可读可写覆盖的方式打开源文件,如果文件不存在,则创建文件
if(objfd<0)
{printf("%s open error\n",argv[2]);return -1;
}
printf("objfd=%d\n",objfd);
//开始读
while(1)
{num=read(srcfd,revbuf,count);//读源文件20个字节到revbufif(num==count){write(objfd,revbuf,num);}else{write(objfd,revbuf,num);break;}}return 0;
}