一、io和文件
> 1、标准io
> 2、文件io
> 3、库的封装
> 4、文件: 相关数据的有序集合;
> 5、文件名: 数据的有序集合的名称。
二、进线程
多进程编程
多线程编程
线程间通信
进程间通信:管道、信号,套接字
三、文件类型
文件概念:相关数据的有序集合,存放在磁盘上。
- 普通文件
d目录文件
c字符设备文件:键盘,鼠标;读取数据的大小。
b块设备文件:(磁盘等存储设备文件)
p管道文件:pine(有名管道文件)
l符号链接文件:快捷方式
s套接字文件:(UNIX域套接字的文件)
块设备和字符设备的区别:读取数据的大小
注意:不同的操作系统使用的文件类型不同
四、标准IO
标准IO遵循 --->ANSI C标准,
实质:使用标准IO就是调用标准的C库函数
特点:标准IO通过缓冲机制减少系统调用的次数,从而提高程序运行的效率。
标准io是文件IO的再封装。
标准IO是高级IO:一般用于普通文件数据读写
文件IO是低级IO
五、流的类型
1、文本流:以ascii码的方式进行存储。
2、二进制流:如果是字符数字,将字符数据转换位对应的二进制进行存储,非字符数字通过转换为ASCII码进行存取。
文本流处理换行符的方式:
文本流:会将换行符‘\n’转换位'\r''\n'
二进制流:没有区分,不进行转换。
linux下没有做区分
六、缓冲区类型
1、全缓存:文件打开,默认使用全缓存。
全缓存特点:当全缓存区满的时候,才会真正的实现IO操作。或者强行刷新缓冲区。
2、行缓存
当IO操作跟终端相关的时候,使用行缓存。
当遇到换行符或者缓冲区满才会输出输入,或者强行刷新。
3、无缓存
当IO操作时跟错误相关时,使用无缓存。
标准IO三个特殊流(流指针);
(1)标准输入流(键盘)0 stdin
(2)标准输出流(终端)1stdout
(3)标准错误流 2 stderr
> 刷新缓冲区的5种方法;
> 1、‘\n’
> 2、fflush(FILE *stream);
> 3、fclose(FILE *stream);
> 4、程序正常结束或者exit结束程序。
> 5、特殊情况,当输出流遇到输入流时,会刷新行缓冲区。
练习:C语言输出行缓存 1024
printf("hello\n");
printf("行缓存:%d\n",stdou->_IO_buf_end - stdout->_IO_buf_base);
man手册
第一页:shell指令和可执行程序
第二页:系统调用函数
第三页:标准C库函数
七、标准IO相关库函数
fopen()/fclose()--打开关闭文件
fget()/fput()--按字节输入输出
fgets()/fputs()--按行输入输出
fread()/fwrite()--按对象输入输出
fflush()--刷新流
foef()/ferror()--判断错误流
ftell()/fseek()/rewind()--定位流
## 利用fseek和ftell定位
函数的查找:EOF对应的宏就是 -1;
int fseek(FILE *stream, long offset, int whence);
参数功能:
FILE *stream:自己定义的流指针;
long offset:偏移量
whence:何处
返回值:
整型int
练习:打开文件的上限
int count=0;
while(fopen ("count.txt","w"))
{
count++;
}
printf("count:%d\n",count+3);
练习:利用fgetc and fputc 完成文件的复制cp,并且计数 源文件的字符个数(包括空格);
#include<stdio.h>
int main(int argc, char *argv[])
{
printf("argc:%d\n",argc);
printf("argv:%s\n",argv[0]);
FILE *a = fopen(argv[1],"r");
FILE *b = fopen(argv[2],"w");
char ch;
int count=0;
while((ch = fgetc(a)) != -1)
{
count++;
fputc(ch,b);
}
printf("count:%d\n",count-1);
fclose(a);
fclose(b);
return 0;
}
练习:用fgets fputs
//复制照片
FILE *fp = fopen(argv[1],"r");
FILE *fp2 = fopen(argv[2],"w");
char buf[2] = {0};
int line = 0;
while(1){
fgets(buf,sizeof(buf),fp);
if( feof(fp) || ferror(fp)){
perror("fgets");
break;
}
if(buf[0] == '\0')
{
fputc('\0',fp2);
line++;
}
//遇见‘0’就立即停止,
line +=fputs(buf,fp2);
}
printf("line:%d\n",line);
fclose(fp);
fclose(fp2);
//记录行数
#include<stdio.h>
#include<string.h>
int main(int argc, char *argv[])
{
char buf[36] = {0};
int count=0;
FILE *a=fopen("1.txt","r");
FILE *b=fopen("2.txt","w");
while( (fgets(buf,36,a)) != NULL)
{
if
count++;
fputs(buf,b);
}
printf("行数:%d\n",count);
fclose(a);
fclose(b);
return 0;
}
重要问题理解
问题:
**1、文件里面写满了以后,4096哪里是什么。
如果一行写满了的话,必须要有一个‘\n’作为结束。
2、fgets读出来的是什么,fputs放回去的是什么。
fgets是要遇到‘\n’,或者读满才会结束。
fputs是遇到'\0'或者‘0’值就会结束,为什么可以直接用来文件拷贝。
因为文件分为文本流和二进制流,二进制流,和图片(混合存储)。
3、怎么解决fputs放回去的时候,自己增加一个‘\n’
‘\n'不用去管,主要是'\0'要主动强行添加进去(遇到二进制流,或者混合存储);**
八、文件IO
**1、不带缓存的IO操作,是系统调用的接口函数。
2、文件描述符:
是系统最小的、未用的非负整数,(是内核向进程返回的 操作文件的标识符)
标准输入、标准输出、标准错误输出(0, 1, 2)
3、接口函数
1、函数原型:
int open(const char *path, int oflag, mode_t mode);
功能:
打开或者创建打开文件
头文件:
#include <sys/types.h>
#include <fcntl.h>
参数:
path :待操作的文件名(可包含路径)
oflag :文件的操作方式:
O_RDONLY、O_WRONL、O_RDWR | O_CREAT | O_APPEND.....
mode :文件的存根权限(8进制表示法)
返回值:
成功:文件描述符
失败:-1,并设置错误信息
2、函数原型:
ssize_t read(int filedes, void *buf, size_t n);
功能:
从 filedes 对应的文件中,读取前 n个字节的数据到 buf地址上。
头文件:
#include <unistd.h>
参数:
filedes :文件描述符
buf :存储数据的缓冲区首地址
n :要读取的字节数
返回值:
成功 :实际读取的字节数
文件末尾:0
失败 :-1,并设置错误信息
3、函数原型:
ssize_t write(int filedes, void *buf, size_t n);
功能:
将buf 地址上前 n个字节数据 写入 filedes对应的文件中。
头文件:
#include <unistd.h>
参数:
filedes :文件描述符
buf :存储数据的缓冲区首地址
n :要写入取的字节数
返回值:
成功 :实际写入的字节数
失败 :-1,并设置错误信息
4、函数原型:
int close(int filedes);**
九、获取文件信息(包括目录信息);
这里是引用
首先-->获取文件的属性:
我们要用到的函数是,系统调用的函数。
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *pathname, struct stat * statbuf);
返回值:
成功返回 0;
失败返回 -1;
参数:
pathname: 文件路径和文件名称,就是一个字符串或者传入一个字符串的首地址。
statbuf:结构体指针的首地址。
struct stat buf详解:
struct stat
{
dev_t st_dev; /* ID of device containing file */文件使用的设备号
ino_t st_ino; /* inode number */ 索引节点号
mode_t st_mode; /* protection */ 文件对应的模式,文件,目录等
nlink_t st_nlink; /* number of hard links */ 文件的硬连接数
uid_t st_uid; /* user ID of owner */ 所有者用户识别号
gid_t st_gid; /* group ID of owner */ 组识别号
dev_t st_rdev; /* device ID (if special file) */ 设备文件的设备号
off_t st_size; /* total size, in bytes */ 以字节为单位的文件容量
blksize_t st_blksize; /* blocksize for file system I/O */ 包含该文件的磁盘块的大小
blkcnt_t st_blocks; /* number of 512B blocks allocated */ 该文件所占的磁盘块
time_t st_atime; /* time of last access */ 最后一次访问该文件的时间
time_t st_mtime; /* time of last modification */ /最后一次修改该文件的时间
time_t st_ctime; /* time of last status change */ 最后一次改变该文件状态的时间
};
st_mode权限:
st_mode 主要包含了 3 部分信息:
15-12 位保存文件类型
11-9 位保存执行文件时设置的信息
8-0 位保存文件访问权限
S_IFMT 0170000 文件类型的位遮罩
S_IFSOCK 0140000 套接字
S_IFLNK 0120000 符号连接
S_IFREG 0100000 一般文件
S_IFBLK 0060000 区块装置
S_IFDIR 0040000 目录
S_IFCHR 0020000 字符装置
S_IFIFO 0010000 先进先出
S_ISUID 04000 文件的(set user-id on execution)位
S_ISGID 02000 文件的(set group-id on execution)位
S_ISVTX 01000 文件的sticky位
S_IRUSR(S_IREAD) 00400 文件所有者具可读取权限
S_IWUSR(S_IWRITE)00200 文件所有者具可写入权限
S_IXUSR(S_IEXEC) 00100 文件所有者具可执行权限
S_IRGRP 00040 用户组具可读取权限
S_IWGRP 00020 用户组具可写入权限
S_IXGRP 00010 用户组具可执行权限
S_IROTH 00004 其他用户具可读取权限
S_IWOTH 00002 其他用户具可写入权限
S_IXOTH 00001 其他用户具可执行权限
上述的文件类型在POSIX中定义了检查这些类型的宏定义:
S_ISLNK (st_mode) 判断是否为符号连接
S_ISREG (st_mode) 是否为一般文件
S_ISDIR (st_mode) 是否为目录
S_ISCHR (st_mode) 是否为字符装置文件
S_ISBLK (s3e) 是否为先进先出
S_ISSOCK (st_mode) 是否为socket
若一目录具有sticky位(S_ISVTX),则表示在此目录下的文件只能被该文件所有者、此目录所有者或root来删除或改名,在linux中,最典型的就是这个/tmp目录啦。
## 练习利用读取目录信息和文件信息 完成一部分ls功能
```c
/*===============================================
* 文件名称:2_homework.c
* 创 建 者:
* 创建日期:2022年07月17日
* 描 述:
================================================*/
#include <stdio.h>
#include <strings.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <sys/types.h>
#include <time.h>
#include <pwd.h>//getpwuid
#include <grp.h>
#include <sys/stat.h>
#include <unistd.h>
void print(char *str,char *name);
struct stat buf;//读取文件信息,需要的缓冲区。
struct tm *tm;//存放时间信息的结构体指针。
int ret;//
struct dirent *p;//读取目录信息的结构体指针,可以通过它访问目录结构体里面的信息。
char buf2[64] = {0};//save path and name,我用来把文件的目录和名称拼接到一起,定义的字符串。
int main(int argc, char *argv[])
{
DIR *dir;
dir = opendir(argv[1]);
if(dir == NULL)
{
perror("opendir");
return -1;
}
while( (p = readdir(dir)) )
{
strcat(buf2,argv[1]);
if(argv[1][strlen(argv[1])-1] != '/')
{
strcat(buf2,"/");
}
strcat(buf2,p->d_name);
print(buf2,p->d_name);
memset(buf2,0,sizeof(buf));
}
return 0;
}
int print(char *str,char *name)//
{
if(name[0] != '.')
{
ret = stat(str,&buf);
if(ret == -1)
{
perror("stat");
return -1;
}
switch (buf.st_mode & S_IFMT)
{
case S_IFBLK:printf("b");break;
case S_IFCHR:printf("c");break;
case S_IFDIR:printf("d");break;
case S_IFIFO:printf("p");break;
case S_IFLNK:printf("l");break;
case S_IFREG:printf("-");break;
case S_IFSOCK:printf("s");break;
default:printf("unkownfile");break;
}
for(int i = 8;i >= 0;i--)
{
if(buf.st_mode & (1<<i))
{
switch(i%3)
{
case 2:printf("r");break;
case 1:printf("w");break;
case 0:printf("x");break;
}
}
else
{
printf("-");
}
}
printf(" %ld",buf.st_nlink);//show number of link;
struct passwd *uid;
struct group *gid;
gid = getgrgid(buf.st_gid);
uid = getpwuid(buf.st_uid);
printf(" %s",uid->pw_name);
printf(" %s",gid->gr_name);
int size = (int)buf.st_size;//print size of file
printf(" %5d",size);
tm = localtime(&buf.st_mtime);//print time and filename
printf("%2d月%4d %02d:%02d %s",tm->tm_mon+1,tm->tm_mday,tm->tm_hour,tm->tm_min,name);
printf("\n");
}
return 0;
}
10、静态库和动态库制作
静态库
第一步–>要gcc -c x.c得到材料
第二步–> arc crs libx.a x.o
第三步–> gcc text.c -lx -L指定路径
动态库
第一步–>gcc -fPIC -c x.c
第二步–>gcc -share -o libx.so x.o
第三步–>gcc text.c -lx -L指定路径
11、printf宏定义调试
#define LINK //这是方法一
#ifdef LINK
#define Debug(info) printf(info)
#else
#define Debug(info)
#endif
//方法二:在编译时gcc -DLINK 生成一个全局的宏定义
练习 解析txt文件
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char buf[36] = {0};
char name[20] = {0};
int flag = 1;
gets(name);
FILE *fp = fopen("1.txt","r");
while(!feof(fp))
{
memset(buf,sizeof(buf),0);
fgets(buf,sizeof(buf),fp);
if(strcmp(name,strtok(buf,"=")) == 0)
{
flag = 0;
printf("%s\n",strtok(NULL,"\0"));
}
}
fclose(fp);
return 0;
}
练习 利用hash提高查询速度
//首先讲解一下hash查找,就是根据键值hash[key][data],存放数据,先确定一个数组大小,一般数据数量/0.75;然后%得到的值;
//这个时候就可以提高查找效率到常数级;
## hash.h