IO
标准IO: C库函数提供的文件操作统一接口,携带缓冲;
文件IO:最直观的系统调用,每一次系统调用操作系统都会参与其中,开销较大,不携带缓冲;
1.缓冲区类型
全缓冲:把缓冲区占满后或特殊情况下才会刷新缓冲区;
行缓冲:缓冲区中遇到换行符就会刷新缓冲区;
无缓冲:任何数据进入缓冲区后马上刷新 strerr
;
setvbuf
int setvbuf(FILE *stream, char *buf, int mode, size_t size);
功能:设置某个流的缓冲区类型;
返回值:成功返回0,失败返回非0;
stream:要更改的流;
buf:要指定的缓冲区地址,对于无缓冲无效,可以写NULL;
mode:要更改的缓冲区类型
——IOFBF:全缓冲;
——IOLBF:行缓冲;
——IONBF:无缓冲;
size:指定的缓冲区大小
2.库函数接口
2.1 打开文件
FILE *fopen (const char *path, const char *mode);
功能:打开或创建后再打开文件;
返回值:成功返回一个流指针,失败返回NULL
path:要打开的文件(可以包含路径);
mode:打开文件的方式;
r:以只读的方式打开;
r+:以读写的方式打开; //前两个一定要文件已存在
w:以只写的方式打开一个文件,文件不存在则创建,文件存在则清空;
w+:以读写的方式打开一个文件,文件不存在则创建,文件存在则清空;
a:以只写的方式打开一个文件,文件不存在则创建,文件存在则内容会写在末尾;
a+:以读写的方式打开一个文件,文件不存在则创建,文件存在则内容会写在末尾;
r+b
a+b
wb
注:加上b表示打开的是二进制文件,但是Linux不区分文本流和二进制流
使用方式:
#include <stdio.h>
int main(int argc, char *argv[])
{
FILE *fp = fopen("要打开的文件", "打开文件的方式");
if(fp == NULL){
perror("fopen");
return -1;
}
printf("Fopen success, fp = %p\n", fp);
return 0;
}
2.2重定向
FILE *freopen(const char *pathname, const char *mode, FILE *stream);
功能:通过打开一个文件产生的流去替代原来的流;
返回值:成功返回一个流指针,失败返回NULL;
pathname:指定要打开的文件;
mode:打开文件的方式;
stream:要替换的流(原来的流);
使用方式:
#include <stdio.h>
int main(int argc, char *argv[])
{
printf("Hi! I'm LCW.\n");
FILE *fp = freopen("要打开的文件","打开的方式",stdout);
if(fp == NULL){
perror("freopen");
return -1;
}
printf("Freopen success, fp = %p\n", fp);
//打开重定向的文件,可以看到“Hi! I'm LCW.”写入了文件中;
return 0;
}
2.3关闭流
int fclose(FILE *stream);
功能:先刷新流的内容再关掉这个流;
返回值:成功返回0,失败返回-1;
stream:要关掉的流;
使用方式:
#include <stdio.h>
int main(int argc, char *argv[])
{
FILE *fp = fopen("xxx", "r");
if(fp == NULL){
perror("fopen");
return -1;
}
printf("Fopen success, fp = %p\n", fp);
int ret;
ret = fclose(fp);
if(ret < 0){
perror("fclose");
return -1;
}
printf("Fclose success!\n");
return 0;
}
3.读写流
3.1按字节读写
3.1.1读
int fgetc(FILE *stream);
功能:从流里面读取一个字符的内容;
返回值:成功返回实际读到的字符ASC码,失败返回EOF;
stream:要读的流;
使用方式:
#include <stdio.h>
int main(int argc, char *argv[])
{
FILE *fp = fopen("要打开的文件", "r");
if(fp == NULL)
{
perror("fopen");
return -1;
}
char a = fgetc(fp); //读取流中的第一个字符
//相当于char a = getchar();
if(a < 0){
perror("fgetc");
return -1;
}
printf("%d\n", a);
return 0;
}
3.1.2写
int fputc(int c, FILE *stream);
功能:往流里面写入一个字符的内容;
返回值:成功返回实际写入的字符ASC码,失败返回EOF;
stream:要写的流;
使用方式:
#include <stdio.h>
int main(int argc, char *argv[])
{
FILE *fp = fopen("xxx", "a+");
if(fp == NULL){
perror("fopen");
return -1;
}
int a = 97;
int ret = fputc(a, fp);
if(ret < 0){
perror("fputc");
return -1;
}
return 0;
}
3.2练习
编写一个文件mycp.c
,执行它能完成cp
的功能,及将1.c
的内容复制到2.c
当中;
#include <stdio.h>
int main(int argc, char *argv[])
{
if(argc != 3){
printf("U need input %s <src_file> <dest_file>\n", argv[0]);
return -1;
}
FILE *fp_src = fopen(argv[1], "r");
if(fp_src == NULL){
perror("fopen src");
return -1;
}
FILE *fp_dest = fopen(argv[2], "w");
if(fp_dest == NULL){
perror("fopen dest");
return -1;
}
char ch = -1;
while( (ch = fgetc(fp_src)) != EOF ){
fputc(ch, fp_dest);
}
fclose(fp_src);
fclose(fp_dest);
return 0;
}
4.标准 IO
4.1按行读写
4.1.1读
char *fgets(char *s, int size, FILE *stream);
功能:读取一行(它想读取一行);
返回值:成功返回字符串存放的地址<s>,失败返回NULL;
s:用于存放读取到的数据的内存空间地址;
size:预计想读多少个字节的内容,不应该超过s的大小;
stream:要读取的流;
注:fgets只会读取size-1个字节,如果读到了换行那么换行会进入到缓冲区并在最后添加一个'\0',如果没有读到换行,则直接在最后添加'\0';
使用方式:
#include <stdio.h>
int main(int argc, char *argv[])
{
char buf[10]; //设置缓冲区
char *p = fgets(buf, sizeof(buf), stdin); //从终端输入中读取
if(p == NULL){
perror("fgets");
return -1;
}
printf("p = %s", buf); //把读取的字符打印出来
}
#include <stdio.h>
int main(int argc, char *argv[])
{
FILE *fp = fopen("test", "r");
char buf[64] = {1,2,3,4,5,6,7,8};
fgets(buf, sizeof(buf), fp);
int i;
for(i = 0; i < 8; i++){
printf("%d\n", buf[i]);
}
}
4.1.2写
int fputs(const char *s, FILE *stream);
功能:往流中写入一行数据(它想写一行);
返回值:成功返回1,失败返回EOF;
s:要写入的内容存放的内存空间地址;
stream:要写入的流;
使用方式:
#include <stdio.h>
int main(int argc, char *argv[])
{
char buf[] = {'o', 'k', '\n', 'a', 'b', '\0'};
int ret = fputs(buf, stdout); //往终端中写入缓冲区中的字符
if(ret < 0){
perror("fputs");
return -1;
}
}
4.2按对象读写
4.2.1读
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:从流里面读取内容出来;
返回值:成功返回实际读到的对象数,失败返回0;
ptr:存放读取到的内容的内存空间地址;
size:每个对象所占字节数;
nmemb:预计要读多少个对象;
stream:要从哪个流中读取;
使用方式:
#include <stdio.h>
int main(int argc, char *argv[])
{
FILE *fp = fopen("test", "r"); //以只读的方式读取文件test
if(fp == NULL){
perror("fopen");
return -1;
}
char buf[21] = {0};
int ret = fread(buf, 7, 3, fp); //从fp流中读取三个对象,每个对象中有7个字节,并存放到缓冲区buf中
if(ret == 0){
perror("fread");
return -1;
}
printf("ret = %d, data = %s\n", ret, buf);
}
4.2.2写
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:往流里面写入内容;
返回值:成功返回实际写入的对象数,失败返回0;
ptr:存放要写入的数据的内存空间地址;
size:每个对象所占字节大小;
nmemb:预计要写多少个对象;
stream:要往哪个流中写入;
4.3文件定位
int fseek(FILE *stream, long offset, int whence);
功能:改变文件内“光标”的位置;
返回值:成功返回0,失败返回-1;
stream:要改变的文件流;
offset:偏移量,正数往后偏移,负数往前偏移;
whence:基准点;
SEEK_SET:文件头;
SEEK_CUR:文件当前位置:
SEEK_END:文件尾;
使用方式:
#include <stdio.h>
int main(int argc, char *argv[])
{
FILE *fp = fopen("test", "r+"); //以读写的方式打开test文件
if(fp == NULL){
perror("fopen");
return -1;
}
char buf[] = {"hello world"};
fwrite(buf, 1, sizeof(buf), fp); //往fp流中写入buf中的内容
int ret = fseek(fp, -5, SEEK_CUR); //光标从当前位置往前偏移五个位移量
char buffer[] = {"kun"};
fwrite(buffer, 1, sizeof(buffer), fp); //往fp流中写入buffer中的内容
}
4.4练习
1:用标准IO计算文件中有多少行,并打印出来:
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
if(argc != 2){
printf("文件数应该等于1");
return -1;
}
FILE *fp = fopen(argv[1], "r"); //以只读的方式打开所给文件
if(fp == NULL){
perror("fopen");
return -1;
}
char buf[64] = {0};
int count = 0;
char *p;
while(1){
p = fgets(buf, sizeof(buf), fp); //循环往buf中写入fp流中的内容
if(p == NULL)
break;
if(buf[strlen(buf)-1] == '\n') //计算行数
count++;
}
printf("line = %d\n", count);
}
2:用标准IO计算文件字节大小,并打印出来:
#include <stdio.h>
int main(int argc, char *argv[])
{
if(argc != 2){
printf("文件数应该等于1");
return -1;
}
FILE *fp = fopen(argv[1], "r");
if(fp == NULL){
perror("fopen");
return -1;
}
int size = 0;
char buf[20];
int ret;
while(1){
ret = fread(buf, 1, 20, fp);
if(ret == 0)
break;
size += ret;
}
printf("size = %d\n", size);
}
5.文件 IO
5.1打开文件
int open(const char *pathname, int flags, mode_t mode);
头文件:#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h> //使用其中之一就可以
功能:打开文件或者创建并打开文件;
返回值:成功返回一个文件描述符,失败返回-1; //文件描述符总是最小的、未使用的非负整数,文件描述符用于其他系统调用操作这个文件
pathname:要打开的文件名(包括路径);
flags:打开文件的方式;
必须要有的{
O_RDONLY; //只读
O_WRONLY; //只写
O_RDWR; //读写
} //三个必须有一个,要使用其他需在man手册里面查
O_APPEND; //追加
O_CREAT; //文件不存在则创建
O_EXCL; //与O_CREAT一起使用时,如果文件存在则直接报错
O_TRUNC; //文件存在则删除
mode:如果写O_CREAT,那么mode有机会生效,表示创建文件时的权限0777 0664 //权限会用~umask和mode相与,最总的数值用于权限设置,即mode & ~umask ==> 文件真实的权限
使用方式:
#include <stdio.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
int fd = open("xxxx", O_RDONLY | O_CREAT, 0777);
if(fd < 0){
perror("open");
return -1;
}
printf("open success, fd = %d\n", fd);
}
5.2读文件
ssize_t read(int fd, void *buf, size_t count);
头文件:#include <unistd.h>
功能:从文件描述符表示的文件中读取内容;
返回值:成功返回实际读到的字节数,失败返回-1;
fd:要读的文件描述符;
buf:存放读取到的内容的内存空间地址;
count:预计要读的字节数(不能超过buf的大小);
5.3写文件
ssize_t write(int fd, const void *buf, size_t count);
头文件:#include <unistd.h>
功能:往文件描述符表示的文件中写入内容;
返回值:成功返回实际写入的字节数,失败返回-1;
fd:要写的文件描述符;
buf:存放写入到的内容的内存空间地址;
count:预计要写的字节数(不能超过buf的大小);
5.4关闭文件
int close(int fd);
头文件:#include <unistd.h>;
功能:关闭文件;
返回值:成功返回0,失败返回-1;
fd:要关闭的文件描述符;
6.时间结构体
time_t time(time_t *tloc);
头文件:#include <time.h>
功能:获取当前系统距离1970-01-01 00:00:00所过的秒数;
返回值:成功返回记录的秒数,失败返回-1;
tloc:如果为NULL则无意义,如果不为NULL,那么系统时间会存放在tloc所指向的空间里面;
转换时间格式:
char *ctime(const time_t *timep);
直接将time_t的秒数转换成当前系统的时间,格式固定;
struct tm *localtime(const time_t *timep);
功能:也是转换秒数;
返回值:成功返回转换后的结构体地址,失败NULL;
struct tm {
int tm_sec; /* Seconds (0-60) */
int tm_min; /* Minutes (0-59) */
int tm_hour; /* Hours (0-23) */
int tm_mday; /* Day of the month (1-31) */
int tm_mon; /* Month (0-11) */
int tm_year; /* Year - 1900 */
int tm_wday; /* Day of the week (0-6, Sunday = 0) */
int tm_yday; /* Day in the year (0-365, 1 Jan = 0) */
int tm_isdst; /* Daylight saving time */
};
使用方式:
#include <stdio.h>
#include <time.h>
int main(int argc, char *argv[])
{
//time_t tm = time(NULL);
time_t tm;
time(&tm);
printf("%s\n", ctime(&tm)); //以函数自有的格式显示时间
struct tm *4tp;
tp = localtime(&tm);
char buf[64] = {0};
sprintf(buf, "%d-%02d-%02d %2d:%2d:%2d", tp->tm_year+1900,tp->tm_mon+1, tp->tm_mday, tp->tm_hourtp->tm_min, tp->tm_sec); //年要加1900,月要加1
printf("data : %s\n", buf);
}
练习
以每秒一次的方式,将时间写入文件当中:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int fd = open("log.txt", O_CREAT | O_RDWR, 0777);
if(fd < 0){
perror("open");
return -1;
}
char buf[64];
time_t tim;
while(1){
time(&tim);
struct tm *tp = localtime(&tim);
memset(buf, 0, sizeof(buf)); //清空垃圾字符
sprintf(buf, "%d-%02d-%02d %02d:%02d:%02d\n", tp->tm_year+1900,tp->tm_mon+1, tp->tm_mday, tp->tm_hour,tp->tm_min,tp->tm_sec);
int len = strlen(buf);
write(fd, buf, len);
sleep(1);
}
}
7.文件夹操作
7.1打开目录
DIR *opendir(const char *name);
头文件:#include <sys/types.h>
#include <dirent.h> //使用其中之一就可以
功能:站在内核的角度打开一个文件夹;
返回值:成功返回目录的流指针,失败返回NULL;
name:目录名;
7.2读目录
struct dirent *readdir(DIR *dirp);
头文件:#include <dirent.h>
功能:读一次目录里的文件;
返回值:成功返回某个文件的结构体指针,失败返回NULL;
struct dirent {
ino_t d_ino; /* Inode number */
off_t d_off; /* Not an offset; see below */
unsigned short d_reclen; /* Length of this record */
unsigned char d_type; /* Type of file; not supported
by all filesystem types */
char d_name[256]; /* Null-terminated filename */
};
dirp:目录流;
7.3练习
写一个能实习ls
的程序:
#include <stdio.h>
#include <dirent.h>
int main(int argc, char *argv[])
{
DIR *dir = opendir(".");
if(dir == NULL){
perror("opendir");
return -1;
}
struct dirent *dirp;
while(1){
dirp = readdir(dir);
if(dirp == NULL)
break;
printf("%s\t", dirp->d_name);
}
puts("");
}
8.文件属性
int stat(const char *pathname, struct stat *statbuf);
头文件:#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
功能:获取文件属性;
返回值:成功返回0,失败返回-1;
struct stat {
dev_t st_dev; /* ID of device containing file */
ino_t st_ino; /* Inode number */
mode_t st_mode; /* File type and mode */
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; /* Block size for filesystem I/O */
blkcnt_t st_blocks; /* Number of 512B blocks allocated */
/* Since Linux 2.6, the kernel supports nanosecond
precision for the following timestamp fields.
For the details before Linux 2.6, see NOTES. */
struct timespec st_atim; /* Time of last access */
struct timespec st_mtim; /* Time of last modification */
struct timespec st_ctim; /* Time of last status change */
#define st_atime st_atim.tv_sec /* Backward compatibility */
#define st_mtime st_mtim.tv_sec
#define st_ctime st_ctim.tv_sec
};
文件类型:
S_IFMT 0170000 bit mask for the file type bit field
S_IFSOCK 0140000 socket
S_IFLNK 0120000 symbolic link
S_IFREG 0100000 regular file
S_IFBLK 0060000 block device
S_IFDIR 0040000 directory
S_IFCHR 0020000 character device
S_IFIFO 0010000 FIFO
函数:
S_ISREG(m) is it a regular file?
S_ISDIR(m) directory?
S_ISCHR(m) character device?
S_ISBLK(m) block device?
S_ISFIFO(m) FIFO (named pipe)?
S_ISLNK(m) symbolic link? (Not in POSIX.1-1996.)
S_ISSOCK(m) socket? (Not in POSIX.1-1996.)
文件权限:
S_ISUID 04000 set-user-ID bit (see execve(2))
S_ISGID 02000 set-group-ID bit (see below)
S_ISVTX 01000 sticky bit (see below)
S_IRWXU 00700 owner has read, write, and execute permission
S_IRUSR 00400 owner has read permission
S_IWUSR 00200 owner has write permission
S_IXUSR 00100 owner has execute permission
S_IRWXG 00070 group has read, write, and execute permission
S_IRGRP 00040 group has read permission
S_IWGRP 00020 group has write permission
S_IXGRP 00010 group has execute permission
S_IRWXO 00007 others (not in group) have read, write, and execute permission
S_IROTH 00004 others have read permission
S_IWOTH 00002 others have write permission
S_IXOTH 00001 others have execute permission
使用方式::
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
struct stat buf;
int ret = stat("888",&buf);
if(ret < 0){
perror("stat");
return -1;
}
printf("inode:%ld\n",buf.st_ino);
printf("mode:%d\n",buf.st_mode);
if((buf.st_mode & S_IFREG) != 0)
printf("The file is refular file.\n");
if(!(buf.st_mode & S_IFIFO))
printf("The file is not FIFO file.\n");
if(S_ISREG(buf.st_mode) != 0)
printf("The file is refular file.\n");
if((buf.st_mode & S_IROTH) != 0)
printf("Others have read permission.\n");
if(!(buf.st_mode & S_IWOTH))
printf("Others have not write permission.\n");
return 0;
}
练习
使用stat
函数完成Linux终端ll
功能。
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <dirent.h>
#include <time.h>
int main()
{
DIR *fpdir = opendir(".");
if(fpdir == NULL){
perror("opendir");
return -1;
}
struct dirent *dp;
struct stat sbuf;
while( (dp = readdir(fpdir)) != NULL ){
stat(dp->d_name, &sbuf);
//if(S_ISREG(sbuf.st_mode))
// printf("-");
switch(sbuf.st_mode & 0770000){
case S_IFSOCK : printf("s"); break;
case S_IFLNK : printf("l"); break;
case S_IFREG : printf("-"); break;
case S_IFBLK : printf("b"); break;
case S_IFDIR : printf("d"); break;
case S_IFCHR : printf("c"); break;
case S_IFIFO : printf("p"); break;
default : break;
}
//user read
if((sbuf.st_mode & S_IRUSR) == 0)
printf("-");
else
printf("r");
//user write
if((sbuf.st_mode & S_IWUSR) == 0)
printf("-");
else
printf("w");
//user exe
if((sbuf.st_mode & S_IXUSR) == 0)
printf("-");
else
printf("x");
//group read
if((sbuf.st_mode & S_IRGRP) == 0)
printf("-");
else
printf("r");
//group write
if((sbuf.st_mode & S_IWGRP) == 0)
printf("-");
else
printf("w");
//group exe
if((sbuf.st_mode & S_IXGRP) == 0)
printf("-");
else
printf("x");
//other read
if((sbuf.st_mode & S_IROTH) == 0)
printf("-");
else
printf("r");
//other write
if((sbuf.st_mode & S_IWOTH) == 0)
printf("-");
else
printf("w");
//other exe
if((sbuf.st_mode & S_IXOTH) == 0)
printf("-");
else
printf("x");
//link number
printf(" %ld", sbuf.st_nlink);
#if 1
//user id
switch(sbuf.st_uid){
case 0 : printf(" root"); break;
case 1000 : printf(" hqyj"); break;
default : break;
}
//group id
switch(sbuf.st_gid){
case 0 : printf(" root"); break;
case 1000 : printf(" hqyj"); break;
default : break;
}
#endif
printf(" %5ld", sbuf.st_size);
struct tm *tp = localtime(&sbuf.st_mtime);
printf(" %2d %2d %2d:%02d %d", tp->tm_mon+1, tp->tm_mday,
tp->tm_hour, tp->tm_min, tp->tm_year+1900);
printf(" %s", dp->d_name);
printf("\n");
}
}
9.库
静态库:在程序编译时所有的库表示的功能都会被编译进整个程序,体积会很大,要占用很多资源。之后的运行不用过多去在意运行环境,移植性特别好;
动态库:在程序编译的时候不加载库的内容进程序,只会在程序使用这个库的地方添加一个标记,这些库文件在程序运行时才会加载。体积小,但是过度依赖运行环境;
静态库编译步骤:
1.gcc -c kunkun.c -o kunkun.o
2.ar crs libkunkun.a kunkun.o //静态库的全面需要以lib开头,以.a结尾;
3.gcc main.c -L. -lkunkun //编译程序时连接静态库,-L指定库文件的路径;
动态库编译步骤:
1.gcc -fPIC -Wall -c kunkun.c //编译与地址无关的程序,让动态库可以识别到标记;
2.gcc -shared kunkun.o -o libkunkun.so //生成动态库文件,动态库也需要lib开头,以.so结尾;
注:静态库和动态库可以重名,在编译主程序时连接库默认会连接动态库,加上-static才会指定连接静态库;