C/C++中的输入与输出及如何读取一行文本

在使用C/C++进行编程的过程中,经常会遇到输入输出的问题。

对于C语言中,

1. 格式化输入输出。

在C语言中,最常用的格式化输入输出是scanf和printf函数。

和这两个函数对应的更安全的函数是fscanf和fprintf:指定文件指针

对于字符串的处理还有sscanf和sprintf:指定字符串

声明如下:

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, ...);

 

int scanf(const char *format, ...);

int fscanf(FILE *stream, const char *format, ...);

int sscanf(const char *str, const char *format, ...);

 

2. 字符输入输出

出了这些格式化输入输出函数之外,还有很多针对字符输入输出的函数:

包括getc,getchar,gets,fgetc,fgets,putc,putchar,puts,fputc,fputs。

首先单个字符输入/输出函数对应的声明如下:

int fgetc(FILE *stream); int getc(FILE *stream);

从文件中读入一个字符,返回值就是读入的字符,如果错误返回EOF。

这两个函数具有相同的效果,但是getc是宏,而fgetc是函数。程序员常用这个宏,因为它比调用函数更快。

int fputc(int c,FILE*stream); int putc(int c,FILE*stream);

输出字符到文件。返回输出的字符(转化为int型),如果错误返回EOF。

putc也是宏实现的。

int getchar(void),int putchar(int c)

这两个也是宏实现,主要是从标准输入输出读取或输出字符。等同于getc(stdin)和putc(c,stdout)

其次是多个字符/字符串的输入/输出函数对应的声明:

char *fgets(char *s, int size, FILE *stream);char *gets(char *s);

读入字符串,fgets适合取代gets,因为gets无法限制读取字符的个数。gets是直接从标准输入读取。

fgets从流中读入最多n-1个字符,最后加一个空字符作为字符串结尾标记。如果在读到最大个数的字符之前遇到了一个换行字符或者文件结尾,那么只有目前所读入的字符会被放入到缓冲区中,如果读到换行符'\n',那么此字符也会被放入到缓冲区中。

返回值为读入的字符串,如果出错,返回值为NULL

int fputs(const char *s,FILE*stream); int puts(const char*s);

返回输出的字符的个数,如果出错,返回EOF。

需要注意的是fgets保留换行符'\n',而gets是从stdin输入,在读取字符串时会删除结尾的换行符'\n';

同样,fputs写入时不包括换行符,而puts在写入字符串时会在末尾添加一个换行符。

 

3. 二进制输入输出

对于二进制的输入输出,主要采用fread和fwrite函数,这个比较单一。

声明如下:

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);

其中ptr是缓冲区指针,size是读取的元素的个数,nmemb是元素的大小(sizeof)

返回值是正确读写的字节数,如果出错或达到文件末尾eof,返回一个小的值或0

必须自己判断文件结尾还是出错,分别使用feof函数和ferror函数

 

对于C++语言,

采用的是输入输出流来进行的。由于C++是面向对象语言,所以C++中采用的是流类。

下图是C++的一个类继承方式:

可以看到cin,cout,ifstream,ofstream,istringstream,ostringstream是对应的输入输出类。而fstream和stringstream类是可以同时进行输入和输出。

 

1. cin,ifstream和istringstream都是从istream继承而来,所以,我们可以分析一下istream类的成员函数:

对于istream,可以分为格式化输入和非格式化输入两种形式:

(1)格式化输入

采用的时候对操作符>>的重载。并且操作符>>在对待输入上遇到空格就停止。

(2)非格式化输入

分为对于字符/字符串的输入和对于字节的输入,包括的函数主要有get,getline,read,readsome,peek等。

其中get是获取一个字符,getline是获取一行字符。read和readsome是读入字节。

get函数的声明如下:

int get();

istream& get ( char& c );

istream& get ( char* s, streamsize n );

istream& get ( char* s, streamsize n, char delim );

istream& get ( streambuf& sb);

istream& get ( streambuf& sb, char delim );

可以从输入设备获得一个字符,也可以读取字符串。默认采用'\n'作为分隔符。

使用get函数时,get函数与getline接受的参数相同,解释参数的方式也相同,并且都读取到行尾,但是get不再读取并丢弃换行符,而是将其留在输入队列中。所以经常出问题。由于第一次调用后,换行符留在队列中,因此第二次调用时看到第一个字符边是换行符,因此get认为已经到达队尾,而没有发现任何可读取的内容。如果不借助于帮助,get将不能跨过该换行符。可以采用get的另一种重载的形式来读取这个换行符,然后为下一行的输入做好准备。

但是对于空行get经常出问题。当get读取空行后将设置失效位(failbit),这意味着接下来的输入将被阻断,但可以用下面的命令来恢复输入。

cin.clear();

 

getline函数的声明如下:

istream& getline (char* s, streamsize n );

istream& getline (char* s, streamsize n, char delim );

从输入读入字符串到s中,n是缓冲区最大容量。默认采用'\n'作为分隔符。

 

read和readsome函数的声明如下:

istream& read ( char* s, streamsize n );

streamsize readsome ( char* s, streamsize n );

这两个函数不是很熟悉。之前很少遇到过。

read()方法从缓冲区或设备读取指定长度的字节数,返回对自身的引用.

而readsome()方法只能从缓冲区中读取指定长度字节数,并返回实际已读取的字节数.

比如:

const int LEN = 20;

char chars[ LEN + 1 ] = {0};

ifstream in( fileName );

in.read( chars, LEN );//将文件从设备载入缓冲区,并读取LEN长度.

cout << chars << endl;

in.readsome( chars, LEN );//就可以从缓冲区中读取. 在缓冲区中没有数据时,用readsome()得不到任何数据.

cout << chars << endl;

 

而有时候想要从设备读取指定长度的数据,但又要知道实际读取的长度,这时候就要用另一个方法: gcount()

它返回自上次读取以来所读取的字节数,因此可以这样得到实际读取的长度.

 

int count = 0;

in.read( chars, LEN );

count = in.gcount();

cout << "Read in " << count << " chars : " << chars << endl;

 

实际上,readsome()也是调用read()和gcount()来实现的.

 

C++为了采用string类而引入了一个全局的输入函数getline,其参数是string类型的:

istream& getline ( istream& is, string& str, char delim );

istream& getline ( istream& is, string& str );

这个函数还是非常有用的。为什么会出现这个getline函数呢?原因:cin是istream的一个对象,cin.getline()中的getline()也就是istream的一个成员函数,在string被加入到c++很久以前,istream就已经存在了,所以,参数针对的是数组类型的存储,而没有string。

 

2. cout,ofstream和ostringstream都是从ostream继承而来,所以,我们可以分析一下ostream类的成员函数:

ostream也是分为格式化和非格式化输出,与istream对应的。

(1)格式化输出

采用对操作符<<重载的方式。

(2)非格式化输出

主要包括put函数和write函数两个。put函数输出一个字符,write函数输出字节。

相比输入istream,输出ostream中的相关函数少了很多,其实主要是因为输出相比输入更简单,输出操作符<<可以解决到大部分情况。

put函数

ostream& put ( char c );

write函数

ostream& write ( const char* s , streamsize n );

 

3. 在使用这些函数的时候,一个主要的问题是如何判断输入是否完成,即循环的控制条件该怎么写?

C++中是通过判断输入流的状态来得到输入是否完成或出错的。

下面的就是几个相关的函数:good,eof,fail,bad

(1) good函数

bool good ( ) const;

检查输入流是否良好,可否继续进行输入。主要检查流的3个状态标志:eofbit, failbit 和badbit

(2) eof函数

bool eof ( ) const;

检查eofbit标志。用于测试是否达到文件末尾(EOF)

(3) fail函数

bool fail ( ) const;

检查failbit和badbit。测试是否出现输入错误。

(4) bad函数

bool bad ( ) const;

检查badbit。检查是否出错。

所以在具体的使用中,可以使用good函数来作为循环的判断条件。

 

所以,我们总结一下在C/C++中读取一个字符或字符串可以采用的方式。

1. 首先,读取一个字符:

C语言方式:

(1).采用getchar从stdin输入

while((c=getchar())!=EOF)

putchar(c);

(2). 采用fgetc/getc输入

while((c=fgetc(stdin))!=EOF)

fputc(c,stdout);

while((c=getc(stdin))!=EOF)

putc(c,stdout);

C++语言方式:

(1).采用重载操作符>>

while(cin>>c)

cout<<c<<endl;

这种形式会跳过所有的空白符,包括空格,换行,制表符

(2). 采用get函数

char c;

while(cin.good())

{

c=cin.get();

if(cin.good())//这儿就是判断是否读入了有效的字符

cout<<c;

}

这儿可以读入任何字符。

或者

char c;

while((c=cin.get())!=-1)

cout<<c;

这儿就是直接判断读到的字符,类似C中的思想。

 

2. 其次, 读取一行文本:

C语言方式:

(1). 采用gets函数,从stdin输入

char str[256];

while(gets(str)!=NULL)

puts(str);

(2). 使用比较安全的fgets函数

char str[256];

while(fgets(str,256,stdin)!=NULL)

fputs(str,stdout);

(3). gcc中扩展的函数

int read;

int len=0;

char *line=NULL;

while((read=getline(&line,&len,stdin))!=-1)

printf("%s\n",line);

free(line);

 

C++语言方式:

(1).采用操作符重载>>

string s;

while(cin>>s)

cout<<s<<endl;

这种方式会以空白(空格,换行,制表)为分隔符,不断的读入字符串。

(2). 采用istream类的成员函数getline

char str[256];

while(cin.good()){

cin.getline(str,256);

cout<<str<<endl;

}

char str[256];

while(cin.getline(str,256)){

cout<<str<<endl;

}

(3). 采用全局函数getline

string str;

while(getline(cin,str)){

cout<<str<<endl;

}

 

(4). 采用get函数

char str[265];

while(cin.good()){

cin.get(str,256);

cin.get();

cout<<str<<endl;

}

 

总结来看,如果要读取一行的话,

对于C语言,可以采用fgets函数,或者如果使用linux平台的话,可以采用扩展的getline函数。

注意,这两个函数都是要读入最后的换行符的。

对于C++语言,如果使用C字符串的话,就采用cin.getline()函数,如果采用string型字符串的话,就采用全局函数getline(cin,n);

注意,这两个函数都不读入最后的换行符。

 

这儿有一个关于getline函数的简单的总结:http://www.cnblogs.com/xkfz007/archive/2012/02/27/2363810.html

这儿有一个关于cin中的getline和get函数的比较:http://www.cnblogs.com/xkfz007/archive/2012/04/06/2435251.html

这儿是对C中输入输出函数的一个简单总结:http://www.cnblogs.com/xkfz007/archive/2012/02/27/2363810.html

posted @ 2012-08-02 10:18  Mr.Rico  阅读(27437)  评论(1编辑  收藏  举报