为什么80%的码农都做不了架构师?>>>
7.1 标准输入/输出
最简单的输入机制是使用getchar函数从标准输入中(一般为键盘)一次读取一个字符:
int getchar( void );
getchar函数在每次被调用时返回下一个输入字符.若遇到文件结尾,则返回EOF.
在许多环境中,可以使用符号<来实现输入重定向,它将把键盘输入替换为文件输入:如果程序prog中使用了函数getchar,则命令行
prog
将使得程序prog从输入文件infile(而不是键盘)中读取字符.如果输入通过管道机制来自于另一个程序,那么这种输入切换也是不可见的.比如:
otherprog | prog
将运行两个程序otherprog和prog,并将程序otherprog的标准输出通过管道重定向到程序prog的标准输入上.
函数
int putchar( int )
用于输出数据.putchar(c)将字符c送至标准输出上.在默认情况下,标准输出为屏幕显示.如果没有发生错误,则函数putchar将返回输出的字符;如果发生错误,则返回EOF.当然,我们也可以用重定向字符>:
prog > outfile
将把程序prog的输出从标准输出设备重定向到文件中.
习题7-1:
#include
#include
#include
int main( int argc, char *argv[] )
{
int ch;
if ( strcmp( argv[ 1 ], "lower" ) == 0 ){ //argv[0]是运行程序的名称
while ( ( ch = getchar() ) != EOF ){
putchar( tolower( ch ) );
}
}
else{
while ( ( ch = getchar() ) != EOF ){
putchar( toupper( ch ) );
}
}
}
程序输出:
7.2 格式化输出----printf函数
int printf( char *format, arg1, arg2,...)
函数printf在输出格式format的控制下,将其参数进行转换与格式化,并在标准输出设备上打印出来.它的返回值为打印的字符数.
在字符%和转换字符中间可能依次包含下列组成部分:
1. 负号:用于指定被转换的参数按照左对齐的形式输出
2. 数: 用于指定最小字段宽度.转换后的参数将打印不小于字段宽度的字段.如果有必要,字段左边(如果使用左对齐的方式,则为右边)多余的字符位置用空格填充以保证最小字段宽.
3. 小数点: 用于将字段宽度和精度分开.
4. 数,用于指定精度,即指定字符串中要打印的最大字符数,浮点数小说点后的位数,整型最少输出的数字数目
5. 字符h或l,字母h表示将证书作为short类型打印,字母l表示将证书作为long类型打印.
在转换说明中,宽度或精度可以用星号*表示,这时,宽度或精度的值通过转换下一参数(必须为int类型)来计算.例如,为了从字符串s中打印最多max个字符,则可以这样写:
printf("%.*s", max, s );
函数sprintf执行的转换和函数printf相同,但它将输出保存到一个字符串中:
int sprintf( char *string, char *format, arg1, arg2,...)
这里最好保证string的空间够大:
#include
#include
#include
#include
int main( void )
{
char str[10];
memset( str, 0, 10 );
sprintf(str, "%s%s", "hello", "--world" );
printf("%s\n", str );
return 0;
}
运行这个程序,则会发现:错误.........
7.3 变长参数表
我们编写一个类似printf函数的minprintf.
标准头文件
va_list类型用于声明一个变量,该变量将依次引用各参数.在函数minprintf中,我们将该变量称为ap,意思是"参数指针".宏va_start将ap初始化为指向第一个无名参数的指针.在使用ap之前,该宏必须被调用一次.参数表必须至少包括一个有名参数,va_start将最后一个有名参数作为起点.
每次调用va_arg,该函数都将返回一个参数,并将ap指向下一个参数.va_arg使用一个类型名来决定返回的对象类型,指针移动的步长.最后,必须在函数返回之前调用va_end,以完成一些必要的清理工作.
#include
#include
void minprintf( char *fmt, ...)
{
va_list ap;
char *p, *sval;
int ival;
double dval;
va_start( ap, fmt );
for ( p = fmt; *p; p++ ){
if ( *p != '%' ){
putchar( *p );
continue;
}
switch( *++p ){
case 'd':
ival = va_arg( ap, int );
printf("%d", ival );
break;
case 'f':
dval = va_arg( ap, double );
printf("%f", dval );
break;
case 's':
for ( sval = va_arg( ap, char * ); *sval; sval++ ){
putchar( *sval );
}
break;
default:
putchar( *p );
break;
}
}
va_end( ap );
}
int main( void )
{
minprintf("%d %f %s\n", 12, 12.345, "hello world" );
return 0;
}
程序输出:
习题7-3:
还是冒昧的用到了printf来指定输出:
#include
#include
#include
#include
#include
#include
void minprintf( char *fmt, ...)
{
va_list ap;
char *p, *sval;
int ival;
double dval;
int isLeft = 0; //是否左对齐
int width = 0; //宽度
int precision = 0; //精度
int isDot = 0; //判断存在小数点
va_start( ap, fmt );
for ( p = fmt; *p; p++ ){
if ( *p != '%' ){
putchar( *p );
continue;
}
if ( *++p ){
if ( '-' == *p ){
isLeft = 1;
p++;
}
if ( isdigit( *p ) ){
width = *p - '0';
p++;
}
if ( '.' == *p ){
p++;
isDot = 1;
}
if ( isdigit( *p ) ){
precision = *p - '0';
p++;
}
if ( 'd' == *p ){
ival = va_arg( ap, int );
if ( isLeft ){
printf( "%-*.*d", width, precision, ival );
}
else{
printf( "%*.*d", width, precision, ival );
}
}
else if ( 'f' == *p ){
dval = va_arg( ap, double );
if ( isLeft ){
if ( precision ){
printf( "%-*.*f", width, precision, dval );
}
else{
if ( isDot ){
printf("%-*.f", width, dval );
}
else{
printf("%-f", dval );
}
}
}
else{
if ( precision ){
printf( "%*.*f", width, precision, dval );
}
else{
if ( isDot ){
printf("%*.f", width, dval );
}
else{
printf("%f", dval );
}
}
}
}
else if ( 's' == *p ){
sval = va_arg( ap, char * );
if ( isLeft ){
if ( precision ){
printf("%-*.*s", width, precision, sval );
}
else{
if ( isDot ){
printf("%-*.s", width, sval );
}
else{
printf("%-s", sval );
}
}
}
else{
if ( precision ){
printf("%*.*s", width, precision, sval );
}
else{
if ( isDot ){
printf("%*.s", width, sval );
}
else{
printf("%s", sval );
}
}
}
}
else{
putchar( *p );
isLeft = 0;
width = 0;
precision = 0;
}
}
}
va_end( ap );
}
int main( void )
{
minprintf("%d\n", 123 );
minprintf("%-9.8d\n", 1234567 );
minprintf("%-9.8f\n", 1234.5678 );
minprintf("%-.8f\n", 1234.5678 );
minprintf("%s\n", "hello world" );
minprintf("%.5s\n", "hello world" );
return 0;
}
程序输出:
7.4 格式化输入---scanf函数
这道题的精髓在于:把地址进行传递:
#include
#include
#include
#include
#include
#define MAXLINE 128
char *getword( char *line, char *word )
{
int i = 0;
while ( !isalnum( *line ) ){
line++;
}
while ( isalnum( *line ) ){
word[ i++ ] = *line++;
}
if ( '.' == *line ){
word[ i++ ] = '.';
line++;
}
while ( isalnum( *line ) ){
word[ i++ ] = *line++;
}
while ( isspace( *line ) && '\n' != *line ){
line++;
}
word[ i ] = '\0';
return line;
}
void minscanf( char *fmt, ...)
{
va_list ap;
char *p;
int index = 0;
int *pint = NULL;
double *pdouble = NULL;
char **pstr = NULL;
char *line = ( char * )malloc( sizeof( char ) * MAXLINE );
char *word = ( char * )malloc( sizeof( char ) * MAXLINE );
char *wordArr[ MAXLINE ];
memset( line, 0, MAXLINE );
memset( word, 0, MAXLINE );
memset( wordArr, 0, MAXLINE );
if ( NULL != fgets( line, MAXLINE, stdin ) ){
while ( ( *line != '\n' ) && ( line = getword( line, word ) ) ){
int i = 0;
char *tempWord = ( char * )malloc( sizeof( char ) * MAXLINE );
memset( tempWord, 0, MAXLINE );
while ( '\0' != *word ){
tempWord[ i++ ] = *word++;
}
tempWord[ i ] = '\0';
wordArr[ index++ ] = tempWord;
}
}
index = 0;
va_start( ap, fmt );
for ( p = fmt; *p; p++ ){
if ( *p != '%' ){
continue;
}
switch ( *++p ){
case 'd':
pint = va_arg( ap, int * );
*pint = atoi( wordArr[ index++ ] );
break;
case 'f':
pdouble = va_arg( ap, double * );
*pdouble = atof( wordArr[ index++ ] );
break;
case 's':
pstr = va_arg( ap, char ** );
*pstr = wordArr[ index++ ];
break;
default:
break;
}
}
va_end( ap );
}
int main( void )
{
int iValue;
double dValue;
char *sValue;
minscanf("%d%f%s", &iValue, &dValue, &sValue );
printf("%d\n", iValue );
printf("%f\n", dValue );
printf("%s\n", sValue );
return 0;
}
程序输出:
我稍微看了下答案,发现答案把问题复杂化了,完全按照书上的思想来写...其实有点误人子弟的意思.
习题7-5:
对于这道习题,我的想法是通过getline来读取一行,然后通过sscanf来把需要计算的数分别进行存放,但是用sscanf这类操作后,程序变复杂了,因为最终都要用到堆栈的思想,就是我们通过sscanf把书提取出来,最后还是放在堆栈中----而之前写过直接从getline读取后放在堆栈中,故这道题就不做了.
7.5 文件访问
假设我们写个类似于unix系统中cat的函数:
cat x.c y.c
将在标准输出上打印文件x.c和y.c的内容.
FILE *fp;
FILE *fopen(char *name, char *mode );
fp = fopen( name, mode );
fp是一个指向结构FILE的指针,并且,fopen函数返回一个指向结构FILE的指针,该指针成为"文件指针",它指向一个包含文件信息的结构,这些信息包括:缓冲区的位置,缓冲区中当前字符的位置,文件的读或写状态,是否出错或是否已经到达文件结尾等等.
fp的第一个参数是一个字符串,包含文件名.第二个参数是访问模式,为"r","w","a".如果为二进制文件,则在访问模式后面增加"b".如果打开错误,放回NULL.
当文件打开的时候,我们需要通过getc和putc函数进行操作:
int getc( FILE *fp );
getc函数放回fp指向的输入流中的下一个字符.如果到达文件尾或出现错误,该函数返回EOF.
int putc( int c, FILE *fp );
将字符c写入到fp指向的文件中,并返回写入的字符.如果发生错误,则返回EOF.
启动一个C语言程序时,操作系统环境负责打开3个文件,并将这3个文件的指针提供给该程序.这3个文件分别是标准输入,标准输出和标准错误,相应的文件指针分别是stdin,stdout和stderr.我们可以将stdin和stdout重定向到文件或管道.
这样的话,getchar和putchar函数可以通过getc,putc,stdin,stdout定义如下:
#define getchar() getc( stdin )
#define putchar( c ) putc( ( c ), stdout )
对于文件的格式化输入或输出,可以使用函数fscanf和fprintf.它们与scanf和printf函数的区别仅仅在于它们的第一个参数是一个指向所要读写的文件的指针,第二个参数是格式串.
int fscanf( FILE *fp, char *format, ...)
int fprintf( FILE *fp, char *format, ...)
这样的话,我们就可以写出cat函数了:
#include
int main( int argc, char *argv[] )
{
FILE *fp;
void filecopy( FILE *, FILE * );
if ( argc == 1 ){
filecopy( stdin, stdout );
}
else{
while ( --argc > 0 ){
if ( ( fp = fopen( *++argv, "r" ) ) == NULL ){
printf(" cat: can't open %s\n", *argv );
return 1;
}
else{
filecopy( fp, stdout );
fclose( fp );
}
}
}
return 0;
}
void filecopy( FILE *ifp, FILE *ofp )
{
int c;
while ( ( c = getc( ifp ) ) != EOF ){
putc( c, ofp );
}
}
不过,在window下运行的时候,提示无法打开x.c,貌似权限不够.
7.6 错误处理
用一个程序说明错误处理:
#include
int main( int argc, char *argv[] )
{
FILE *fp;
void filecopy( FILE *, FILE * );
char *prog = argv[ 0 ];
if ( argc == 1 ){
filecopy( stdin, stdout );
}
else{
while ( --argc > 0 ){
if ( ( fp = fopen( *++argv, "r" ) ) == NULL ){
fprintf( stderr, "%s: can't open %s\n", prog, *argv );
exit( 1 );
}
else{
filecopy( fp, stdout );
fclose( fp );
}
}
}
if ( ferror( stdout ) ){
fprintf( stderr, "%s:error writing stdout\n", prog );
exit( 2 );
}
exit( 0 );
}
void filecopy( FILE *ifp, FILE *ofp )
{
int c;
while ( ( c = getc( ifp ) ) != EOF ){
putc( c, ofp );
}
}
7.7 行输入和行输出
我们来看fgets和fputs的源代码即可:
char *fgets( char *s, int n, FILE *iop )
{
register int c;
register char *cs;
cs = s;
while ( --n > 0 && ( c = getc( iop ) ) != EOF ){
if ( ( *cs++ = c ) == '\n' ){
break;
}
}
*cs = '\0';
return ( c == EOF && cs == s ) ? NULL : s;
}
int fputs( char *s, FILE *iop )
{
int c;
while ( c = *s++ ){
putc( c, iop );
}
return ferror( iop ) ? EOF : 非负值;
}
习题7-6:
#include
#include
#define MAXLINE 128
int main( void )
{
char line1[ MAXLINE ];
char line2[ MAXLINE ];
FILE *fp1 = fopen( "x.txt", "r" );
FILE *fp2 = fopen( "y.txt", "r" );
memset( line1, 0, MAXLINE );
memset( line2, 0, MAXLINE );
if ( NULL == fp1 || NULL == fp2 ){
perror("file open error");
exit( 1 );
}
while ( NULL != fgets( line1, MAXLINE, fp1 ) && NULL != fgets( line2, MAXLINE, fp2 ) ){
if ( strcmp( line1, line2 ) != 0 ){
printf("line1 is:\n%s\nline2 is:\n%s\n", line1, line2 );
break;
}
}
return 0;
}
程序输出:
习题7-8(不知道如何组织打印文件,故把答案粘贴出来.PS:这道题和cat类似而已):
#include
#include
#define MAXBOT 3 /* maximum # lines at bottom page */
#define MAXHDR 5 /* maximum # lines at head of page */
#define MAXLINE 100 /* maximum size of one line */
#define MAXPAGE 66 /* maximum # lines on one page */
int main( int argc, char *argv[] )
{
FILE *fp;
void fileprint( FILE *fp, char *fname );
if ( argc == 1 ){
fileprint( stdin, " " );
}
else{
while ( --argc > 0 ){
if ( ( fp = fopen( *++argv, "r" ) ) == NULL ){
fprintf( stderr, "print:can't open %s\n", *argv );
exit( 1 );
}
else{
fileprint( fp, *argv );
fclose( fp );
}
}
}
return 0;
}
void fileprint( FILE *fp, char *fname )
{
int lineno, pageno = 1;
char line[ MAXLINE ];
int heading( char *fname, int pageno );
lineno = heading( fname, pageno++ );
while ( fgets( line, MAXLINE, fp ) != NULL ){
if ( lineno == 1 ){
fprintf( stdout, "\f" );
lineno = heading( fname, pageno++ );
}
fputs( line, stdout );
if ( ++lineno > MAXPAGE - MAXBOT ){
lineno = 1;
}
}
fprintf( stdout, "\f");
}
int heading( char *fname, int pageno )
{
int ln = 3;
fprintf( stdout, "\n\n" );
fprintf( stdout, "%s page %d\n", fname, pageno );
while ( ln++ < MAXHDR ){
fprintf( stdout, "\n" );
}
return ln;
}
习题7-9:
int isupper( char c )
{
if ( c >= 'A' && c <= 'Z' ){
return 1;
}
return 0;
}
#define isupper( c ) ( ( c ) >= 'A' && ( c ) <= 'Z' ) ? 1 : 0