APUE读书笔记-05标准输入输出库(2)

5、打开和关闭流

三种打开流的方式: fopenfreopenfdopen 其原型如下:

#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 filedes, const char *type);

三者返回:如果成功返回文件指针,如果错误返回 NULL

这里:
fopen 打开路径名由 pathname 指定的文件。

fdopen 可以接受一个已经打开的文件描述符号,将这个文件描述符和标准输入输出流关联。这样可以实现对除了磁盘 normal 文件之外的其他特殊文件进行流的操作(例如,有些特殊文件不能用 fopen 打开,那么可以先通过 open 或者 pipe 等获取其文件描述符浩,再用这个函数将其与标准流关联)。

freopen 在一个特定的流上(由 fp 指示)打开一个指定的文件(其路径名由 pathname 指示)如若该流已经打开,则先关闭该流。此函数常用于将一个指定的文件打开为一个预定义的流:标准输入、标准输出或标准出错。

ISO Ctype 参数指定了如下15种值:

                                打开一个标准输入输出流的类型参数
+------------------------------------------------------------------------------------+
|       type       |                           Description                           |
|------------------+-----------------------------------------------------------------|
| r or rb          | open for reading                                                |
|------------------+-----------------------------------------------------------------|
| w or wb          | truncate to 0 length or create for writing                      |
|------------------+-----------------------------------------------------------------|
| a or ab          | append; open for writing at end of file, or create for writing  |
|------------------+-----------------------------------------------------------------|
| r+ or r+b or rb+ | open for reading and writing                                    |
|------------------+-----------------------------------------------------------------|
| w+ or w+b or wb+ | truncate to 0 length or create for reading and writing          |
|------------------+-----------------------------------------------------------------|
| a+ or a+b or ab+ | open or create for reading and writing at end of file           |
+------------------------------------------------------------------------------------+

使用 b 作为 type 的一部分,使标准 I/O 系统可以区分文本文件和二进制文件。但是因为 UNIX 内核并不对这两种文件进行区分,所以在 UNIX 系统环境下指定字符b作为 type 的一部分实际上并没有什么作用。

对于 fdopentype 参数的意义稍有不同。因为该描述符已被打开,所以 fdopen 以写方式打开时并不截短该文件。(例如,若该描述符原来是由 open 函数打开的,该文件那时已经存在,则其 O_TRUNC 标志将决定是否截短该文件。 fdopen 函数不能截短它为写而打开的任一文件。)另外,标准 I/O 以追加方式操作,也不能用于创建该文件(因为如若一个描述符引用一个文件,则该文件一定已经存在) 。

当用追加方式打开一文件后,则每次写都将数据写到文件的当前尾端处。如若有多个进程用标准 I/O 添加方式打开了同一文件,那么来自每个进程的数据都将正确地写到文件中。

当以读和写类型打开一文件时( type+ 号),具有下列限制:
a)如果中间没有 fflushfseekfsetposrewind ,则在输出的后面不能直接跟随输入。
b)如果中间没有 fseekfsetpos 或 =rewind = ,或者一个输出操作没有到达文件尾端,则在输入操作之后不能直接跟随输出。

根据上表,下表列出了打开一个流的六种不同方式。

                        六种打开标准输入输出流的方法
+-----------------------------------------------------------------+
|             Restriction              | r | w | a | r+ | w+ | a+ |
|--------------------------------------+---+---+---+----+----+----|
| file must already exist              | • |   |   | •  |    |    |
|--------------------------------------+---+---+---+----+----+----|
| previous contents of file discarded  |   | • |   |    | •  |    |
|--------------------------------------+---+---+---+----+----+----|
| stream can be read                   | • |   |   | •  | •  | •  |
|--------------------------------------+---+---+---+----+----+----|
| stream can be written                |   | • | • | •  | •  | •  |
|--------------------------------------+---+---+---+----+----+----|
| stream can be written only at end    |   |   | • |    |    | •  |
+-----------------------------------------------------------------+

注意,在指定 wa 类型创建一个新文件时,我们无法说明该文件的存取许可权位( open 函数和 creat 函数则能)。如果流引用的不是终端设备,那么系统默认,它被打开时是全缓存的。如果流引用终端设备,则该流是行缓存的。打开流之后,在对该流执行任何操作之前,可使用前面所述的 setbufsetvbuf 改变缓存的类型。

可以调用 fclose 关闭一个打开的流。声明如下:

#include <stdio.h>
int fclose(FILE *fp);

返回:如果成功返回0,如果错误返回 EOF

在文件被关闭之前,会将任何缓存中的输出数据刷新,而缓存中的输入数据被丢弃。如果标准 I/O 库已经为该流自动分配了一个缓存,则释放此缓存。当一个进程正常终止时(直接调用 exit 函数,或从 main 函数返回),则所有带未写缓存数据的标准 I/O 流都被刷新,所有打开的标准 I/O 流都被关闭。

译者注

原文参考

参考: APUE2/ch05lev1sec5.html

6、关于流的读写

当打开一个 stream 的时候,我们可以有三种方式读写其中的内容.例如: fgetc , fputc .

  1. 字符方式。一次读写一个字符,如果 stream 有缓存那么由标准输入输出函数自己管理缓存。
  2. 行方式。一次读写一行。例如 fputs , fgets .
  3. 直接方式。在 ISO C 中的直接方式,也有时候被称作 binary I/O (二进制方式), object-at-a-time I/O (一次一个对象), record-oriented I/O (面向记录), structure-oriented I/O (面向结构)。每次读写指定的大小。例如 fread , fwrite .

(1)输入函数

有三个函数,可以每次读取输入的一个字符,声明如下:

#include <stdio.h>
int getc(FILE *fp);
int fgetc(FILE *fp);
int getchar(void);

三者返回:如果成功返回下一个字符;否则到达文件结尾返回 EOF ,或者错误。

三个函数都以单个字符的方式操作流。

getchargetc ( stdin )一样。 getcfgetc 的区别是 getc 可能是一个宏定义而 fgetc 一定是一个函数的实现。三者都返回一个 unsigned char 并转化成为 int .这样返回值非负。不能用其返回值和 EOF 比较。判断文件结束有另外的方法。

当读取文件错误或者到达文件结尾的时候,返回值一样,这时候,需要通过 ferrorfeof 才能加以区别。其声明如下:

#include <stdio.h>
int ferror(FILE *fp);
int feof(FILE *fp);
void clearerr(FILE *fp);

两者返回:如果条件符合则返回 true (非0),否则 false

FILE 对象中为每一个 stream 维护一个 error flagend of file flag 。就是通过这两个函数进行判断的。使用 clearerr 可以清除这两个标记。

从流中读取之后,我们也可以通过 ungetc 函数将字符推送到流中,声明如下:

#include <stdio.h>
int ungetc(int c, FILE *fp);

返回:如果成功返回c,如果错误返回 EOF

这个函数可以把一个字符推送回到流中,下次读取的时候就会读到那个字符了。

注意这个函数只是把一个字符推送到流里面了,并没有写这个流对应的实际文件。不能把 EOF 推送到 stream 中。当推送一个字符到 stream 中成功的时候,会自动清除 end of file flag

(2)输出函数

我们也可看到相应于前面讨论的输入函数的输出函数,声明如下:

#include <stdio.h>
int putc(int c, FILE *fp);
int fputc(int c, FILE *fp);
int putchar(int c);

三者返回:如果成功返回c,如果错误返回 EOF

这三个函数和 getc , fgetc , getchar 类似,不过是写而不是读取了。 putchar (c)等价于 putc (c, stdout ), putc 可以用宏的方式实现而 fputc 不能用宏实现。

译者注

原文参考

参考: APUE2/ch05lev1sec6.html

7、行输入输出

下面两个函数提供行输入功能:

#include <stdio.h>
char *fgets(char *restrict buf, int n, FILE *restrict fp);
char *gets(char *buf);

返回:如果成功返回 buf ,如果失败或者到达文件结尾返回 NULL

两者以行的方式操作流。 fgets 需要指定缓存的大小,然后读取一行,读取的结果包含 new line 和一个 null 结束。如果行长超过了n,那么就读取到n,最后也包含一个 null 字符(需要仔细调查???),反正 fgets 最多读取 n-1 个字符。 gets 已经逐渐被淘汰了,因为没有指定缓存大小,不安全,另外它并不把 newline 存放到 buffer 中。

下面是一个例子:

/*程序功能: 
使用fgets读取文件的一行,并且打印出来。
*/
#include <stdio.h>
int main(int argc, char *argv[])
{
        char chars[256];
        FILE *f = fopen("./test","r");
        while(!feof(f))
        {   
                fgets(chars,256,f);//注意回车也都读取了
                printf("%s",chars,ftell(f));
                //printf("%s:%ld",chars,ftell(f));
        }   
        fclose(f);
        return 0;
}

下面两个函数提供行输出功能:

#include <stdio.h>
int fputs(const char *restrict str, FILE *restrict fp);
int puts(const char *str);

两者返回:如果成功返回非负,否则返回 EOF

fputs 会把一个 null 结束的字符串写到一个指定的 stream 中。结尾的 null 字节并不会被写入。需要注意的是,这个函数并不一定是一次一行的输出,因为输出的字符串并不需要把 newline 作为最后的非空字符。一般来说,最后一个非空字符会是 newline 字符,但是这不是必须的。

puts 函数会把 null 字符串写入到标准输出,但是不会写入空字符。另外, puts 还会把一个 newline 字符输出到标准输出。

puts 函数是安全的,而不像相应的 gets 。然而,我们也要避免使用它,否则还要记住它要在输出的最后追加一个 newline 字符。如果我们始终使用 fgetsfputs ,我们会知道我们怎样处理每一行最后的一个 newline 字符。

译者注

原文参考

参考: APUE2/ch05lev1sec7.html

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,172评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,346评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,788评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,299评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,409评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,467评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,476评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,262评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,699评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,994评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,167评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,499评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,149评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,387评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,028评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,055评论 2 352

推荐阅读更多精彩内容