PILE读书笔记_标准I/O
在学习和分析标准I/O库的同时, 可以重点与Linux的I/O系统调用进行比较。
stdin、 stdout和stderr都是FILE类型的文件指针, 是由C库静态定义的, 直接与文件描述符0、 1和2相关联, 所以应用程序可以直接使用它们。其中,stdin是不可写的, stdout是不可读的, 而stderr不仅不可读, 且没有缓存。
I/O的缓存
C库的I/O接口对文件I/O进行了封装, 为了提高性能, 其引入了缓存机制, 共有三种缓存机制: 全缓存、 行缓存及无缓存。
(1)全缓存一般用于访问真正的磁盘文件。 C库会为文件访问申请一块内存, 只有当文件内容将缓存填满或执行冲刷函数flush时, C库才会将缓存内容写入内核中。
(2)行缓存一般用于访问终端。 当遇到一个换行符时, 就会引发真正的I/O操作。 需要注意的是, C库的行缓存也是固定大小的。 因此, 当缓存已满, 即使没有换行符时也会引发I/O操作。
(3)无缓存, 顾名思义, C库没有进行任何的缓存。 任何C库的I/O调用都会引发实际的I/O操作。
下面看一个行缓存的例子:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
printf("Hello ");
if ( == fork()) {
printf("child\n");
return ;
}
printf("parent\n");
return ;
}
执行结果如下:
之所以是这样的结果, 就是因为背后的行缓存。 执行printf( "Hello") 时, 因为printf是向标准输出打印的, 因此使用的是行缓存。 字符串Hello没有换行符, 所以并没有真正的I/O输出。 当执行fork时,子进程会完全复制父进程的内存空间, 因此字符串Hello也存在于子进程的行缓存中。 故而最后的输出结果中, 无论是父进程还是子进程都有Hello字符串。
fopen函数
# include<stdio.h>
FILE * fopen(const char * path,const char * mode)
参数说明:
(1)path:需要打开的文件路径
(2)mode:文件打开方式,主要对应下表
mode | function |
---|---|
r | 以只读方式打开文件,该文件必须存在。 |
r+ | 以可读写方式打开文件,该文件必须存在。 |
rb+ | 读写打开一个二进制文件,允许读数据。 |
rt+ | 读写打开一个文本文件,允许读和写。 |
w | 打开只写文件,若文件存在则文件长度清为0,即该文件内容会消失。若文件不存在则建立该文件。 |
w+ | 打开可读写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。 |
a | 以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。(EOF符保留) |
a+ | 以附加方式打开可读写的文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾后,即文件原先的内容会被保留。 (原来的EOF符不保留) |
wb | 只写打开或新建一个二进制文件;只允许写数据。 |
wb+ | 读写打开或建立一个二进制文件,允许读和写。 |
wt+ | 读写打开或着建立一个文本文件;允许读写。 |
at+ | 读写打开一个文本文件,允许读或在文本末追加数据 |
ab+ | 读写打开一个二进制文件,允许读或在文件末追加数据。 |
fdopen函数
#include <stdio.h>
FILE *fdopen(int fd, const char *mode);
fdopen用于从文件描述符fd生成一个文件流FILE
fileno函数
#include <stdio.h>
int fileno(FILE *stream);
fileno则用于从文件流FILE得到对应的文件描述符
无论是fdopen还是fileno, 关闭文件时, 都要使用fclose来关闭文件, 而不是用close。 因为只有采用此方式, fclose作为C库函数, 才会释放文件流FILE占用的内存。
fread函数
size_t fread(void *buffer, size_t size, size_t count, FILE *file);
参数说明:
(1)buffer是读取数据后存放地址
(2)size是的块长度
(3)count是块的数量,实际读取长度为size*count,返回值为块成功读取块的count数量
fwrite函数
size_t fwrite(const void *buffer, size_t size, size_t count, FILE *file);
参数说明:
(1)buffer是写入数据后存放地址
(2)size是的块长度
(3)count是块的数量,实际读取长度为size*count,返回值为块成功写入块的count数量
下面看一个读写文件的例子:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char buf[];
int ret;
FILE *fp = fopen("./tmp.txt", "w+");
if (!fp) {
printf("Fail to open file\n");
return -;
}
ret = fwrite("", sizeof(""), , fp);
printf("we write %d member\n", ret);
memset(buf, , sizeof(buf));
ret = fread(buf, , , fp);
printf("We read %s, ret is %d\n", buf, ret);
fwrite("", sizeof(""), , fp);
fclose(fp);
return ;
}
运行结果:
为什么fread什么都没有读取到, 返回值是0呢? 因为每一次系统调用的read和write成功返回后, 文件的偏移量都会被更新 。fwrite和fread操作的是同一个文件指针fp, 也就是对应的是同一个文件描述符。 第一次fwrite后, 在tmp.txt中写入了字符串“123”, 同时文件偏移为3, 也就是到了文件尾。 进行fread操作时, 既然操作的是同一个文件描述符, 自然会共享同一个文件偏移, 那么, 从文件尾自然读取不到任何数据。
ferror函数
int ferror(FILE *stream);
若文件流出错则返回非0,否则返回0。ferror()函数常与clearerr()函数一起使用,如果ferror(fp)发现错误,返回一个非0值,那么调用clearerr(fp)后,ferror(fp)的值变为0。
当文件流读到文件尾时, 文件流会被设置上EOF标志。 即使不使用clearerr清除EOF标志, 有新的数据, 也可以读取成功,这是因为:文件流FILE的错误标志位只有在打开IO_DEBUG的情况下才会对后面的I/O调用产生影响: 在有错误标志位的时候, 后面的I/O调用都会直接返回EOF。 而一般情况下, IO_DEBUG这个宏是没有定义的。
getc函数
#include <stdio.h>
int fgetc(FILE *stream);
int getc(FILE *stream);
正常的情况下返回一个char,可以吗?
不可以,这些函数返回的字符是以unsigned char形式保存,然后转换为int的。因此,在int的值域里,有效的返回值总是一个正整数。例如,遇到文件结束时,返回宏EOF。通常EOF定义成-1,在int域里,-1是没有歧义的,我们不会迷惑到底是读进来了一个字符'0xff',还是遇到了EOF。而在char域里,这样的歧义是存在的。
PILE读书笔记_标准I/O的更多相关文章
- PILE读书笔记_文件I/O
open函数 int open(const char *pathname, int flags, mode_t mode); 参数说明: (1)pathname: 表示要打开的文件路径 (2)flag ...
- PILE读书笔记_进程环境
进程是操作系统运行程序的一个实例, 也是操作系统分配资源的单位. 在Linux环境中, 每个进程都有独立的进程空间, 以便对不同的进程进行隔离, 使之不会互相影响. atexit函数 #include ...
- PILE读书笔记_基础知识
程序的构成 Linux下二进制可执行程序的格式一般为ELF格式. 我们可以用readelf命令来读取二进制的信息. ELF文件的主要内容就是由各个section及symbol表组成的. 下面来分别介绍 ...
- 新标准C++程序设计读书笔记_继承和多态
简单继承的例子: #include <iostream> #include <string> using namespace std; class CStudent { pri ...
- 新标准C++程序设计读书笔记_运算符重载
形式 返回值类型 operator 运算符(形参表) { …… } 运算符重载 (1)运算符重载的实质是函数重载(2)可以重载为普通函数,也可以重载为成员函数 class Complex { publ ...
- 新标准C++程序设计读书笔记_类和对象
面向对象的程序设计方法 抽象:将某类客观事物共同特点(属性)归纳出来,形成一个数据结构(可以用多个变量描述事物的属性):将这类事物所能进行的行为也归纳出来,形成一个个函数,这些函数可以用来操作数据结构 ...
- C++ primer读书笔记 chapter3 标准库类型
除第二章介绍的是C++的基本类型,本章将大致介绍一下C++定义的内容丰富的抽象数据库类型标准库.着重介绍一下sting.vector和bitset. 3.2标准库string类型 1.string类型 ...
- 『TensorFlow』读书笔记_进阶卷积神经网络_分类cifar10_上
完整项目见:Github 完整项目中最终使用了ResNet进行分类,而卷积版本较本篇中结构为了提升训练效果也略有改动 本节主要介绍进阶的卷积神经网络设计相关,数据读入以及增强在下一节再与介绍 网络相关 ...
- 『TensorFlow』读书笔记_进阶卷积神经网络_分类cifar10_下
数据读取部分实现 文中采用了tensorflow的从文件直接读取数据的方式,逻辑流程如下, 实现如下, # Author : Hellcat # Time : 2017/12/9 import os ...
随机推荐
- HDU 6017 Girls Love 233(多态继承DP)
[题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=6017 [题目大意] 给出一个只包含2和3的串,你可以花费两个智力值交换相邻的两个字符 问在智力值不 ...
- 【分块】bzoj2957 楼房重建
http://www.cnblogs.com/wmrv587/p/3843681.html ORZ 分块大爷.思路很神奇也很清晰. 把 块内最值 和 块内有序 两种良好的性质结合起来,非常棒地解决了这 ...
- 【计算几何】【极角序】【前缀和】bzoj1132 [POI2008]Tro
把点按纵坐标排序,依次枚举,把它作为原点,然后把之后的点极角排序,把叉积的公式稍微化简一下,处理个后缀和统计答案. #include<cstdio> #include<cmath&g ...
- Mysql主要索引方式:FULLTEXT,HASH,BTREE,RTREE。
使用方式 CREATE TABLE `user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `username` varchar(50) NOT NULL ...
- Java下String和List<String>的互相转换
// List转换为String数组 List<String> list = new ArrayList<String>(); list.add("a1") ...
- Java二进制指令代码解析
http://www.blogjava.net/DLevin/archive/2011/09/13/358497.html http://blog.csdn.net/sum_rain/article/ ...
- Mysql -- Can’t connect to local MySQL server through socket ‘/var/lib/mysql/mysql.sock’解决方法
启动mysql 报错: ERROR 2002 (HY000): Can’t connect to local MySQL server through socket ‘/var/lib/mysql/m ...
- Golang 内存热力图
https://cizixs.com/2017/09/11/profiling-golang-program/
- Python测试Kafka集群(kafka-python)
生产者代码: import time from kafka import SimpleProducer, KafkaClient from kafka import KafkaProducer pro ...
- ActiveMQ实现负载均衡+高可用部署方案 -转载
转:http://www.open-open.com/lib/view/open1400126457817.html 一.架构和技术介绍 1.简介 ActiveMQ 是Apache出品,最流行的,能力 ...