4 reading and writing a stream
当我们打开一个流之后,可以选择三种unformatted I/O类型进行读写。
-1:每次一个字符的I/O,每次可以读或者写一个字符,如果流是带缓冲的,I/O标准库会处理所有的缓冲。
-2:每次一行的I/O,可以使用fgets/fputs来读/写一行,每行以一个换行符终止。
-3:直接I/O,可以使用fread/fwrite支持这种I/O。
-1:character-at-a-time I/O
reading:
#include <stdio.h>
int getc(FILE* fp);
int fgetc(FILE* fp);
int getchar();
return: next character if OK, EOF on end of file or error.
getchar()相当于getc(stdin)
getc: getc被ISO C实现为宏,因此,不能将其作为函数指针传递(或许有编译器将其实现为函数),书上称其不能成为有副作用的表达式(稍后解释)。
fgetc:被实现为一个函数。
副作用的表达式:
#define SQRT(x) (x)*(x)
int sqrt(int x) { return x * x; }
如果x的值为++5,我们想取得的值为 6 * 6 = 36.
那么宏SQRT不能正确表达我们的想法,称为有副作用的表达式。
需要注意的是当文件到达末尾或者发生错误时,都是返回EOF,为了分辨使用以下函数:
#include <stdio.h>
int feof(FILE* stream);
int ferror(FILE* stream);
如果为真,则返回非零整数表示true,返回零表示false
在大部分的实现中,流内部维持着一个状态,通过这个状态来判断是那种错误。
可以通过以下函数来清除这种状态:
#include <stdio.h>
void clearerr(FILE* stream);
还有一种函数时用来将字符塞回到缓冲区的,相当于push back,从后头塞进去。
假设缓冲中有"abcd", 当我们塞入f的时候,它出现在末尾"abcdf"。
一次只能塞入一个字符,且不能塞入EOF,这个字符并不是真正的写到底层文件或者设备中,而是流的缓冲区中。
#include <stdio.h>
int ungetc(int c, FILE* stream);
writing:
#include <stdio.h>
int putc(int c, FILE* stream);
int fputc(int c, FILE* stream);
int putchar(int c);
return c if OK, EOF on error.
putc通常被实现成宏,而fputc实现为函数。
-2: line-at-a-time I/O
reading:
#include <stdio.h>
char* gets(char* s);
char* fgets(char* s, int size, FILE* stream);
return buf if OK, null on end of file or error.
fgets指定读取的长度,如果一行超过这个长度,则只读取部分,然后接着读取这一行。
一行的结束以null字符做为标志。
gets是不建议使用的,因为没有指定长度,很容易overflow。
writing:
#include <stdio.h>
int fputs(const char* s, FILE* stream);
int puts(const char* s);
return non-negative value if OK, EOF on error
puts到没有像gets()那样强调不要使用,但是,还是最好使用fputs()吧。
程序用例:
#include <stdio.h>
#include <errno.h>
int main(int argc, char* argv[])
{
int c;
while((c == getc(stdin)) != EOF)
{
if(putc(c, stdout) == EOF)
{
printf("putc error[%d].\n", errno);
return 1;
}
}
const int MAXLINE = 4096;
char buf[MAXLINE];
while(fgets(buf, MAXLINE, stdin) != NULL)
{
if(fputs(buf, stdout) == EOF)
{
printf("putc error[%d].\n", errno);
return 1;
}
}
printf("All success.\n");
return 0;
}
fgetc和fgets效率,依赖于其实现。
5 binary I/O
#include <stdio.h>
size_t fread(void* ptr, size_t size, size_t nmemb, FILE* stream);
size_t fwrite(const void* ptr, size_t size, size_t nmemb, FILE* stream);
fread: ptr指向欲存放读取进来的数据空间,读取字符数以参数size * nmemb决定。
fwrite: ptr指向欲下乳的数据地址,写入的字符书以参数size * nmemb决定。
程序用例:
#include <stdio.h>
#include <errno.h>
#include <string.h>
const int _name_size = 32;
struct Data
{
int id;
int money;
int imageId;
char name[_name_size];
};
void FillData(Data* dataArray)
{
Data* data1 = &dataArray[0];
data1->id = 100;
data1->money = 1000;
data1->imageId = 1000010;
memcpy(data1->name, "AlexZ", _name_size);
Data* data2 = &dataArray[1];
data2->id = 101;
data2->money = 2000;
data2->imageId = 1000011;
memcpy(data2->name, "Hey", _name_size);
}
void PrintfData(const Data* read)
{
if(!read)
return;
printf("id: %d\t money: %d\t imageId: %d\t name: %s\n",
read->id, read->money, read->imageId, read->name);
}
int main(int argc, char* argv[])
{
// test fwrite
FILE* fp = fopen("test5.2.log", "w+");
if(!fp)
{
printf("fp fopen failed, error[%d].\n", errno);
return 1;
}
struct Data dataArray[2];
memset(dataArray, 0, sizeof(Data) * 2);
FillData(dataArray);
int ret = fwrite(dataArray, sizeof(Data), 2, fp);
if(ret != 2)
{
printf("fwrite falied. ret[%d]\t error[%d].\n", ret, errno);
return 1;
}
fclose(fp);
printf("fwrite success.\n");
// test fread
FILE* readFp = fopen("test5.2.log", "r");
if(!readFp)
{
printf("readFp fopen failed, error[%d].\n", errno);
return 1;
}
struct Data readArray[2];
ret = fread(readArray, sizeof(Data), 2, readFp);
if(ret != 2)
{
printf("fread failed. ret[%d]\t error[%d].\n", ret, errno);
return 1;
}
// printf
printf("print whatever read:\n");
PrintfData(&readArray[0]);
PrintfData(&readArray[1]);
printf("test Over.\n");
return 0;
}
执行结果:
fwrite success.
print whatever read:
id: 100 money: 1000 imageId: 1000010 name: AlexZ
id: 101 money: 2000 imageId: 1000011 name: Hey
test Over.
fread和fwrite涉及到一个读写不在相同系统上的问题,在一中类型的系统写,在另一种系统读。
这种情况暂时未碰见,不过后边socket倒是也会遇到这个问题。一般都是改变结构体对齐方式来解决。或者是自行填充一个缓冲区。
6 formatted I/O
格式化输出:
#include <stdio.h>
int dprintf(int fd, const char* format, ...);
int vdprintf(int fd, const char* format, va_list ap);
int printf(const char* format, ...);
int fprintf(FILE* stream, const char* format, ...);
int sprintf(char* str, const char* format, ...);
int snprintf(char* str, size_t size, const char* format, ...);
#include <stdarg.h>
int vprintf(const char* format, va_list ap);
int vfprintf(FILE* stream, const char* format, va_list ap);
int vsprintf(char* str, const char* format, va_list ap);
int vsnprintf(char* str, size_t size, const char* format, va_list ap);
转换类型:
d, i: 有符号十进制
o : 无符号八进制
u : 无符号十进制
x, X: 无符号十六进制
f, F: double精度浮点数
e, E: 指数格式的double精度浮点数
g, G: 解释为f, F, e, E,取决于被转换的值
a, A: 十六进制指数格式的double精度浮点数
c : 字符(若带有长度修饰符l,则为宽字符)
s : 字符串(若带有长度修饰符l,则为宽字符)
p : 指向void的指针
n : 将到目前为止,把缩写的字符数写入到指针所指向的无符号整型中
C : 相当于 lc
S : 相当于 ls
格式化输出:
#include <stdio.h>
int scanf(const char* format, ...);
int fscanf(FILE* stream, const char* format, ...);
int sscanf(const char* str, const char* format, ...);
#include <stdarg.h>
int vscanf(const char* format, va_list ap);
int vsscanf(const char* str, const char* format, va_list ap);
int vfscanf(FILE* stream, const char* format, va_list ap);
转换类型:
d : 有符号十进制,基数为10
i : 有符号十进制,技术由输入格式决定
o : 无符号八进制(输入可选的有符号)
u : 无符号十进制,技术为10(输入可选的有符号)
x : 无符号十六进制(输入可选的有符号)
a, A, e, E, f, F, g, G: 浮点数
c : 字符(若带有长度修饰符l,则为宽字符)
s : 字符串(若带有长度修饰符l,则为宽字符)
[ : 匹配列出的字符序列,以]终止
[^: 匹配除列出的字符序列以外的序列,以]终止
p : 指向void的指针
n : 将到目前为止读取的字符数写入到指针所指向的无符号整型中
C : 宽字符,相当于 lc
S : 宽字符串,相当于 ls
7 memory stream
#include <stdio.h>
FILE* fmemopen(void*buf, size_t size, const char* mode);
FILE* open_memstream(char**ptr, size_t* sizeloc);
#include <wchar.h>
FILE* open_wmemstream(wchar_t** ptr, size_t* sizeloc);
相当于把一段内存映射成一个文件来操作。
fmemopen: mode与fopen相同
#include <stdlib.h>
#include <string.h>
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while(0)
static char buffer[] = "Hello world";
int main(int argc, char* argv[])
{
FILE* in = fmemopen(buffer, strlen(buffer), "r");
if(!in)
handle_error("fmemopen failed.");
int ch;
while((ch = fgetc(in)) != EOF)
printf("%c ", ch);
fclose(in);
printf("\ntest Over\n");
return 0;
}
/////////////////////
open_memstream:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while(0)
int main(int argc, char* argv[])
{
char* buf;
size_t len;
FILE* out = open_memstream(&buf, &len);
if(!out)
handle_error("open_memstream is failed.");
printf("len: %d\n", len);
fprintf(out, "Hello World\n");
fflush(out);
printf("out: %s len: %d\n", buf, len);
fclose(out);
free(buf); // 需要主动释放
return 0;
}