《unix环境高级编程》 读书笔记 (3)

近来读书,做些笔记,来年好翻翻。

本文所使用的操作系统为 CentOS7.0,如果不想装双系统的可以装虚拟机,可以参考这里:

http://blog.csdn.net/alex_my/article/details/38142229

Standard I/O library


1 byte oriented or wide(multibyte) oriented

标准IO文件流可以是单字节或者是多字节字符集,流定向决定了字符串读写时是单字节还是多字节。当流建立的时候,没有定向。当一个多字节IO函数使用在流上面的时候,流被设置为宽(multibyte)定向;当一个字节IO函数使用在流上面的时候,流被设置为字节(single-byte)定向,这边所提到的流是尚未定义定向的流。

#include <wchar.h>

int fwide(FILE *stream, int mode);

如果mode为负数,则尝试设置流为字节定向。

如果mode为正数,则尝试设置流为宽定向。

如果mode为0,则不尝试设置流定向,当时仍会返回流的定向。

fwide不能改变已经定向的流定向,而且返回值并不是和以往一样,0表示成功,-1表示失败。在调用fwide的时候可以清楚errno值,并在调用结束之后检查errno,借此来检查是否发生了错误。


2 buffering

标准库中提供了三种类型的缓冲:全缓冲,行缓冲,不缓冲。

以下函数可以用于更改缓冲类型:

#include <stdio.h>

void setbuf(FILE *stream, char *buf);

int setvbuf(FILE *stream, char *buf, int mode, size_t size);

setbuf: 用于开关缓冲,当将buf设置为nullptr时,关闭缓冲,或者buf指向一个长度为BUFSIZ(stdio.h)的缓冲区。

setvbuf: 设置所需的缓冲类型

mode可以选择的值如下:

_IOFBF: 全缓冲

_IOLBF: 行缓冲

_IONBF: 不缓冲

如果指定一个不缓冲的流,则可以忽略buf和size参数。

如果指定全缓冲或者行缓冲,则可以指定一个缓冲区和长度,如果流的buf也设置为nullptr, 则标准IO库自动分配一个BUFSIZE长度的缓冲区。

#include <stdio.h>

int fflush(FILE* stream);

fflush: 强制刷新一个流,该流所有未写的数据都被传送到内核中,当stream为nullptr时,所有打开的输出流都被刷新。


3 opening a stream

#include <stdio.h>

FILE *fopen(const char *path, const char *mode);

FILE *fdopen(int fd, const char *mode);

FILE *freopen(const char *path, const char *mode, FILE *stream);

fopen: 打开一个文件,并返回一个与之相关联的流

其中,mode可以选择以下字符串:

"r" : 打开一个读文件(可以读的文件,下同), 流从文件头开始。

"r+": 打开一个读写文件,流从文件头开始。

"w" : 打开一个写文件,并且文件长度截短为0,流从文件头开始。

"w+": 打开一个读写文件,如果文件不存在,则创建,如果存在,则文件长度截短为0,流从文件头开始。

"a" : 打开一个写文件,如果文件不存在,则创建,如果存在,流从文件尾开始。

"a+": 打开一个读写文件,如果文件不存在,则创建,如果存在,则读从文件头开始,写从文件尾开始。

经常可以看见添加符号‘b‘的,不过POSIX系统一般都忽略,包括Linux。

fdopen: 通过文件描述符返回一个与之关联的流。

其中,mode可选的字符串与fopen相同,但不会有"文件不存在,则创建"之类效果,也不会截短,因为有效的fd保证了文件已   经存在了。

当fdopen创建的流被关闭的时候,文件描述符fd也会被关闭。这边会有一些共享内存对象的问题。

另外,mode与获取fd时选择的mode要兼容,假设使用open获取fd时,设置为readonly,则此时设置"r+"会冲突,具体看示例 test5.1, 对应如下:

r  : O_RDONLY

r+ : O_RDWR

w  : O_WRONLY | O_CREAT | O_TRUNC

w+ : O_RDWR | O_CREAT | O_TRUNC

a  : O_WRONLY | O_CREAT | O_APPEND

a+ : O_RDWR | O_CREAT | O_APPEND

并不一定是完全对应的,应该根据实际情况来定,比如下边的示例:

r+ : O_RDWR | O_CREAT

程序用例:

// test5.1.cc

#include <stdio.h>

#include <errno.h>

#include <fcntl.h>

int main()

{

int fd = open("test5.1.log", O_RDWR | O_CREAT);

if(fd == -1)

{

printf("fd open filed, error[%d].\n", errno);

return 1;

}

printf("fd is %d.\n", fd);

FILE* file = fdopen(fd, "r+");

if(!file)

{

printf("fdopen failed, error[%d].\n", errno);

return 1;

}

printf("fdopen success\n");

return 0;

}

可以尝试将open时候的O_RDWR改为O_RDONLY或者修改fdopen中的r+为r。会报错为无效参数

#include <stdio.h>

int fileno(FILE* stream);

fileno: 返回它的文件描述符。


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;

}

时间: 2024-10-14 04:35:34

《unix环境高级编程》 读书笔记 (3)的相关文章

高性能javascript读书笔记(三.DOM 编程1)

第三章DOM Script DOM编程 读书笔记 访问和修改DOM元素 浏览器通常要求DOM实现和JavaScript保持相互独立. <!-- 例如IE中,被称为JScript的JavaScript实现位于库文件jscript.dll中,而DOM实现位于另一个库mshtml.dll(内 部代号Trident).这种分离技术允许其他技术和语言,如VBScript,受益于Trident所提供的DOM功能和渲染功能.Safari使用Webkit的WebCore处理DOM和渲染,具有一个分离的JavaS

数据访问---高性能JavaScript读书笔记(2)

对于任何一种编程语言来说,数据存储的位置关系到访问速度! 在JavaScript中的直接量包括字符串string.数字number.布尔值boolean.对象object.数组array.函数function.正则表达式regular expression.空值null.未定义数组undefined.而数组项则需要通过数组的数字索引来访问,对象通过字符串进行索引来访问其成员(这里顺便提一句因为数组项是通过数字进行索引.对象成员是通过字符串进行索引,所以这也就是为什么访问对象成员比访问数组项更慢的

加载和运行---高性能JavaScript读书笔记(1)

众所周知大多数浏览器是使用单进程处理UI更新和JavaScript运行等多个任务的,而同一时间只能有一个任务被执行,如此说来,JavaScript运行了多长时间就意味着用户得等待浏览器响应需要花多久时间. 从认知上来说,解析器解析一个界面的时候都是从上至下依次解析的,这就是说界面上出现多少个<script>标签(不管是内联还是外部文件),页面下载和解析必须停止等待脚本下载完成并运行完成(注意这里包括运行),这个过程当中,页面解析和用户交互是被完全阻塞的. Javascript第一条定律:将脚本

DOM访问---高性能JavaScript读书笔记(3)

在JavaScript高级程序设计第一章当中就把JavaScript分成三大部分 所以事实上DOM和BOM是两在独立的部分,它们之间的通信是通过相互之间的功能接口来实现的,这样说来两个独立的部分以功能接口必定会带来性能损耗.这也就是为什么大家一致都说尽量少去访问和修改DOM元素(注意我这里说的是访问和修改,为什么包括访问,请继续往下看  哈哈). 下面用一张图来说明它们各自的作用. 1.在修改DOM元素的时候,我们应该尽量使用innerHTML而不是CreateElement再AppendChi

高性能javascript读书笔记(三.DOM 编程2)

重绘和重排版 浏览器下载完所有的HTML标记,Javascript,CSS,图片之后,它解析文件并创建两个内部数据结构 DOM树 表示页面结构渲染树 表示DOM节点如何显示 渲染树中为每个需要显示的DOM树木=节点存放至少一个节点(隐藏DOM元素在选桉树中没有对应节点)渲染树上的节点称为"框"或者"盒",符合CSS模型的定义,将页面元素看作一个具有填充,边距,边框和位置的盒.一 旦DOM树和渲染树构造完毕,浏览器就可以显示(绘制)页面上的元素了. 当DOM改变影响到

Javascript读书笔记:函数定义和函数调用

定义函数 使用function关键字来定义函数,分为两种形式: 声明式函数定义: function add(m,n) { alert(m+n); } 这种方式等同于构造一个Function类的实例的方式: var add = new Function("m", "n", "alert(m+n);"); Function类构造方法的最后一个参数为函数体:"alert(m+n);",前面的都是函数的形参,参数必须是字符串形式的:&

Javascript读书笔记:字符串常用方法

concat() 连接多个字符串,返回合并后的字符串. 1 var s1="a"; 2 var s2="b"; 3 var s3="c"; 4 5 console.log(s1.concat(s2,s3));//abc 数组中的concat():将参数添加为数组的元素,返回新的数组. 1 var arr = [1, 2, 3]; 2 console.log(arr.concat(4, 5));//[1,2,3,4,5] indexOf() 查找子

高性能javascript学习笔记系列(1) -js的加载和执行

这篇笔记的内容主要涉及js的脚本位置,如何加载js脚本和脚本文件执行的问题,按照自己的理解结合高性能JavaScript整理出来的 javascript是解释性代码,解释性代码需要经历转化成计算机指令的过程,这个过程就会带来一定的性能损耗,所以在js中做性能的优化是必须的 javascript的阻塞特性:浏览器在执行js代码的时候,不能做其他的任何事情,因为浏览器使用单一的进程来处理用户界面的刷新和javascript的脚本执行,也就是说什么时候执行js脚本影响着用户对页面的使用体验(之所以js

高性能javascript学习笔记系列(6) -ajax

参考 高性能javascript javascript高级程序设计 ajax基础  ajax技术的核心是XMLHttpRequest对象(XHR),通过XHR我们就可以实现无需刷新页面就能从服务器端读取数据 var xhr = new XMLHttpRequest(); //只支持IE7以及更高的版本 xhr.onreadystatechange = function() { if(xhr.readyState == 4) { if( (xhr.status >= 200 && xh

高性能javascript学习笔记系列(5) -快速响应的用户界面

参考高性能javascript 理解浏览器UI线程  用于执行javascript和更新用户界面的进程通常被称为浏览器UI线程  UI线程的工作机制可以理解为一个简单的队列系统,队列中的任务按顺序执行 <button onclick="handleClick()">click</button> <script type="text/javascript"> function handleClick() { var div = do