为什么80%的码农都做不了架构师?>>>
流和FIFE对象
标准I/O文件流可以处理单字节和宽字节. 函数fwide用于设置流的方向:
#include
#include
int fwide(FILE *fp, int mode);
returns: 宽字节流则返回正数, 单字节流则返回负数, 没有则返回0.
如果mode为负数, 则函数尝试将流设置为单字节.
如果mode为正数, 则函数尝试将流设置为宽字节.
如果mode为0, 则保持原样.
当我们使用fopen打开一个流时, 它将返回一个指针指向FILE对象. 此对象包含了流的全部信息: 文件描述符, 指针指向一个存储流的Buffer, Buffer的大小, 已读取流的大小, 错误标志等等.
缓冲(Buffering)
标准I/O提供三种类型的缓冲:
1. 全缓冲: 在填满标准I/O缓冲区后才进行实际I/O操作.
2. 行缓冲: 输入和输出遇到换行符时候, 标准I/O执行I/O操作.
3. 不带缓冲.
通常, 系统默认是:
1. 标准错误是不带缓冲的.
2. 如若是涉及终端设备的其他流,则它们是行缓冲的;否则是全缓冲的。
通过函数setbuf来改变流的模式:
#include
void setbuf(FILE *restrict fp, char *restrict buf);
int setvbuf(FILE *restrict fp, char *restrict buf, int mode, size_t size);
returns: 成功返回0, 失败返回非0.
这些函数必须在流被打开,后并且对流操作之前调用.
使用setbuf函数, 我们可以打开或关闭缓冲. 打开缓冲, 则buf的长度必须为BUFSIZ, 默认情况下为全缓冲, 但如果流关联到终端则会被修改为行缓冲. 如果关闭缓冲, 则buf设置为NULL.
使用setvbuf函数, 我们可以显式设置缓冲类型:
_IOFBF: 全缓冲
_IOLBF: 行缓冲
_IONBF: 非缓冲
任何情况下, 我们可以使用以下函数强制刷新缓冲:
#include
int fflush(FILE *fp);
returns: 成功返回0, 失败返回EOF
打开一个流
以下函数打开一个标准的I/O流:
#include
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 filedes, const char *type);
returns: 成功返回文件指针, 失败返回NULL
而type有以下类型:
type | 含义 |
r, rb | 读 |
w, wb | 文件截断为0或者打开文件用于写 |
a, ab | 打开文件追加至末尾, 或者创建文件用于写 |
r+, r+b, rb+ | 读写 |
w+, w+b, wb+ | 文件截断为0或创建文件用于读写 |
a+, a+b, ab+ | 打开文件用于读, 或者在文件末尾写入 |
b用于表明是二进制文件还是文本文件, 但是对于Unix来说二进制文件和文本文件并无区别, 所以b参数无任何影响.
使用fclose关闭一个打开流:
#include
int fclose(FILE *fp);
读取或写入流
以下函数允许我们一次读取一个字符:
#include
int getc(FILE *fp);
int fgetc(FILE *fp);
int getchar(void);
returns: 成功返回下一个字符, 失败或读取完毕返回EOF
getchar函数等价于getc(stdin). 而getc和fgetc不同之处在于: getc为宏定义, 而fgetc为函数.
用于无论出错还是读取完毕, 均返回EOF, 所以以下两个函数用于辨别它们:
#include
int ferror(FILE *fp);
int feof(FILE *fp);
returns: 成功返回非0, 失败返回0
错误标志和EOF标志均会绑定到FILE对象上, 使用clearerr来清除:
void clearerr(FILE *fp);
在读取一个流之后, 我们可以使用ungetc将字符push back.
#include
int ungetc(int c, FILE *fp);
returns: 成功返回c, 失败返回EOF
以下函数允许我们一次写入一个字符:
#include
int putc(int c, FILE *fp);
int fputc(int c, FILE *fp);
int putchar(int c);
returns: 成功返回c, 失败返回EOF
一个实际的例子:
#include
int main(void)
{
unsigned int c;
while ((c = getc(stdin)) != EOF) {
putc(c, stdout);
}
return 0;
}
我们可以使用以下函数读取和写入一行数据:
#include
char *fgets(char *restrict buf, int n, FILE *restrict fp);
char *gets(char *buf);
returns: 成功返回buf, 失败或到文件末尾返回NULL.
int fputs(const char *restrict str, FILE *restrict fp);
int puts(const char *str);
returns: 成功返回非负值, 失败返回EOF
fgets用于读取一行, 但buf长度不可大于n.
gets函数不推荐使用, 因为没确定所读取数据的长度, 容易导致溢出或者数据丢失.
而puts也不推荐使用, 因为它写入的字符串不带null.
一个实际的例子:
#include
#define BUFFSIZE 4096
int main(void)
{
unsigned int arr[BUFFSIZE];
while (fgets(arr, BUFFSIZE - 1, stdin) != NULL) {
fputs(arr, stdout);
}
return 0;
}
二进制I/O
在读取二进制文件时候我们无法使用getc/putc, 也无法使用fputs/fgets, 应该使用fread/fwrite:
#include
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);
returns: 读取或写入的个数
1--读写一个二进制数组。例如,为了将一个浮点数组的第2~5个元素写至一个文件上,则代码如下:
float data[ 10 ];
if (fwrite(&data[2], sizeof(float), 4, fp ) != 4 )
printf("write error\n");
2--读写一个结构,则代码如下:
struct {
short count;
long total;
char name[ NAMESIZE ];
} item;
if ( fwrite(&item, sizeof(item), 1, fp ) != 1 )
printf("write error\n");
以下程序将结构写入文件中,然后复制文件再读取出来:
#include
struct people{
char *name;
int age;
};
int main(void)
{
int c;
char buf[ 10 ];
FILE *fp1 = fopen("people", "w+");
FILE *fp2 = fopen("people.foo","w+");
if (NULL == fp1 || NULL == fp2) {
printf("open file error\n");
return 1;
}
struct people p1;
struct people p2;
struct people p3;
struct people p4;
p1.name = "小雷";
p1.age = 25;
p2.name = "小猫咪";
p2.age = 24;
if (fwrite(&p1, sizeof(p1), 1, fp1) != 1) {
printf("write error\n");
return 1;
}
if (fwrite(&p2, sizeof(p2), 1, fp1) != 1) {
printf("write error\n");
return 1;
}
if (fseek(fp1, 0, SEEK_SET) != 0) {
printf("fseek error\n");
return 1;
}
while ((c = getc(fp1)) != EOF) {
if (putc(c, fp2) == EOF) {
printf("write error\n");
return 1;
}
}
if (fseek(fp2, 0, SEEK_SET) != 0) {
printf("fseek error\n");
return 1;
}
if (fread(&p3, sizeof(p3), 1, fp2) != 1) {
printf("read error\n");
return 1;
}
if (fread(&p4, sizeof(p4), 1, fp2) != 1) {
printf("read error\n");
return 1;
}
printf("name is:%s, age is:%d\n", p3.name, p3.age);
printf("name is:%s, age is:%d\n", p4.name, p4.age);
return 0;
}
程序输出:
leicj@leicj:~/test$ ./a.out
name is:小雷, age is:25
name is:小猫咪, age is:24
临时文件
#include
#define MAXLINE 4096
int main(void)
{
char name[L_tmpnam], line[MAXLINE];
FILE *fp;
printf("%s\n", tmpnam(NULL));
tmpnam(name);
printf("%s\n", name);
if ((fp = tmpfile()) == NULL) {
printf("tmpfile error\n");
return 1;
}
fputs("one line of output\n", fp);
rewind(fp);
if (fgets(line,sizeof(line), fp) == NULL) {
printf("fgets error\n");
return 1;
}
fputs(line, stdout);
return 0;
}
程序输出:
leicj@leicj:~/test$ ./a.out
/tmp/fileUmW8wA
/tmp/fileBpa2Jr
one line of output