文件流

标准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总结的更多相关文章

  1. Linux 驱动开发

    linux驱动开发总结(一) 基础性总结 1, linux驱动一般分为3大类: * 字符设备 * 块设备 * 网络设备 2, 开发环境构建: * 交叉工具链构建 * NFS和tftp服务器安装 3, ...

  2. Linux 内核概述 - Linux Kernel

    Linux 内核学习笔记整理. Unix unix 已有40历史,但计算机科学家仍认为其是现存操作系统中最大和最优秀的系统,它已成为一种传奇的存在,历经时间的考验却依然声名不坠. 1973 年,在用 ...

  3. 死磕内存篇 --- JAVA进程和linux内存间的大小关系

    运行个JAVA 用sleep去hold住 package org.hjb.test; public class TestOnly { public static void main(String[] ...

  4. NodeJs在Linux下使用的各种问题

    环境:ubuntu16.04 ubuntu中安装NodeJs 通过apt-get命令安装后发现只能使用nodejs,而没有node命令 如果想避免这种情况请看下面连接的这种安装方式: 拓展见:Linu ...

  5. [linux]阿里云主机的免登陆安全SSH配置与思考

    公司服务器使用的第三方云端服务,即阿里云,而本地需要经常去登录到服务器做相应的配置工作,鉴于此,每次登录都要使用密码是比较烦躁的,本着极速思想,我们需要配置我们的免登陆. 一 理论概述 SSH介绍 S ...

  6. 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. ...

  7. 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 服务器安装操 ...

  8. SQL Server on Linux 理由浅析

    SQL Server on Linux 理由浅析 今天的爆炸性新闻<SQL Server on Linux>基本上在各大科技媒体上刷屏了 大家看到这个新闻都觉得非常震精,而美股,今天微软开 ...

  9. Microsoft Loves Linux

    微软新任CEO纳德拉提出的“Microsoft Loves Linux”,并且微软宣布.NET框架的开源,近期Microsoft不但宣布了Linux平台的SQL Server,还宣布了Microsof ...

  10. Linux 江湖系列阶段性总结

    引言 我使用 Linux 已经有很多年了,最开始接触 Linux 的时候是从 RedHat 9(没有 Enterprise),中途换过 N 个不同的发行版.多年前,我在 BlogJava 上面分享 J ...

随机推荐

  1. CreateThread和_BeginThread的区别

    1.程序: 程序构成: (1)源代码 (2)可执行的二进制代码 程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念.由操作系统加载其可执行的二进制代码,分配相应的数据结构:进程控制 ...

  2. Windows phone 8 学习笔记(8) 定位地图导航(转)

    Windows phone 8 已经不使用自家的bing地图,新地图控件可以指定制图模式.视图等.bing地图的定位误差比较大,在模拟器中测试新地图貌似比较理想.本节主要讲解下位置服务以及新地图控件的 ...

  3. Eclipse 安装Groovy插件

    摘自: http://blog.csdn.net/boonya/article/details/45399901 步骤一: 下载eclipse4.3.0,地址:http://www.eclipse.o ...

  4. Oracle 查询字段在什么表

    -- 查询字段在什么表 select * from all_tab_cols t where t.column_name='ABC'; -- 查询字段在什么表并且 判断是否是主键 select * f ...

  5. 解决win 7&win xp等系统无法正常用U盘安装或启动

    目前,制作启动U盘通常是用ultraiso,但由于各种硬件设备与系统的更新,导致现在装系统会出现各种错误. 在用ultraiso制作的启动U盘,装XP时,可能找不到引导项:装win7时,可能提示”wi ...

  6. Unieap3.5错误收集

    String index out of range:32 错误描述:Drm查询报错(drm解析报不通过,后台未打印sql) 错误原因:查询SQL中包含字符 <> ,一般用于查询条件 A & ...

  7. 一个简单的SpringMVC3 程序

    初学者对于Spring框架的难度:引用Jar包不全,或者不正确: 1.运行界面 2.客户端页面 index.jsp 的代码 <%@ page language="java" ...

  8. css3选择器 以及当天知道的东西

    10.25日伪类:a:link{}未访问的链接      a:visited{}已访问的链接     a:hover{}鼠标移动到链接上      a:active{}选定的链接  注:a:hover ...

  9. NodeJs 中的Crypto 加密模块

    加密技术通常分为两大类:“对称式”和“非对称式”. 对称式加密: 就是加密和解密使用同一个密钥,通常称之为“Session Key ”这种加密技术在当今被广泛采用,如美国政府所采用的DES加密标准就是 ...

  10. 网页打包安卓APP流程

    搭建环境过程: 1. 安装JDK. 参见http://www.cnblogs.com/Li-Cheng/p/4334985.html. 注:实质上到该网址上下载好JDK安装包,安装后添加一个环境变量: ...