7.1 标準輸入/輸出
最簡單的輸入機制是使用getchar函數從标準輸入中(一般為鍵盤)一次讀取一個字元:
getchar函數在每次被調用時傳回下一個輸入字元.若遇到檔案結尾,則傳回EOF.
在許多環境中,可以使用符号<來實作輸入重定向,它将把鍵盤輸入替換為檔案輸入:如果程式prog中使用了函數getchar,則指令行
将使得程式prog從輸入檔案infile(而不是鍵盤)中讀取字元.如果輸入通過管道機制來自于另一個程式,那麼這種輸入切換也是不可見的.比如: 将運作兩個程式otherprog和prog,并将程式otherprog的标準輸出通過管道重定向到程式prog的标準輸入上.
函數
用于輸出資料.putchar(c)将字元c送至标準輸出上.在預設情況下,标準輸出為螢幕顯示.如果沒有發生錯誤,則函數putchar将傳回輸出的字元;如果發生錯誤,則傳回EOF.當然,我們也可以用重定向字元>: 将把程式prog的輸出從标準輸出裝置重定向到檔案中.
習題7-1:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
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函數
函數printf在輸出格式format的控制下,将其參數進行轉換與格式化,并在标準輸出裝置上列印出來.它的傳回值為列印的字元數.
在字元%和轉換字元中間可能依次包含下列組成部分:
1. 負号:用于指定被轉換的參數按照左對齊的形式輸出
2. 數: 用于指定最小字段寬度.轉換後的參數将列印不小于字段寬度的字段.如果有必要,字段左邊(如果使用左對齊的方式,則為右邊)多餘的字元位置用空格填充以保證最小字段寬.
3. 小數點: 用于将字段寬度和精度分開.
4. 數,用于指定精度,即指定字元串中要列印的最大字元數,浮點數小說點後的位數,整型最少輸出的數字數目
5. 字元h或l,字母h表示将證書作為short類型列印,字母l表示将證書作為long類型列印.
在轉換說明中,寬度或精度可以用星号*表示,這時,寬度或精度的值通過轉換下一參數(必須為int類型)來計算.例如,為了從字元串s中列印最多max個字元,則可以這樣寫:
函數sprintf執行的轉換和函數printf相同,但它将輸出儲存到一個字元串中: 這裡最好保證string的空間夠大:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
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.
标準頭檔案<stdarg.h>中包含一組宏定義,它們對如何周遊參數表進行了定義.
va_list類型用于聲明一個變量,該變量将依次引用各參數.在函數minprintf中,我們将該變量稱為ap,意思是"參數指針".宏va_start将ap初始化為指向第一個無名參數的指針.在使用ap之前,該宏必須被調用一次.參數表必須至少包括一個有名參數,va_start将最後一個有名參數作為起點.
每次調用va_arg,該函數都将傳回一個參數,并将ap指向下一個參數.va_arg使用一個類型名來決定傳回的對象類型,指針移動的步長.最後,必須在函數傳回之前調用va_end,以完成一些必要的清理工作.
#include <stdarg.h>
#include <stdio.h>
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 <stdarg.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
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 <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#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的函數:
将在标準輸出上列印檔案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函數進行操作:
getc函數放回fp指向的輸入流中的下一個字元.如果到達檔案尾或出現錯誤,該函數傳回EOF. 将字元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 <stdio.h>
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 <stdio.h>
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 <stdio.h>
#include <stdlib.h>
#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 <stdio.h>
#include <stdlib.h>
#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
版權聲明:本文為CSDN部落客「weixin_34005042」的原創文章,遵循CC 4.0 BY-SA版權協定,轉載請附上原文出處連結及本聲明。
原文連結:https://blog.csdn.net/weixin_34005042/article/details/91711420