Linux操作系统-标准IO库(2)

Linux操作系统—标准IO库(2)(2015-8-4)

分类:Linux操作系统

  打开一个流后,可采用三种不同类型的非格式化I/O对其进行读,写操作。
1. 每次读取一个字符的I/O
2. 每次一行的I/O。以换行符标示一行的终止
3. 二进制I/O。每次I/O操作读或写一定数量的对象,而每个对象具有指定的长度。

  前两者可以说是基于字符和行的I/O。后者叫做二进制I/O。

基于字符和行的I/O

字符I/O

读字符

  使用下面的三个函数可以一次读取一个字符

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

  对于这些函数的区别,作为初学者,我不想太纠结,这不是重点。等以后使用熟练再深究也不迟。暂时先记着这个:函数getchar等同于getc(stdin)(这就意味着getchar是特定地从stdin中读取数据)。
  参数stream表示已打开并准备从其中读取数据的流。成功时,三个函数均返回所读取的字符,出错或已到达文件尾端时返回EOF。
&esmp; 下面来讲一讲非常有意思东西。大家可能发现了,这三个函数返回的是字符,然而它们的函数类型却是int型的,这是怎么回事呢?
  这三个函数以unsigned char类型转换为int的方式返回下一个字符。说明为不带符号的理由是,即使所读字节最高位为1也不会返回负数。要求整形返回值的理由是,这样可以返回已发生错误或已达到文件尾端的指示值EOF(-1)。

小插曲:回送字符
  当读一个输入流时,经常会用到回送字符操作。回送字符操作通常用于需要根据下一个字符的值来决定如何处理当前字符的情况。处理这种情况时,需要在读出下一个字符以后,能将其送回流缓冲区,以便下一次输入时再返回该字符。
  标准I/O库提供了ungetc函数以支持字符回送操作。该函数的原型如下。

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

  说明:参数c是要送回流中的字符,stream是所操作的流。成功时返回c,失败时返回EOF。送到流中的字符以后又可以从流中读出,但读出的顺序与送回的顺序相反。回送的字符,不一定必须是上一次读到的字符,但是不能回送EOF。但已到达文件尾端时,扔可以回送一个字符。下次读将返回该字符,再次读时才返回EOF。之所以能这样做的原因是一次成功的ungetc调用会清除该流的文件结束指示。

判断流结束或出错

  在大多数的FILE对象的实现中都为每个流保持了两个标志:文件结束标志和文件出错标志。feof函数和ferror函数分别根据这两个标志来判断流是否结束或着流是否出错。因为到达文件尾端和出错时,三个字符读取函数的返回值都是EOF,所以应当通过调用feof和ferror函数区分这两种情况。(看到这,终于明白了,这两个函数的存在是为了分辨,当这三个函数返回的是EOF时,到底是因为流结束还是因为是出错了)。这两个函数的原型如下:

#include <stdio.h>
int feof(FILE *stream);
int ferror(FILE *stream);

  说明:参数stream为要判断是否已到达文件尾或出错的流。当流已到达文件尾时,feof返回非0值,否则返回0值。当流出错时,ferror返回非0,否则返回0。
  那么,如何清除FILE对象中的文件结束标志和出错标志呢?可以使用clearerr函数,该函数的原型如下:

#include <stdio.h>
void clearerr (FILE *stream);
写字符

  使用下列字符可以一次写入一个字符

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

  与字符输入函数一样,putchar(c)与putc(c, stdout)等价。这些函数的每一次调用会使当前读写位置向文件尾部移动一个字符,即每次写入的位置是上一次写入位置之后的一个字节。
  成功时,三个函数均返回写入的字符,出错时返回EOF。

实践篇

  初学者嘛,尽情地实践,有了想法就把它实现出来,尽量把学到的东西都练习一遍。
目标一:从标准输入中读取字符并将其保存在名为data.txt的文件中
  这个并不难,可以很轻松地写出来。注意在命令行中,按下Ctrl + D可以向输入流中产生EOF值。

/*
 * Name : IO001.c
 * Author : LazyBone1994
 * Date : 2015-8-4 18:00
 *
*/

#include <stdio.h>
#include <stdlib.h>

/* 从标准输入中读取字符并将其保存在名为data.txt的文件中 */
int main(int argc, char *argv[])
{
    FILE *fp;
    int c;

    if ((fp = fopen("data.txt", "w")) == NULL){         /* 无法创建文件 */
        printf("Cannot create data.txt file!\n");
        exit(-1);
    }
    while ((c = getchar()) != EOF)                      /* 从标准输入读取字符 */
        fputc(c, fp);
    fclose(fp);                                         /* 关闭流 */

    return 0;
}

  查看一下结果呗。实现了目标

biantiao@lazybone1994-ThinkPad-E430:~/桌面$ gcc -o IO001 IO001.c
biantiao@lazybone1994-ThinkPad-E430:~/桌面$ ./IO001
Hello World !
This is a sample test.
biantiao@lazybone1994-ThinkPad-E430:~/桌面$ ls
data.txt  IO001  IO001.c  IO001.c~  IO001.C~
biantiao@lazybone1994-ThinkPad-E430:~/桌面$ cat data.txt
Hello World !
This is a sample test.
biantiao@lazybone1994-ThinkPad-E430:~/桌面$

  初学者就要尽情地尝试。改造一下,改哪里?将getchar语句改掉改成一个等效的语句。

/*
 * Name : IO001.c
 * Author : LazyBone1994
 * Date : 2015-8-4 18:00
 *
*/

#include <stdio.h>
#include <stdlib.h>

/* 从标准输入中读取字符并将其保存在名为data.txt的文件中 */
int main(int argc, char *argv[])
{
    FILE *fp;
    int c;

    if ((fp = fopen("data.txt", "w")) == NULL){         /* 无法创建文件 */
        printf("Cannot create data.txt file!\n");
        exit(-1);
    }
    while ((c = getc(stdin)) != EOF)                    /* 从标准输入读取字符 */
        fputc(c, fp);
    fclose(fp);                                         /* 关闭流 */

    return 0;
}

  看看结果如何。实现了目标。

biantiao@lazybone1994-ThinkPad-E430:~/桌面$ gedit IO001.c
biantiao@lazybone1994-ThinkPad-E430:~/桌面$ gcc -o IO001 IO001.c
biantiao@lazybone1994-ThinkPad-E430:~/桌面$ ls
IO001  IO001.c
biantiao@lazybone1994-ThinkPad-E430:~/桌面$ ./IO001
Hello, My CSDN ID is LazyBone1994.
biantiao@lazybone1994-ThinkPad-E430:~/桌面$ ls
data.txt  IO001  IO001.c
biantiao@lazybone1994-ThinkPad-E430:~/桌面$ cat data.txt
Hello, My CSDN ID is LazyBone1994.
biantiao@lazybone1994-ThinkPad-E430:~/桌面$

目标二:从标准输入中构造文本data.txt,并实现copyFile函数复制data.txt文件
  按照顺序来写吧,如下:

/*
 * Name : IO002.c
 * Author : LazyBone1994
 * Date : 2015-8-5 11:23
*/

#include <stdio.h>
#include <stdlib.h>
#define     ERROR       -2
#define     OK          1

/* source为要被复制的文件的指针,filename为复件的文件名 */
int copyFile(FILE *source, char *filename)
{
    char c;
    FILE *newFile = NULL;

    if (source == NULL){
        printf("Source file error!\n");
        return ERROR;
    }
    if (filename == NULL){
        printf("Warning:The new file will use the default name.\n");
        filename = "a.txt";
    }

    newFile = fopen(filename, "w");                 /* 打开文件的另外两个是fdopen和freopen,它们在这里并不适用 */
    if (newFile == NULL){
        printf("Cannot create new file.\n");
        return ERROR;
    }
    while ((c = getc(source)) != EOF){              /* 可用fgetc替换,另一个函数getchar在这并不适用 */
        putc(c, newFile);                           /* 可用fputc替换,另一个函数putchar在这并不适用 */
    }
    fclose(newFile);

    return OK;
}

/* 从标准输入中构造文本data.txt,并实现copyFile函数复制data.txt文件 */
int main(int argc, char *argv[])
{
    int flag;
    char c;
    char filename[33];
    FILE *fp;

    printf("Creating data.txt begins...\n");
    if ((fp = fopen("data.txt", "w")) == NULL){
        printf("Cannot create file data.txt.\n");
        exit(-1);
    }
    printf("Please input the content of data.txt(end with Ctrl + D):\n");
    while ((c = getchar()) != EOF){
        fputc(c, fp);
    }
    fclose(fp);
    fp = fopen("data.txt","r");

    printf("Create file data.txt is done.\n");
    printf("Copy new file is begining...\n");
    printf("Please input new file's name(end with Ctrl + D):");
    fgets(filename, 33, stdin);
    putchar('\n');
    flag = copyFile(fp, filename);
    if (flag == OK)
        printf("Copy new file is done.\n");
    else
        printf("Copy new file failed.\n");

    return 0;
}

  修改了好久(没办法比较菜),终于能行了,还算比较友好的界面提示。查看结果了。

biantiao@lazybone1994-ThinkPad-E430:~/桌面$ gcc -o IO002 IO002.c
biantiao@lazybone1994-ThinkPad-E430:~/桌面$ ./IO002
Creating data.txt begins...
Please input the content of data.txt(end with Ctrl + D):
Hello everybody!
This is all the words that will be copied later.
Create file data.txt is done.
Copy new file is begining...
Please input new file's name(end with Ctrl + D):newFile.txt
Copy new file is done.
biantiao@lazybone1994-ThinkPad-E430:~/桌面$ ls
data.txt  IO002  IO002.c  IO002.c~  newFile.txt
biantiao@lazybone1994-ThinkPad-E430:~/桌面$ cat newFile.txt
Hello everybody!
This is all the words that will be copied later.
biantiao@lazybone1994-ThinkPad-E430:~/桌面$

  怎么说呢?当所有的输入都不为空的时候,确实能够复制成功。但是但你输入的新文件的名称为空时,复制的新文件的名字将会出现乱码。为什么?因为filename这个指针从来不为空(即使你不输入它)所以函数中的if (filename == NULL)就成了无用的了,实际的文件名在你不输入的情况下取的是垃圾值。怎么修改呢?暂时先不想,往下继续。想好的朋友可以告诉我。
  发现自己的C语言功底实在是菜。特别是在字符串,数组,指针,传指针调用结合在一起的情况下,就懵了。得好好恶补。

行I/O

读行

  使用以下两个函数可以从流中一次读取一行文本。

#include <stdio.h>
char *fgets(char *s, int size, FILE *stream);
char *gets(char *s);

  各参数和返回值含义如下:
1. s:输入缓存
2. size:输入缓存大小
3. stream:流文件指针
4. 返回值

  • 成功时返回指向缓存的指针
  • 失败时或处于文件尾端时返回NULL,同时设置errno

  fgets从指定的流读,而gets从标准输入读。每调用一次fgets会使当前读写位置向文件尾部移动所读取到的字符数,以保证每次读取的是上一次读取位置之后的位置。
  对于fgets,必须指定缓存的长度size。次函数一直读取到下一个换行符为止,但是不超过size - 1个字符,读入的字符被送入缓存。该缓存总是以null字符结尾。如果该行(包括最后一个换行符)的字符数超过size - 1,则只返回一个不完整的行,而且缓存仍以null字符结尾。对fgets的下一次调用会继续读取该行剩余的字符。
  gets并不招人待见,因为使用它非常不安全,而且在新的C语言标准中它已经被踢出去了,只是为了向后兼容,使用它仍可以通过编译。记住一点:fgets会将读取到的换行符送入缓存,而gets并不保存换行符。

写行

  使用下面两个函数可向流中一次写入一行文本。

#include <stdio.h>
int fputs(const char *s, FILE *stream);
int puts(const char *s);

  这两个函数的各参数和返回值的含义如下:
1. s:待输出的字符串,应以null终止
2. stream:流文件指针
3. 返回值

  • 成功时返回非负数
  • 失败时返回EOF(-1)

  函数fputs将一个以null符终止的字符写到指定的流,终止符null本身并不写出。但是,puts在输出指定的字符串后又附加地将一个换行符写到标准输出。

实践篇

目标一:反复从标准输入中读取行并将其保存在一个文本中
  代码如下喽

/*
 * Name : IO003.c
 * Author : LazyBone1994
 * Date : 2015-8-6 15:35
*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define     BUFFSIZE        81

void DeleteCharEnter(char *s)
{
    if (s == NULL)
        return ;

    for (; (*s) != '\0'; s++){
        if (*s == '\n')
            *s = '\0';
    }
}

/* 反复从标准输入中读取行并将其保存在一个文本中 */
int main(int argc, char *argv[])
{
    FILE *fp = NULL;
    char filename[33], str[BUFFSIZE];

    printf("Please input the filename(end with Enter):");
    fgets(filename, 33, stdin);
    DeleteCharEnter(filename);

    if ((fp = fopen(filename, "w")) == NULL){
        puts("Error : Cannot open file for writing.");
        exit(-1);
    }

    printf("Please input the file content:\n");             /* 输入quit结束 */
    while (strcmp(fgets(str, BUFFSIZE, stdin), "quit\n") != 0){
        fputs(str, fp);
    }
    fclose(fp);

    return 0;
}

  编译并运行

biantiao@lazybone1994-ThinkPad-E430:~/桌面$ gcc -o IO003 IO003.c
biantiao@lazybone1994-ThinkPad-E430:~/桌面$ ./IO003
Please input the filename(end with Enter):NewFIle
Please input the file content:
Hello World!
I am LazyBone1994.
quit
biantiao@lazybone1994-ThinkPad-E430:~/桌面$ ls
IO003  IO003.c  NewFIle
biantiao@lazybone1994-ThinkPad-E430:~/桌面$ cat NewFIle
Hello World!
I am LazyBone1994.
biantiao@lazybone1994-ThinkPad-E430:~/桌面$

目标二:从文件中读取字符,并将其显示在标准输出中
  就利用刚才生成的NewFile文件吧,代码如下喽

/*
 * Name : IO004.c
 * Author : LazyBone1994
 * Date : 2015-8-6 16:23
*/

#include <stdio.h>
#include <stdlib.h>
#define     BUFFSIZE    80

/* 从文件中读取字符,并将其显示在标准输出中 */
int main(int argc, char *argv[])
{
    char buf[BUFFSIZE];
    FILE *fp;

    fp = fopen("NewFIle", "r");
    if (fp == NULL){
        puts("Error : Cannot open file for reading.");
        exit(-1);
    }

    while((fgets(buf, BUFFSIZE, fp)) != NULL){
        fputs(buf, stdout);
    }

    fclose(fp);

    return 0;
}

  编译并运行:

biantiao@lazybone1994-ThinkPad-E430:~/桌面$ ls
IO004  IO004.c  NewFIle
biantiao@lazybone1994-ThinkPad-E430:~/桌面$ ./IO004
Hello World!
I am LazyBone1994.
biantiao@lazybone1994-ThinkPad-E430:~/桌面$
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值