知识点链接
https://www.yuque.com/aihenaobaijin/camuoq/lscmvf6z1arklau4?singleDoc# 《IO进程》
函数接口
打开文件fopen
man 3 fopen
FILE *fopen(constchar*path,constchar*mode);
功能:打开文件
参数:
path:打开的文件路径
mode:打开的方式
r:只读,当文件不存在时报错,文件流定位到文件开头
r+:可读可写,当文件不存在时报错,文件流定位到文件开头
w:只写,文件不存在创建,存在则清空
w+:可读可写,文件不存在创建,存在则清空
a:追加(在末尾写),文件不存在创建,存在追加,文件流定位到文件末尾
a+:读和追加,文件不存在创建,存在追加,读文件流定位到文件开头,写文件流定位到文件末尾
注:当a+的方式打开文件时,写只能在末尾进行追加,定位操作是无法改变写的位置,但是可以改变读的位置
返回值:
成功:文件流
失败:NULL,并且会设置错误码
补充:
perror ( )用来将上一个函数发生错误的原因输出到标准设备 (stderr) 。参数 s 所指的字符串会先打印出, 后面再加上错误原因字符串。此错误原因依照全局变量errno 的值来决定要输出的字符串。在库函数中有个errno变量,每个errno值对应着以字符串表示的错误类型。当你调用"某些"函数出错时,该函数已经重新设置了errno的值。perror函数只是将你设置的一些信息和现在的errno所对应的错误一起输出。
关闭文件fclose
int fclose(FILE* stream);
功能:关闭文件
参数:stream:文件流
文件读写操作
读一个字符:fgetc()
int fgetc(FILE * stream);
功能:从文件中读取一个字符,并将当前文件指针位置向后移动一个字符
参数:stream:文件流
返回值:成功:读到的字符
失败或读到文件末尾:EOF(-1)
写一个字符:fputc()
int fputc(int c, FILE * stream);
功能:向文件中写入一个字符, 成功写入后文件指针会自动向后移动一个字节位置
参数:c:要写的字符
stream:文件流
返回值:成功:写的字符的ASCII
失败:EOF(-1)
读一个字符串:fgets()
char *fgets(char *s,int size, FILE *stream);
功能:从文件中每次读取一行字符串
参数: s:存放字符串的地址
size:期望一次读取的字符个数
stream:文件流
返回值:成功:s的地址
失败或读到文件末尾:NULL
特性: 每次实际读取的字符个数为size-1个,会在末尾自动添加\0
每次读一行,遇到\n或者到达文件末尾后不再继续读下一行
并把它存储在s所指向的字符串内
写一个字符串:fputs()
int fputs(const char *s, FILE *stream);
功能:向文件中写字符串
参数:s:要写的内容
stream:文件流
返回值:成功:非负整数
失败:EOF
feof和ferror
int feof(FILE * stream);
功能:判断文件有没有到结尾,也就是当前所在位置后面还有没有字符
返回:如果到达文件末尾,返回非零值。如果后面还有字符则返回0
int ferror(FILE * stream);
功能:检测文件有没有出错
返回:文件出错,返回非零值。如果没有出错,返回0。
二进制读写 fread()和fwrite()
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:从文件流读取多个元素(将二进制数据从文件读出)
参数: ptr :是一个指针,是存放数据的存储空间的起始地址,用来存放读取元素
size :元素大小 sizeof(元素数据类型)
nmemb :读取元素的个数
stream :要读取的文件流
返回值:成功:读取的元素的个数
读到文件尾或失败: 0
size_t fwrite(const void*ptr, size_t size, size_t nmemb,FILE *stream);
功能:将二进制数据写入文件
参数: ptr :是一个指针,保存要输出数据的空间的地址
size :要写入的字节数 sizeof(数据类型)
nmemb : 要进行写入元素的个数
strem: 目标文件流指针
返回值:成功:写的元素个数
失败 :-1
重定向流到文件freopen
FILE *freopen(const char *pathname,const char *mode, FILE* fp);
功能:将指定的文件流重定向到打开的文件中
参数:path:文件路径
mode:打开文件的方式(同fopen)
fp:文件流指针
返回值:成功:返回打开的文件流指针
失败:NULL
文件定位操作
void rewind(FILE *stream);
功能:将文件位置指针定位到起始位置
参数:stream:文件流
int fseek(FILE *stream,long offset,int whence);
功能:文件的定位操作
参数:stream:文件流
offset:偏移量:正数表示向后文件尾部偏移,负数表示向文件开头偏移
whence:相对位置:
SEEK_SET:相对于文件开头
SEEK_CUR:相对于文件当前位置
SEEK_END:相对于文件末尾
返回值:成功:0
失败:-1
注:当打开文件的方式为a或a+时,fseek不起作用
补充:其中SEEK_SET,SEEK_CUR和SEEK_END和依次为0,1和2
fseek(fp,100,0);把fp指针移动到离文件开头100字节处.
fseek(fp,100,1);把fp指针移动到离文件当前位置100字节处
ffseek(fp,-100,2);把fp指针退回到离文件结尾100字节处
long ftell(FILE *stream);
功能:获取当前的文件位置
参数:要检测的文件流
返回值:成功:当前的文件位置
失败:-1
编程练习
练习1:通过fgetc和fputc实现"cat 文件名"功能(将文件内容在终端显示)
#include <stdio.h>
/* cat 文件名:查看文件内容,显示到终端。
步骤:
1.打开文件 fopen
2.循环用fgetc获取文件内容
3.当读到文件末尾标志EOF时结束
4.将读取文件内容用fputc打印到终端
5.关闭文件
*/
int main(int argc, char const *argv[])
{
if (argc != 2)
{
printf("err:%s<filename>\n", argv[0]);//提示正确格式
return -1;
}
//1.打开文件
FILE *fp = fopen(argv[1],"r");
if (NULL == fp)
{
printf("failed\n");
}
else
{
printf("seccess\n");
}
//2.循环用fgetc读文件,只要读到就fputc打印到终端
char ch;
while ((ch = fgetc(fp) )!= EOF)
{
fputc(ch, stdout);//将读到字符打印到终端
}
//3.关闭文件
fclose(fp);
return 0;
}
练习2:通过fgets实现"wc -l 文件名"命令功能(计算文件行数)
#include <stdio.h>
#include <string.h>
int main(int argc, char const *argv[])
{
if (argc != 2)
{
printf("err:%s <filename>\n", argv[0]);
return -1;
}
FILE *fp = fopen(argv[1], "r");
if (NULL == fp)
printf("fopen failed\n");
else
printf("fopen seccess\n");
char str[100];
int len = 0;
//循环fgets读文件, 只要读到就判断是否有\n, 如果有就累加行数
while (fgets(str, 100, fp))
{
fputs(str, stdout);
if (str[strlen(str) - 1] == '\n')
len++;
}
printf("len=%d\n", len);//wc -l会少一行,因为最后一行没有换行
fclose(fp);
return 0;
}
练习3: 实现“head -n 文件名”命令的功能
atoi功能: "123" -> 123(整型)
#include <stdio.h>
#include <stdlib.h>
#define N 32
int main(int argc, char const *argv[])
{
if (argc != 3)
{
printf("usage:%s <line><filename>\n", argv[0]);
return -1;
}
FILE *fp = fopen(argv[2], "r");
if (NULL == fp)
{
perror("open error");
return -1;
}
char buf[N];
int line = atoi(argv[1] + 1);
while (fgets(buf, N, fp) != NULL && line)
{
fputs(buf, stdout);
line--;
}
fclose(fp);
return 0;
}
题目要求:编程读写一个文件test.txt,每隔1秒向文件中写入一行录入时间的数据,类似这样:
1、2007-7-30 15:16:42
2、2007-7-30 15:16:43
该程序应该无限循环,直到按Ctrl-C中断程序
再次启动程序写文件时可以追加到原文件之后,并且序号能够接续上次的序号,比如:
1、2007-7-30 15:16:42
2、2007-7-30 15:16:43
3、2007-7-30 15:19:02
4、2007-7-30 15:19:03
5、2007-7-30 15:19:04
整体思路:
1. 打开文件fopen,循环往文件写内容
2. 每隔1s写入一行,sleep(1);
3. 计算文件行数,wc -l
4. 计算当前时间,转换成年月日、时分秒,time,localtime
man 2 time
time_t time(time_t *t);
如果t是空指针,直接返回当前时间。如果t不是空指针,返回当前时间的同时,将返回值赋予t指向的内存空间。
man 3 localtime
struct tm *localtime(const time_t *timep);
返回值是结构体指针,所指结构体封装着年月日时分秒
5. 字符串拼接函数:strcpy/strcat(dest, src)、sprintf、fprintf
fprintf:
格式化输出到流(stream)文件中,返回值是输出的字符数,发生错误时返回一个负值
int fprintf( FILE *stream, const char *format, ... );
sprintf:
格式化输出发送到buffer(缓冲区)中,返回值是写入的字符数量
int sprintf( char *buffer, const char *format, ... );
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
FILE *fp = fopen("time.txt", "a+");
if (NULL == fp)
printf("fopen failed\n");
else
printf("fopen success\n");
char str[100];
int len = 0;
while (fgets(str, 100, fp) != NULL)
{
if (str[strlen(str) - 1] == '\n')
len++;
}
time_t tm; // 获取系统当前时间,并保存在变量t中,存的是秒数
struct tm *ge; // struct tm*localtime(const time_t *timep)//参数:保存当前秒数的地址
// 返回值:tm结构体指针,转换的本地时间保存在指针指向的结构体中
while (1)
{
time(&tm);
ge = localtime(&tm);
// 输入到指定文件中
fprintf(fp, "%d:%d-%d-%d %d:%d:%d\n", ++len, ge->tm_year + 1900, ge->tm_mon + 1, ge->tm_mday, ge->tm_hour, ge->tm_min, ge->tm_sec);
fflush(fp);
sleep(1);
// 把当前时间给到一个字符串
sprintf(str, "%d:%d-%d-%d %d:%d:%d\n", len, ge->tm_year + 1900, ge->tm_mon + 1, ge->tm_mday, ge->tm_hour, ge->tm_min, ge->tm_sec);
// 终端打印字符串内容
fputs(str, stdout);
}
fclose(fp);
return 0;
}