在工作中如果存在日志读取一定用标准IO,因为文件IO没法只读一行
ps:读文件用标准IO,读设备用文件IO,因为蓝牙等设置是实时的,不可能等缓冲区满了再输出,而且标准IO对某些设备可能不支持,而文件IO是底层的,更灵活。
以下三个函数可用于一次读一个字符
int getc(FILE * stream);
int fgetc(FILE *stream);
int getchar(void);
- 返回值:若成功则为一个字符,若已处文件尾端或出错则为EOF
- 函数getchar()等同于getc(stdin)
- 注意,不管是出错还是到达文件尾端,这三个函数都返回同样的值。为了区分这两种不同的情况,必须调用ferror或feof()
- getc()的实现是一个宏,而fgetc()是一个函数(用宏效率高一点但代码量比较大,函数开销要大写)
- 返回值为int类型
为什么get系列函数返回一个int而不是char?
因为返回值包括了有效数据和状态类数据,成功时返回有效数据字符char类型够用,但是出错时char不够返回状态类数据。(返回0-255是成功,-1是失败)
缓存文件按字符的读写
fgetc是从缓存区中获取一个字符,若缓存区里没数据,就会触发系统调用read从驱动中获取一块数据
- fgetc并没有引起内核驱动的频繁调用,性能瓶颈仅仅在用户空间调用的次数而已
#include <stdio.h>
#define NEW_FILE_NAME "/tmp/temp/tmp_new.txt"
/*
* fgetc是从缓存区中获取一个字符,若缓存区里没数据,就会触发系统调用read从驱动中获取一块数据
*
* fgetc并没有引起内核驱动的频繁调用,性能瓶颈仅仅在用户空间调用的次数而已
*
* 思考,为什么返回值是int类型而不是char类型
* */
int read_char_lesson(const char *filename) {
FILE *fp;
int cnt;
fp = fopen(filename, "r");
if (fp == NULL) {
perror("open");
return -1;
}
cnt = fgetc(fp);
while (cnt != EOF) {
printf("the cnt is %x\n", cnt);
getchar();
cnt = fgetc(fp);
}
return 0;
}
/*
* 案例:实现一个从标准输入获取内容,写到目标文件的程序
*
* 作业:用字符IO编写文件拷贝方法
* */
int write_lesson1(const char *filename) {
FILE *fp;
int buf;
fp = fopen(filename, "w");
if (fp == NULL) {
perror("fopen");
return -1;
}
printf("input: ");
buf = fgetc(stdin);
while (buf != EOF){
fputc((unsigned char)buf, fp);
buf = fgetc(stdin);
}
printf("user input exit...!\n");
fclose(fp);
return 0;
}
int main() {
mkdir("/tmp/temp",0777);
write_lesson1(NEW_FILE_NAME);
return 0;
}
在上述代码中,值得注意的是,EOF为一个宏定义,实际上是-1
#define EOF (-1)
表示 End of file
但是返回EOF时有两种情况,第一种是文件成功,但是文件结束了,另外一种情况是文件出错也返回EOF。因此,更安全的方式是使用feof()函数,来判断文件是否结束
按行进行读写:
ps:gets是危险函数,不能用
//删除标准输入的回车信号
buf[strlen(buf)-1]=0;
- fgets从标准缓存中读取数据,直到读到一个\n符号就会停止,若缓存区没有数据,则触发系统调用read
- 读取驱动层数据
- fgets是字符安全函数,而gets是绝对不要使用的函数
- fgets不仅约定了\n作为结束标志,也约定了最多读取N-1个符号,会在空间的最后一个字符中添加\0标志
#include <stdio.h>
#define NEW_FILE_NAME "/tmp/temp/tmp_new.txt"
/*
* fgets从标准缓存中读取数据,直到读到一个\n符号就会停止,若缓存区没有数据,则触发系统调用read
* 读取驱动层数据
* fgets是字符安全函数,而gets是绝对不要使用的函数
* fgets不仅约定了\n作为结束标志,也约定了最多读取N-1个符号,会在空间的最后一个字符中添加\0标志
* */
int read_lesson1(const char *filename) {
FILE *fp;
char buf[16];
fp = fopen(filename, "r");
if (fp == NULL) {
perror("open");
return -1;
}
while (fgets(buf, sizeof(buf)/ sizeof(buf[0]), fp) != NULL) {
printf("%s", buf);
}
fclose(fp);
return 0;
}
/*
* 思考题: fputs和puts有什么区别?
* */
int main() {
read_lesson1(NEW_FILE_NAME);
}
读写块数据(二进制流数据)
这里的void *ptr 表示没有指定类型,即任意空间
第一个案例:写入到指定文件中
也可以写成:
fwrite(a+1,sizeof(a[0]),4,fp);
从指定文件中读取:
int test2(){
FILE *fp;
double buf[4];
int i;
fp = fopen("/tmp/temp/a.txt","r");
memset(buf,0,sizeof(buf));
fread(buf,sizeof(double),4,fp);
for(i=0; i<4;i++){
printf("%f\t",buf[i]);
}
printf("\n");
fclose(fp);
}
- fwrite写入的是文件缓存区,当缓存区达到一定条件时,才会触发系统调用的write写入到驱动
- 对于文件来说,默认的触发条件就是缓存区满,而标准输出,则是换行符
- 对于C语言的字符串结束标志\0,可以写入文件,但是它是一个不可见字符
- fwrite返回值代表成功写入的元素个数,失败返回的是0
注:
Windows默认编码:GBK
Linux默认编码:UTF-8
标准IO和文件IO对比
- 缓存IO和非缓存IO
文件系统编程
Linux中有两种类型的链接:
硬链接
- 是利用Linux中为每个文件分配的物理编号——inode建立链接。因此,硬链接不能跨越文件系统。
软链接(符号链接)
- 是利用文件的路径名建立链接。通常建立软链接使用绝对路径而不是相对路径,以最大限度增加可移植性。
需要注意:
如果是修改硬链接的目标文件名,链接依然有效;如果修改软链接的目标文件名,则链接将断开;
对一个已存在的文件执行移动或删除操作,将导致指向它软链接的断开。硬链接仍然有效。