1.5.输入和输出(Input and Output)

1.5.输入和输出(Input and Output)

文件描述符(File Descriptors)
正常地,文件描述符是非负的小整数(small non-negative integers:0,1,2...)。文件描述符出现的原因是为了kernel能够识别一个进程正在存取的所有文件。无论何时kernel打开一个存在的文件或者创建一个新的文件,它都返回一个文件描述符,以便我们随后能够使用文件描述符来读或写文件。

标准输入,标准输出和标准出错(Standard Input, Standard Output, and Standard Error)
按惯例,无论何时一个新程序运行,任一种shell都会打开3个文件描述符:标准输入,标准输出和标准出错。If nothing special is done,这3个文件描述符都联系到终端(terminal)。大多数的shell都提供了一种方法,能将这3个文件描述符重新定向到任一个文件。例如,
ls > file.list
执行命令ls with它的标准输出重新定向到文件file.list。
-----------------------------
如果文件file.list不存在,会先创建文件file.list,然后再执行ls。
这样,就会出现这样的情况:
[lizl@hydra03 ~]$ ls
betweenExt3AndHfs
[lizl@hydra03 ~]$ ls > file.list
[lizl@hydra03 ~]$ more file.list
betweenExt3AndHfs
file.list
[lizl@hydra03 ~]$
-----------------------------

不用缓存的输入输出(Unbuffered I/O)
函数open, read, write, lseek以及close提供了不用缓存的I/O。这些函数都用文件描述符进行工作。

Example
如果我们愿意从标准输入读,并向标准输出写, 那么可以看程序figure1.4,该程序也可用于复制任一UNIX文件。

Figure 1.4. Copy standard input to standard output, using Unbuffered I/O

#include <unistd.h>
#include <iostream>
using namespace std;

#define BUFFSIZE    4096

int
main(void)
{
    int     n;
    char    buf[BUFFSIZE];

    while ((n = read(STDIN_FILENO, buf, BUFFSIZE)) > 0)
        if (write(STDOUT_FILENO, buf, n) != n)
            cout<<"write error!"<<endl;
    if (n < 0)
        cout<<"read error!"<<endl;

        exit(0);
}
(第3章将更详细地说明不用缓存的I/O函数。)
头文件<unistd.h>和常量STDIN_FILENO,STDOUT_FILENO是POSIX标准的一部分(在下一章中我们会对POSIX进行更多的说明)。在头文件<unistd.h>中,有许多UNIX系统服务的函数原型(function prototypes for many of the UNIX system services),比如在figure1.4中使用的read和write。

常量STDIN_FILENO和STDOUT_FILENO定义在头文件<unistd.h>中,它们为标准输入和标准输出指定了文件描述符,它们的典型值是0和1,但是为了可移植性(portability),我们将使用这些新名字。
--------------------------------------
/usr/include/unistd.h
/* Standard file descriptors.  */
#define STDIN_FILENO    0       /* Standard input.  */
#define STDOUT_FILENO   1       /* Standard output.  */
#define STDERR_FILENO   2       /* Standard error output.  */
---------------------------------------
section3.9将详细讨论BUFFSIZE常数,说明其各种不同的值将如何影响程序1.4的效率。但是不管该常数的值如何,此程序总能copies any regular file。
read函数返回读得的字节数,此值用作要写的字节数。当到达输入文件的end时,read返回0,程序停止执行(成功执行完毕)。如果发生了一个读错误,read返回-1。大多数系统函数在出错时返回-1。
If we compile the program(figure1.4) into the standard name (a.out) and execute it as
./a.out > data
那么,标准输入是终端,标准输出则重新定向至文件data,标准出错也是终端。如果文件data不存在,则shell默认地会创建它。程序1.4拷贝我们输入的行,然后送向标准输出。直到我们键入文件末尾字符(end-of-file character),通常是Control-D,才会跳出while循环。
如果我们这样执行:
./a.out < infile > outfile
那么文件infile将会被拷贝到文件outfile。(标准输入重定向到文件infile,标准输出重定向到文件outfile,标准出错仍是终端,这就是前面提到的能够copy any regular file。)

标准输入输出(Standard I/O)
标准I/O函数提供了一个面向不用缓存的I/O函数的带缓存的接口(a buffered interface to the unbuffered I/O functions,也就是在上面提到的unbuffered I/O之上建立的接口函数,那么standard I/O背后仍然是调用unbuffered I/O?)。使用标准I/O可无需担心如何选取最佳的缓存长度,例如如何确定figure1.4中的BUFFSIZE常量大小。另一个使用标准I/O函数的优点与处理行输入有关(行输入常常发生在UNIX的应用中)。例如,fgets函数读一完整的行,而另一方面,read函数则是读指定数量的字节。我们将在section5.4中看到,标准I/O库提供了一些函数that let us control the style of buffering used by the library.

我们最熟悉的标准I/O函数是printf。在调用printf的程序中,总是包含头文件<stdio.h>,因为此头文件包括了所有标准I/O函数的原型。

Example
Figure1.5的功能类似于调用read和write的前一个程序figure1.4,5.8节将对Figure1.5作更详细的说明。它将标准输入复制到标准输出,于是也就能复制任一UNIX文件(can copy any regular file)。

Figure 1.5. Copy standard input to standard output, using standard I/O
#include <stdio.h>
#include <iostream>
using namespace std;

int
main(void)
{
    int     c;

    while ((c = getc(stdin)) != EOF)
        if (putc(c, stdout) == EOF)
            cout<<"output error"<<endl;

    if (ferror(stdin))
        cout<<"input error"<<endl;

    exit(0);
}

函数getc一次读1个字符,然后putc将此字符写到标准输出。读完输入的最后1个字节后,getc返回常量EOF(定义在头文件<stdio.h>)。标准I/O常量stdin和stdout也定义在头文件< stdio.h>中,它们分别表示标准输入和标准输出。
-------------------------------------------
/* End of file character.
   Some things throughout the library rely on this being -1.  */
#ifndef EOF
# define EOF (-1)
#endif

 

/* Standard streams.  */
extern struct _IO_FILE *stdin;          /* Standard input stream.  */
extern struct _IO_FILE *stdout;         /* Standard output stream.  */
extern struct _IO_FILE *stderr;         /* Standard error output stream.  */
#ifdef __STDC__
/* C89/C99 say they're macros.  Make them happy.  */
#define stdin stdin
#define stdout stdout
#define stderr stderr
#endif
--------------------------------------------

--------------------------------------------

1.也就是说,可以在任何一个程序里面直接使用文件描述符0,1,2(当然还是直接用STDIN_FILENO,STDOUT_FILENO,STDERR_FILENO 好些,但前提是要#include <unistd.h>,而直接用0,1,2则不用#include <unistd.h>,但是有时候程序仍需要#include <unistd.h>却是由于使用了如read和write这样包含在该头文件中的函数,而不是由于使用0,1,2),而不需要去open来获得;事实上目前我也不知道open哪个文件。
2.标准输入和标准输出的重定向用<和>即可,那么标准出错用什么符号实现重定位?
3.不用缓存的I/O函数才需要指定缓存,标准I/O函数则由函数内部替程序员实现了缓存,再去调用不用缓存的I/O。
4.一些标准I/0函数:fgets,getc,putc,printf,ferror
5.unbuffered I/O functions:<unistd.h>
  STDIN_FILENO
  STDOUT_FILENO
  STDERR_FILENO

  standard I/O functions  :<stdio.h>
  stdin
  stdout
  stderr

--------------------------------------------

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值