摘要:
本篇是IO编程第二篇文章,主要讲述 IO编程中的时间编程基础以及文件IO编程基础。涉及到时间编程和文件IO的函数。文章中基本每个函数都举了一些简单的例子,希望能帮助大家理解。
目录
一,时间编程
概念
世界标准时间以格林威治时间为起点分为24个时区,从格林威治向东时间早,向西时间晚
在程序开发费时,可以获取标准时间(以秒为单位)
#include <time.h>
time_t time(time_t *tloc);
//参数 -----保存秒数的变量地址
//返回值 --- 成功:秒数, 失败:-1
秒数: 从1970-01-01 00:00:00 到 此时此刻的秒数
例如:
#include <stdio.h>
#include <time.h>
int main(int argc,char **argv)
{
time_t tm;
printf("%ld\n",time(&tm));
printf("tm = %ld\n",tm);
return 0;
}
将标准时间转换为字符串格式的时间
char *ctime(const time_t *timep);
//参数 ----- time()返回的秒数
//返回值 ----成功:字符串格式的时间,失败:NULL
例如:
int main(int argc,char **argv)
{
time_t tm;
//获取标准数据
time(&tm);
//将标准时间转为字符串
printf("%s",ctime(&tm));
return 0;
}
将标准时间转换为本地时间
struct tm *localtime(const time_t *timep);
//参数 -------- time()返回的秒数
//返回值---- 成功:结构体struct tm的指针 失败:NULL
struct tm
{
int tm_sec; /* Seconds. [0-60] (1 leap second) */
int tm_min; /* Minutes. [0-59] */
int tm_hour; /* Hours. [0-23] */
int tm_mday; /* Day. [1-31] */
int tm_mon; /* Month. [0-11] */
int tm_year; /* Year - 1900. */
int tm_wday; /* Day of week. [0-6] */
int tm_yday; /* Days in year.[0-365] */
int tm_isdst; /* DST. [-1/0/1]*/
};
例如:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define perr(str) ({ perror(str); exit(1);})
int main(int argc,char **argv)
{
time_t tm;
struct tm * tmp;
//获取标准数据
time(&tm);
tmp = localtime(&tm);
if(tmp == NULL){
perror("localtime");
exit(1);
}
printf("%4d-%02d-%02d\n",tmp->tm_year+1900,tmp->tm_mon+1,tmp->tm_mday);
printf("%02d:%02d:%02d\n",tmp->tm_hour,tmp->tm_min,tmp->tm_sec);
return 0;
}
将本地时间转换为字符串格式的时间
char *asctime(const struct tm *tm);
例如:
int main(int argc,char **argv)
{
time_t tm;
struct tm * tmp;
//获取标准数据
time(&tm);
printf("%s",asctime(tmp));
return 0;
}
二、文件IO
概念
文件描述符 fd
顺序分配的非负整数
内核用以标识一个特定进程正在访问的文件
其他资源(socket、pipe等)的访问标识
当运行程序时,系统会自动打开三个标准文件
文件指针 文件描述符
标准输入 stdin 0(STDIN_FILENO)
标准输出 stdout 1 (STDOUT_FILENO)
标准出错 stderr 2 (STDERR_FILENO)
程序(静态)
存放在磁盘文件中的可执行代码
通过exec函数族调用运行
进程(动态)
程序的一次执行过程
进程的标识(pid,ppid,…)
出错处理
全局错误码errno
在linux中,用于标记函数调用失败的信息,定义一个全局的变量errno,把它也称为全局错误码
在linux系统中,给全局错误码定义了以下一些错误值:
在errno.h中定义,全局可见
错误值定义为“EXXX”形式,如EACCESS
/usr/include/asm-generic/errno-base.h // 1-34个错误值
#define EPERM 1 /* Operation not permitted */
#define ENOENT 2 /* No such file or directory */
#define ESRCH 3 /* No such process */
#define EINTR 4 /* Interrupted system call */
#define EIO 5 /* I/O error */
#define ENXIO 6 /* No such device or address */
#define E2BIG 7 /* Argument list too long */
#define ENOEXEC 8 /* Exec format error */
#define EBADF 9 /* Bad file number */
#define ECHILD 10 /* No child processes */
.......
#define ERANGE 34 /* Math result not representable */
/usr/include/asm-generic/errno.h // 35 -- 133个错误值
#define EDEADLK 35 /* Resource deadlock would occur */
#define ENAMETOOLONG 36 /* File name too long */
#define ENOLCK 37 /* No record locks available */
....
#define EHWPOISON 133 /* Memory page has hardware error */
当程序中调用某个函数失败时,系统会修改errno的值,将错误值赋给errno
这是在程序中,可以通过读取errno的值,获取对应的错误信息,有两种方法:
方法一:调用perror()函数
void perror(const char *s);
//参数 ---- 字符串,表示错误信息的提示,调用失败的函数名
当调用某个函数失败时,系统会修改errno,此时调用perror()时,它会读取errno的值,打印出对应的错误信息
例如:
int main(int argc,char **argv)
{
FILE *fp;
if((fp = fopen("1.txt","r"))== NULL){
printf("errno = %d\n",errno);
perror("fopen");
exit(1);
}
return 0;
}
方法二:调用strerror()函数
#include <string.h>
char *strerror(int errnum);
//参数 --- 错误值
//返回值 ----成功:错误值对应的错误信息,失败:NULL
例如:
int main(int argc,char **argv)
{
#if 1
FILE *fp;
if((fp = fopen("1.txt","r"))== NULL){
printf("errno = %d\n",errno);
//perror("fopen");
fprintf(stderr,"fopen:%s\n",strerror(errno));
exit(1);
}
fprintf(stderr,"%s\n",strerror(200));
#else
int i;
for(i = 1; i <= 133;i++)
fprintf(stderr,"%d --->%s\n",i,strerror(i));
#endif
return 0;
}
文件IO操作文件的函数
打开文件、关闭文件
#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);
//参数1 ----- 要打开的文件
//参数2 ----- 打开的方式:
O_RDONLY:只读方式打开文件。
O_WRONLY:只写方式打开文件。
O_RDWR:读写方式打开文件。
//上面三个随便选一个 | 上下面的组合
O_CREAT:如果该文件不存在,就创建一个新的文件,并用第三的参数为其设置权限。
O_EXCL:如果使用O_CREAT时文件存在,则可返回错误消息。这一参数可测试文件是否存在。
O_NOCTTY:使用本参数时,如文件为终端,那么终端不可以作为调用open()系统调用的那个进程的控制终端。守护进程、后台进程、精灵进程。
O_TRUNC:如文件已经存在,并且以读写或只写成功打开,那么先全部删除文件中原有数据。
O_APPEND:以添加方式打开文件,在打开文件的同时,文件指针指向文件的末尾。
//参数3 ------ 文件权限,当参数2中有O_CREAT时,必须要通过参数3设置文件权限,权限 = 0666 & ~umask
//返回值 ---- 成功:最小且没有被使用的文件描述符,失败:-1
例如:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define perr(str) ({perror(str); exit(1);})
int main(int argc,char **argv)
{
int fd;
if(argc !=2){
fprintf(stderr,"Usage: %s <filename>\n",argv[0]);
exit(1);
}
//打开文件
if((fd = open(argv[1],O_WRONLY|O_CREAT|O_TRUNC,0666)) < 0)
//if((fd = open(argv[1],O_WRONLY)) < 0)
perr("open");
printf("%s打开成功!\n",argv[1]);
//关闭文件
close(fd);
return 0;
}
从文件读数据
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count); //以字节流方式读取数据
//参数1 ----- 文件描述符
//参数2 ----- 保存数据的空间地址
//参数3 ----- 空间的大小
//返回值 ----- 大于0,表示读到的字节数,等于0,表示读到文件末尾,-1,失败。
例如:
int main(int argc,char **argv)
{
int fd;
char buf[100];
int ret;
if(argc !=2){
fprintf(stderr,"Usage: %s <filename>\n",argv[0]);
exit(1);
}
if((fd = open(argv[1],O_RDONLY)) < 0)
perr("open");
while(1){
memset(buf,0,sizeof(buf));//将数组清0
if((ret= read(fd,buf,sizeof(buf))) < 0)
perr("read");
else if(!ret)
break;
else
printf("%s",buf);
}
close(fd);
return 0;
}
向文件中写数据
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
//参数1 ----- 文件描述符
//参数2 ----- 数据空间地址
//参数3 ----- 要写的数据长度
//返回值 ---- 成功:实际写的数据长度,失败: -1
例如: 封装write函数
void mywrite(int fd,char *buf,int count)
{
int ret=0;
while(1){
if((ret = write(fd,buf+ret,count)) < 0)
perr("write");
if(ret != count)
count = count - ret;
else
break;
}
}
设置文件位移量
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
//参数1 ----- 文件描述符
//参数2 ----- 相对于参数3的偏移量
//参数3 ----- 参数2的起始位置:
SEEK_SET ----- 文件开头
SEEK_CUR ----- 文件位移量的当前位置
SEEK_END ----- 文件末尾
//返回值 ---- 成功: 文件位移量,失败:-1
创建空洞文件: ------- //创建空洞文件时,不能以追加方式打开文件
实际就是先开辟一个能加载一定大小的文件出来,类似steam下载游戏时
步骤一: 修改文件位移量到指定的大小
步骤二: 在修改后的文件位移量位置写一个标签。
int main(int argc,char **argv)
{
int fd;
int n;
if(argc !=2){
fprintf(stderr,"Usage: %s <filename>\n",argv[0]);
exit(1);
}
if((fd = open(argv[1],O_WRONLY|O_CREAT,0666)) < 0)
perr("open");
printf("请输入文件的大小:");
scanf("%d",&n);
//1,设置文件位移量
lseek(fd,n-3,SEEK_SET);
//2,写入一个标签
write(fd,"end",3);
close(fd);
return 0;
}
复制文件描述符
int dup(int oldfd);
//参数 --- 要复制的文件描述符
//返回值 ---成功:最小没有被使用的文件描述符,该文件描述符是oldfd的一个拷贝
例如:
int main(int argc,char **argv)
{
int fd,newfd;
char buf[100];
if(argc !=2){
fprintf(stderr,"Usage: %s <filename>\n",argv[0]);
exit(1);
}
if((fd = open(argv[1],O_WRONLY|O_CREAT|O_TRUNC,0666)) < 0)
perr("open");
printf("请输入一个字符串:");
fgets(buf,sizeof(buf)-1,stdin);
write(fd,buf,strlen(buf));
//复制文件描述符fd,返回新的文件描述符,该文件描述符是fd的一个拷贝
newfd = dup(fd);//相当于newfd指向oldfd这个文件,因此写入时oldfd会多一行内容
printf("请输入一个字符串:");
fgets(buf,sizeof(buf)-1,stdin);
if(write(newfd,buf,strlen(buf)) < 0)
perr("write");
close(fd);
return 0;
}
文件描述符的重定向
int dup2(int oldfd, int newfd); //把newfd重定向到oldfd,在重定向之前,会关闭newfd对应的文件
//参数1 ---- 目标文件描述符
//参数2 ---- 需要重定向的文件描述符
//返回值 ----- 成功:返回重定向后的文件描述符,失败:-1
例如:
int main(int argc,char **argv)
{
int fd,newfd;
char buf[100];
文件描述符的重定向
int dup2(int oldfd, int newfd); //把newfd重定向到oldfd,在重定向之前,会关闭newfd对应的文件
//参数1 ---- 目标文件描述符
//参数2 ---- 需要重定向的文件描述符
//返回值 ----- 成功:返回重定向后的文件描述符,失败:-1
例如:
int main(int argc,char **argv)
{
int fd,newfd;
char buf[100];
if((fd = open("1.txt",O_WRONLY|O_CREAT|O_TRUNC,0666)) < 0)
perr("open");
if((newfd = open("2.txt",O_WRONLY|O_CREAT|O_TRUNC,0666)) < 0)
perr("open");
#if 0
//将标准输出重定向到文件中
dup2(fd,STDOUT_FILENO);
printf("hello world\n");
#else
write(fd,"hello",5);
dup2(fd,newfd); //把newfd重定向到fd,同时关闭newfd原来的文件
write(newfd,"world",5);
#endif
close(fd);
close(newfd);
return 0;
}