Linux I/O总结
文件流
标准I/O文件流可用于单字节或多字节字符集。流的定向决定了所读写的是单字节还是多字节。流在最初创建时,并没有定向,此时如果在为定向的流上使用多字节I/O函数,那么该流被设置为宽定向的;如果在为定向的流中使用单字节I/O函数,那么该流被设置为字节定向的。
如下两个函数可用于改变流的定向:
|
#include <stdio.h> #include <wchar.h> int fwide(FILE* fp, int mode); 返回值:流为宽定向,返回正值;流为字节定向,返回负值;流为定向,返回0 说明: fwide不改变已定向的流的定向。 mode > 0,则fwide试图将流设置为宽定向; mode < 0,则fwide试图将流设置为字节定向; mode = 0,则fwide不设置流的定向,而是返回标识当前流定向的值。 FILE* freopen(const char* restrict pathname, const char* restrict type, FILE* restrict fp); 说明: 在指定的流上打开一个指定的文件,如果该流已经打开,则先关闭该流;若该流已经定向,则清除该定向。该函数常用于将一个指定的文件打开为一个预定义的流:标准输入、标准输出、标准错误 |
缓冲
标准I/O提供缓冲的目的是尽可肯地减少read和write的次数,有3种类型:
|
类型 |
说明 |
|
全缓冲 |
在填满整个缓冲区后才进行实际的I/O操作。可以调用fflush函数将缓冲区的数据写入磁盘中。 |
|
行缓冲 |
当在输入和输出中遇到换行符或者缓冲区已满时,执行实际的I/O操作。 |
|
无缓冲 |
标准I/O不对字符进行缓冲存储。标准错误流stderr通常不带缓冲区,这就使得可以尽快显示出错信息。 |
对于一个给定的流,也可以使用以下函数更改系统默认的缓冲类型:
|
#include <stdio.h> void setbuf(FILE* restrict fp, char* restrict buf); int setvbuf(FILE* restrict fp, char* restrict buf, int mode, size_t size); 返回值:成功,0;失败,-1 说明: setbuf用于打开或关闭流缓冲机制,参数buf指向一个长度为BUFSIZ(该常量在<stdio.h>中定义)的缓冲区;如果要关闭缓冲,则将buf设置为NULL即可。 setvbuf用于精确地设置所需的缓冲类型,mode取值如下:_IOFBF(全缓冲)/_IOLBF(行缓冲)/_IONBF(无缓冲);如果指定了mode为带缓冲类型,而buf却为NULL,则系统会自动分配BUFSIZ个字节的缓冲区。 int fflush(FILE* fp); 说明: 强制冲刷一个流到磁盘中。如果fp为NULL,则系统中的所有输出流将被冲刷。 |
打开流
|
#include <stdio.h> FILE* fopen(const char* restrict pathname, const char* restrict type); FILE* freopen(const char* restrict pathname, const char* restrict type, FILE* restrict fp); FILE* fdopen(int fd, const char* type); 说明: fopen打开指定路径pathname的文件;freopen用于将一个指定文件打开为一个预定义的流:标准输入、标准输出、标准错误;fdopen根据fd返回一个打开的流,常用于由创建管道和网络通信函数返回的描述符。 type参数指定了对该I/O流的读写方式。 |
读写流
在打开流后,有3种不同类型的非格式化I/O(格式化I/O是诸如printf和scanf等的函数):
|
类型 |
说明 |
|
每次处理一个字符 |
#include <stdio.h> int getc(FILE* fp); int fgetc(FILE* fp); int getchar(void); 返回值:成功,返回下一个字符;失败或到达文件尾端,返回EOF 说明: getchar等价于getc(stdin)。 getc可以被实现为宏,而fgetc一定是个函数,因此fgetc调用的时间通常长于getc。 为了区分引起EOF的原因是到达文件尾端还是读入失败,引入如下三个函数: int ferror(FILE* fp); int feof(FILE* fp); void clearerr(FILE* fp); 返回值:条件为真,非0;否则,0 说明: 每个流在FILE对象中维护了两个标志:出错标志、文件结束标志,调用clearerr可以清除这两个标志。 有时在读一个输入流时,我们需要先查看下一个字符是否是需要读入的字符,此时需要利用ungetc函数将字符压回输入流中: int ungetc(int c, FILE* fp); 返回值:成功,返回c;失败,EOF 说明: 用ungetc压回字符,并非将其写到底层文件或设备中,只是将其写回标准I/O的缓冲区中。 int putc(int c, FILE* fp); int fputc(int c, FILE* fp); int putchar(int c); 返回值:成功,返回c;失败,EOF 说明: putchar(c)等价于putc(c, stdout)。 putc可被实现为宏,而fputc只能是一个函数。 |
|
每次处理一行 |
#include <stdio.h> char* fgets(char* restrict buf, int n, FILE* restrict fp); char* gets(char* buf); 返回值:成功,返回buf;失败,返回NULL 说明: gets从标准输入读,fgets从指定的文件读。 每次读取直至遇到换行符或者达到缓冲区长度n(由于缓冲区以null字节结尾,有效长度实则是n - 1),如果fgets读取的行超过n – 1个字节,那么返回前n – 1个字节,对fgets的下一次调用将继续读取该行其余部分。 gets由于不能指定缓冲区长度n,因此在最新的ISO C中已被忽略,因此不推荐使用gets函数。 int fputs(const char* restrict str, FILE* restrict fp); int puts(const char* str); 返回值:成功,返回非负值;失败,返回EOF 说明: fputs将一个以null字节终止的字符串写到指定的流中,null终止符不写出。 虽然,puts并没有太大的安全隐患,但还是避免使用它,因为puts每次写出数据后还附加写出一个换行符。 |
|
直接I/O或二进制I/O |
比如我们需要一次读写一个完整的结构,如果使用getc/putc,那么必须循环遍历整个结构,每次处理一个字节,非常麻烦;如果使用fgets/fputs,那么由于其遇到null字节就停止了,而在结构中可能含有null字节,因此也不能很好地工作。因此,提供了如下两个函数: #include <stdio.h> size_t fread(void* restrict ptr, size_t size, size_t nobj, FILE* restrict fp); size_t fwrite(const void* restrict ptr, size_t size, size_t nobj, FILE* restrict fp); 返回值:成功读写的结构个数 例如:将data数组的第2~5个元素(一共4个)写至一个文件上: float data[10]; if(fwrite(&data[2], sizeof(float), 4, fp) != 4) printf(“fwrite error”); |
示例程序:将标准输入复制到标准输出:
()每次处理一个字符(getc/putc)
[root@benxintuzi IO]# cat getc.c
#include <stdio.h> int main(void)
{
int c; while((c = getc(stdin)) != EOF)
if(putc(c, stdout) == EOF)
printf("output error\n"); if(ferror(stdin))
printf("input error\n"); return ;
} [root@benxintuzi IO]# ./getc
hello
hello
benxintuzi
benxintuzi
^C
[root@benxintuzi IO]# () 每次处理一行(fgets/fputs)
[root@benxintuzi IO]# cat fgets.c
#include <stdio.h> #define MAXLINE 4096 int main(void)
{
char buf[MAXLINE]; while(fgets(buf, MAXLINE, stdin) != NULL)
if(fputs(buf, stdout) == EOF)
printf("output error\n"); if(ferror(stdin))
printf("input error\n"); return ;
} [root@benxintuzi IO]# ./fgets
benxin
benxin
tuzi
tuzi
^C
[root@benxintuzi IO]#
流定位
|
#include <stdio.h> long ftell(FILE* fp); offset ftello(FILE* fp); 返回值:成功,返回文件当前位置;失败,返回-1L int fseek(FILE* fp, long offset, int whence); int fseeko(FILE* fp, off_t offset, int whence); 返回值:成功,0;失败,-1 说明: whence取值如下:SEEK_SET/SEEK_CUR/SEEK_END。 fseek和fseeko唯一的区别是offset的类型不同。 void rewind(FILE* fp)将一个流设置到文件的起始位置。 int fgetpos(FILE* restrict fp, fpos_t* restrict pos); int fsetpos(FILE* fp, const fpos_t* pos); 返回值:成功,0;失败,-1 说明: fgetpos将当前文件指针存入pos中;fsetpos将pos的值设为当前文件位置。 |
格式化I/O
|
格式化输出: #include <stdio.h> int printf(const char* restrict format, ...); int fprintf(FILE* restrict fp, const char* restrict format, ...); int dprintf(int fd, const char* restrict format, ...); int sprintf(char* restrict buf, const char* restrict format, ...); int snprintf(char* restrict buf, size_t n, const char* restrict format, ...); 返回值:成功,返回写入的字符数;失败,返回负值 说明: printf写到标准输出,fprintf写到文件,dprintf写到文件描述符,sprintf写到buf中,但是可能会溢出,snprintf也写到buf中,但是由于指定了缓冲区长度n,可能截断但不会溢出。 格式化输入: #include <stdio.h> int scanf(const char* restrict format, ...); int fscanf(FILE* restrict fp, const char* restrict format, ...); int sscanf(const char* restrict buf, const char* restrict format, ...); |
格式控制:
%[flags][fldwidth][precision][lenmodifier]convtype
|
flags |
说明 |
|
' |
将整数按千位分组输出 |
|
- |
左对齐输出 |
|
+ |
显示带符号转换的正负号 |
|
空格 |
如果第一个字符不是正负号,则用空格代替 |
|
# |
指定转换格式,例如0x前缀 |
|
0 |
用0而非空格进行填充 |
fldwidth:最小字段宽度。若转换后字符数小于该值,则用空格填充。该值可以是一个非负整数或*。
precision:精度表示,整数位数、浮点数小数位数、字符串最大字节数。该值可以是一个.加上非负整数或者*。
lenmodifier:参数长度。hh(signed或unsigned char)/h(signed或unsigned short)/l(signed或unsigned long)/ll(signed或unsigned long long)/j(intmax_t或uintmax_t)/z(size_t)/t(ptrdiff_t)/L(long double)。
convtype:转换类型:
|
d、i |
有符号十进制 |
|
o |
无符号八进制 |
|
u |
无符号十进制 |
|
x、X |
无符号十六进制 |
|
f、F |
双精度浮点数 |
|
e、E |
指数格式双精度浮点数 |
|
g、G |
根据转换后的值解释为f、F、e、E |
|
a、A |
十六进制指数格式双精度浮点数 |
|
c |
字符 |
|
s |
字符串 |
|
C |
宽字符 |
|
S |
宽字符串 |
|
% |
%本身 |
|
p |
void*指针 |
临时文件
每个标准I/O流都有一个相关联的文件描述符,可以调用int fileno(FILE* fp)来获得这个描述符。如下两个函数用于创建临时文件:
|
#inlcude <stdio.h> char* tmpnam(char* ptr); FILE* tmpfile(void); 说明: tmpnam产生一个与现有文件名不同的临时文件名,每次调用时,都产生一个不同的临时文件名。最多的调用次数为TMP_MAX(定义在<stdio.h>中)。若ptr为NULL,则所产生的临时文件名存放在一个静态区中,指向该静态区的指针作为函数值返回;如果ptr不为NULL,则其指向长度大于等于L_tmpnam个字符的数组,所产生的临时文件名存放在该数组中,ptr作为函数值返回。 tmpfile创建一个临时的二进制文件(wb+),在关闭文件或程序结束时自动删除该文件。 #include <stdlib.h> char* mkdtemp(char* template); int mkstemp(char* template); 说明: mkdtemp创建一个目录,返回指向目录名的指针;mkstemp创建一个文件,返回文件描述符。 template的后六位设置为XXXXXX。函数将这些占位符替换成不同的字符来构建一个唯一的名称。 |
[root@benxintuzi IO]# cat tmp.c
#include <stdio.h> int main(void)
{
char name[L_tmpnam], line[];
FILE* fp; printf("%s\n", tmpnam(NULL)); /* first temp name */ tmpnam(name); /* second temp name */
printf("%s\n", name); if((fp = tmpfile()) == NULL) /* create temp file */
printf("tmpfile error\n");
fputs("write one line to tmpfile\n", fp); /* write to temp file */ rewind(fp); /* then read it back */
if(fgets(line, sizeof(line), fp) == NULL)
printf("fgets error\n");
fputs(line, stdout); /* print the line */ return ;
}
[root@benxintuzi IO]# gcc tmp.c -o tmp
/tmp/cc6sXVXs.o: In function `main':
tmp.c:(.text+0x14): warning: the use of `tmpnam' is dangerous, better use `mkstemp'
[root@benxintuzi IO]# ./tmp
/tmp/filekyJuQu
/tmp/fileKA5UyL
write one line to tmpfile
[root@benxintuzi IO]#
[root@benxintuzi IO]# cat mkstemp.c
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h> void make_temp(char *template); int main(void)
{
char good_template[] = "/tmp/dirXXXXXX"; /* right way */
char *bad_template = "/tmp/dirXXXXXX"; /* wrong way*/ printf("trying to create first temp file...\n");
make_temp(good_template);
printf("trying to create second temp file...\n");
make_temp(bad_template);
exit();
} void make_temp(char *template)
{
int fd;
struct stat sbuf; if ((fd = mkstemp(template)) < )
printf("can't create temp file\n");
printf("temp name = %s\n", template);
close(fd);
if (stat(template, &sbuf) < ) {
if (errno == ENOENT)
printf("file doesn't exist\n");
else
printf("stat failed\n");
} else {
printf("file exists\n");
unlink(template);
}
} [root@benxintuzi IO]# gcc mkstemp.c -o mkstemp
[root@benxintuzi IO]# ./mkstemp
trying to create first temp file...
temp name = /tmp/dirnemrcT
file exists
trying to create second temp file...
Segmentation fault (core dumped)
[root@benxintuzi IO]# 说明:
对于第一个模板,由于使用了数组,则数组内容存储在栈上;但是第二个模板,只有指针本身存储在栈上,而具体字符串内容却存放在可执行文件的只读段中,因此当mkstemp函数试图修改字符串时,出现segment fault。
内存流
在内存流中,所有的I/O都是通过在缓冲区与主存之间来回传送字节来完成的。由于避免了缓冲区溢出,因此内存流非常适用于创建字符串。内存流只访问主存,不访问磁盘上的文件,所以性能方面会有显著的提升。有三个函数用于内存流的创建:
|
#include <stdio.h> FILE* fmemopen(void* restrict buf, size_t size, const char* restrict type); 返回值:成功,返回流指针;失败,返回NULL 说明: fmemopen函数允许调用者指定自己的缓冲区用于内存流。buf指向缓冲区的开始位置,size指定了缓冲区的大小,type参数控制流的使用方式:r/rb/w/wb/a/ab/r+/r+b/rb+/w+/w+b/wb+/a+/a+b/ab+ 注意点: (1)以追加方式打开内存流时,当前文件位置设为缓冲区中的第一个null字节;如果缓冲区中不存在null字节,则当前文件位置设为缓冲区结尾的后一个字节。 (2)以其他方式打开内存流时,当前文件位置设为缓冲区的开始位置。 (3)如果buf为null,则打开内存流没有任何意义。 (4)增加流缓冲区中的数据或者调用fclose、fflush、fseek、fseeko、fsetpos时都会在当前位置写入一个null字节。 #include <stdio.h> FILE* open_menstream(char** bufp, size_t* sizep); #include <wchar.h> FILE* open_wmemstream(wchar_t** bufp, size_t sizep); 返回值:成功,返回流指针;失败,返回NULL 说明: open_memstream函数创建的流是面向字节的,其与fmemopen的区别如下: 创建的流只能写打开; 不能指定自己的缓冲区,可以通过bufp和sizep访问缓冲区地址和大小; 关闭流后需要自行释放缓冲区; 对流添加字节会增加缓冲区的大小。 |
以下程序说明了如何在我们自己提供的缓冲区上操作内存流:
[root@benxintuzi IO]# cat memstr.c
#include <stdio.h>
#include <stdlib.h> #define BSZ 48 int main(void)
{
FILE *fp;
char buf[BSZ]; memset(buf, 'a', BSZ-);
buf[BSZ-] = '\0';
buf[BSZ-] = 'X';
if ((fp = fmemopen(buf, BSZ, "w+")) == NULL)
printf("fmemopen failed\n");
printf("initial buffer contents: %s\n", buf);
fprintf(fp, "hello, world");
printf("before flush: %s\n", buf);
fflush(fp);
printf("after fflush: %s\n", buf);
printf("len of string in buf = %ld\n", (long)strlen(buf)); memset(buf, 'b', BSZ-);
buf[BSZ-] = '\0';
buf[BSZ-] = 'X';
fprintf(fp, "hello, world");
fseek(fp, , SEEK_SET);
printf("after fseek: %s\n", buf);
printf("len of string in buf = %ld\n", (long)strlen(buf)); memset(buf, 'c', BSZ-);
buf[BSZ-] = '\0';
buf[BSZ-] = 'X';
fprintf(fp, "hello, world");
fclose(fp);
printf("after fclose: %s\n", buf);
printf("len of string in buf = %ld\n", (long)strlen(buf)); return();
} [root@benxintuzi IO]# ./memstr
# 用a字符改写缓冲区
initial buffer contents: # fmemopen在缓冲区开始处放置null字节
before flush: # 流冲刷后缓冲区才会变化
after fflush: hello, world
len of string in buf = # null字节加到字符串结尾
# 现在用b字符改写缓冲区
after fseek: bbbbbbbbbbbbhello, world # fseek引起缓冲区冲刷
len of string in buf = # 再次追加写null字节
after fclose: hello, worldcccccccccccccccccccccccccccccccccc # 现在用c字符改写缓冲区
len of string in buf = # 没有追加写null字节
[root@benxintuzi IO]#
Linux I/O总结的更多相关文章
- Linux 驱动开发
linux驱动开发总结(一) 基础性总结 1, linux驱动一般分为3大类: * 字符设备 * 块设备 * 网络设备 2, 开发环境构建: * 交叉工具链构建 * NFS和tftp服务器安装 3, ...
- Linux 内核概述 - Linux Kernel
Linux 内核学习笔记整理. Unix unix 已有40历史,但计算机科学家仍认为其是现存操作系统中最大和最优秀的系统,它已成为一种传奇的存在,历经时间的考验却依然声名不坠. 1973 年,在用 ...
- 死磕内存篇 --- JAVA进程和linux内存间的大小关系
运行个JAVA 用sleep去hold住 package org.hjb.test; public class TestOnly { public static void main(String[] ...
- NodeJs在Linux下使用的各种问题
环境:ubuntu16.04 ubuntu中安装NodeJs 通过apt-get命令安装后发现只能使用nodejs,而没有node命令 如果想避免这种情况请看下面连接的这种安装方式: 拓展见:Linu ...
- [linux]阿里云主机的免登陆安全SSH配置与思考
公司服务器使用的第三方云端服务,即阿里云,而本地需要经常去登录到服务器做相应的配置工作,鉴于此,每次登录都要使用密码是比较烦躁的,本着极速思想,我们需要配置我们的免登陆. 一 理论概述 SSH介绍 S ...
- Linux平台 Oracle 10gR2(10.2.0.5)RAC安装 Part3:db安装和升级
Linux平台 Oracle 10gR2(10.2.0.5)RAC安装 Part3:db安装和升级 环境:OEL 5.7 + Oracle 10.2.0.5 RAC 5.安装Database软件 5. ...
- Linux平台 Oracle 10gR2(10.2.0.5)RAC安装 Part1:准备工作
Linux平台 Oracle 10gR2(10.2.0.5)RAC安装 Part1:准备工作 环境:OEL 5.7 + Oracle 10.2.0.5 RAC 1.实施前准备工作 1.1 服务器安装操 ...
- SQL Server on Linux 理由浅析
SQL Server on Linux 理由浅析 今天的爆炸性新闻<SQL Server on Linux>基本上在各大科技媒体上刷屏了 大家看到这个新闻都觉得非常震精,而美股,今天微软开 ...
- Microsoft Loves Linux
微软新任CEO纳德拉提出的“Microsoft Loves Linux”,并且微软宣布.NET框架的开源,近期Microsoft不但宣布了Linux平台的SQL Server,还宣布了Microsof ...
- Linux 江湖系列阶段性总结
引言 我使用 Linux 已经有很多年了,最开始接触 Linux 的时候是从 RedHat 9(没有 Enterprise),中途换过 N 个不同的发行版.多年前,我在 BlogJava 上面分享 J ...
随机推荐
- 关闭GS选项,解决注入后崩溃
利用CreateRemoteThread向进程注入远程代码时,一般会有以下两种做法: 利用LoadLibrary在目标进程加载指定的DLL 将代码复制到目标进程,然后启动这段代码 上面的第二种方法其实 ...
- MFC ComboBox的使用
前言 Combo Box (组合框)控件很简单,可以节省空间.从用户角度来看,这个控件是由一个文本输入控件和一个下拉菜单组成的.用户可以从一个预先定义的列表里选择一个选项,同时也可以直接在文本框里面输 ...
- grep使用
grep常用的使用方法 grep –rns “match_content”filename 查看匹配内容的行 find /path –name “*.h” –o –name “*.cpp” | xar ...
- MS SqlSever一千万条以上记录分页数据库优化经验总结【索引优化 + 代码优化】[转]
对普通开发人员来说经常能接触到上千万条数据优化的机会也不是很多,这里还是要感谢公司提供了这样的一个环境,而且公司让我来做优化工作.当数据库中的记录不超过10万条时,很难分辨出开发人员的水平有多高,当数 ...
- Boost C++: 数据结构---tuple
#include <boost/tuple/tuple.hpp> #include <boost/tuple/tuple_io.hpp> #include <boost/ ...
- JSF 监听
JSF项目中实现基于RBAC模型的权限管理设计(二) 转 4.3 权限验证模块设计 一个好的权限管理机制在项目中应用时,最好不要让程序员在具体业务代码的方法中来判断用户权限.因为这意味着大量重复的代码 ...
- Jquery获取selelct选中值
误区: 一直以为jquery获取select中option被选中的文本值,是这样写的: $("#s").text(); //获取所有option的文本值 实际上应该这样: $(& ...
- Unable to add App ID because the '10' App ID limit in '7' days has been exceeded.
Unable to add App ID because the '10' App ID limit in '7' days has been exceeded. 官方的原因是对bundle iden ...
- Hive[4] 数据定义 HiveQL
HiveQL 是 Hive 查询语言,它不完全遵守任一种 ANSI SQL 标准的修订版,但它与 MySQL 最接近,但还有显著的差异,Hive 不支持行级插入,更新和删除的操作,也不支持事务,但 H ...
- IOS绘图——简单三角形
#import <UIKit/UIKit.h> @interface MyView : UIView @end #import "MyView.h" @implemen ...