3.3.3 二进制读写 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
(1)针对终端读写
//针对终端
char buf[32] = "";
fread(buf, sizeof(char), 10, stdin);
printf("printf:%s\n", buf);
fwrite(buf, 1, 10, stdout);
-
- 针对文件读写
#include <stdio.h>
int main(int argc, char const *argv[])
{
FILE *fp;
float arr[3] = {1.23, 2.34, 4.56};
float data[3] = {0};
fp = fopen(argv[1], "r+");
if (fp == NULL)
{
perror("fopen err");
return -1;
}
fwrite(arr, sizeof(float), 3, fp);
//将位置指针定位到文件开头,不然上一步是写操作所以接着读的话会从末尾开始读从而什么都读不到
rewind(fp); //定位到文件开头
fread(data, 4, 3, fp);
printf("%f %f %f\n", data[0], data[1], data[2]);
return 0;
}
3.4 其他操作
3.4.1 重定向流到文件freopen
FILE * freopen(const char *pathname, const char *mode, FILE* fp);
功能:将指定的文件流重定向到打开的文件中
参数:path:文件路径
mode:打开文件的方式(同fopen)
fp:文件流指针
返回值:成功:返回打开的文件流指针
失败:NULL
#include <stdio.h>
int main(int argc, char const *argv[])
{
printf("hello\n");
//将标准输出流重定向到打开的test.txt文件中
freopen("test.txt", "w+", stdout);
printf("world\n");
//将标准输出重定向到终端文件
freopen("/dev/tty", "r+", stdout);
printf("6666666666666666\n");
return 0;
}
3.4.2 文件定位操作
void rewind(FILE *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字节处;
fseek(fp,-100,2);把fp指针退回到离文件结尾100字节处。
long ftell(FILE *stream);
功能:获取当前的文件位置
参数:要检测的文件流
返回值:成功:当前的文件位置,出错:-1
#include <stdio.h>
int main(int argc, char const *argv[])
{
FILE *fp = fopen("test.txt", "w+");
if (NULL == fp)
{
perror("fopen err");
return -1;
}
printf("fopen success\n");
//相当于开头位置,往后10个
fseek(fp, 10, 0);
fputc('a', fp);
//相对于当前位置,往后5个
fseek(fp, 5, 1);
fputs("hello", fp);
//相对于最后位置,往前1个
fseek(fp, -1, 2);
fputc('b', fp);
//计算文件位置
long l = ftell(fp);
printf("%ld\n", l);
return 0;
}
练习题:
- 相对一个文本文件的尾部追加写入,应当在fopen何中使用的文件操作方式指示符号为 () A.r B.wb C. a D.w+
- fseek(1, 2, 3); 这个函数是什么作用,三个参数分别是什么意思?
- 函数调用语句:fseek (fp,-10L,2);的含义是
A 将文件位置指针从文件未尾处向文件头的方向移动10个字节
B 将文件位置指针从当前位置向文件头的方向移动10个字节
C 将文件位置指针从当前位置向文件未尾方向移动10个字节
总结:
为什么用标准IO?
- 因为读写文件通常是大量的数据(相对于底层驱动的系统调用所实现的数据操作单位),这时,使用库函数可以大大减少系统调用的次数。
- 为了保证可移植性
关于缓存区: 库函数的缓冲区对于库函数,如果标准输出连到终端设备(直接输出到屏幕),则它是行缓冲的(遇到回车换行符或者是缓冲区满了才输出);否则(输出到文件)是全缓冲的(缓冲区填满或者是程序运行结束了才输出)。程序运行结束时,会刷新所有的缓冲区。
最后给大家留一个小小的练习题。我把思路和代码都附上了,大家做完可以参考一下。
编程读写一个文件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
思路:
- 打开文件fopen,循环往文件写内容
- 每隔1s写入一行,sleep(1);
- 计算文件行数,wc -l
- 计算当前时间,转换成年月日、时分秒,time,localtime
man 2 time
time_t time(time_t *t);
如果t是空指针,直接返回当前时间。如果t不是空指针,返回当前时间的同时,将返回值赋予t指向的内存空间。
man 3 localtime
struct tm *localtime(const time_t *timep);
返回值是结构体指针,所指结构体封装着年月日时分秒
- 字符串拼接函数: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 <time.h>
#include <string.h>
#include <unistd.h>
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], "a+");
if (NULL == fp)
{
perror("fopen err");
return -1;
}
//2. 计算行数
char buf[32] = "";
int len = 0;
while (fgets(buf, 32, fp) != NULL)
{
if (buf[strlen(buf) - 1] == '\n')
len++;
}
//3. 循环把时间戳写入文件,1s一行
time_t t;
struct tm *tm;
while (1)
{
time(&t);
tm = localtime(&t);
fprintf(fp, "%d, %d-%d-%d %d:%d:%d\n", ++len, tm->tm_year + 1900,
tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
sleep(1);
fflush(NULL);
}
return 0;
}
明天为大家更新文件IO的相关内容。