小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

小話c語言(4)

 londonKu 2012-05-13

開頭將文章中代碼所在的環(huán)境介紹下:

[Mac-10.7.1 Lion Intel-based]


Q: 看到stdio.h中有這么多關(guān)于輸入或者輸出的函數(shù),怎么會這么多?

A: 其實(shí)基本的函數(shù)不多,不過從易于上層使用的角度,又被封裝出很多特定情況的函數(shù)接口,所以才顯得很多。


Q: 比如關(guān)于字符操作的函數(shù)有g(shù)etc, fgetc, getch, getche還有g(shù)etchar,它們有什么區(qū)別嗎?

A: 一個重要的區(qū)別是getc, fgetc, getchar是標(biāo)準(zhǔn)函數(shù),而getch和getche不是標(biāo)準(zhǔn)規(guī)定的。不是標(biāo)準(zhǔn),就意味著它的實(shí)現(xiàn)可能在不同平臺有不一樣的表現(xiàn)。正如getch一樣,windows平臺被包含在conio.h頭文件中,運(yùn)行到getch,可能需要等待輸入,當(dāng)有輸入時就會運(yùn)行過去不需要再按回車; getche在windows下和getch類似,不過多了最后的字母e,它表示echo,即在輸入后會回顯輸入的字符。不過它們在mac平臺的表現(xiàn)卻不大一樣,這里不做具體介紹。

現(xiàn)在介紹下getc, fgetc和getchar.用man getc得到如下信息:


可以發(fā)現(xiàn),getc基本等同于fgetc,getchar是getc的特殊形式,是從標(biāo)準(zhǔn)輸入獲取字符。


Q: getch和getche接收了輸入即可繼續(xù)運(yùn)行,而getchar函數(shù)當(dāng)緩沖區(qū)中沒有數(shù)據(jù)時,需要輸入字符最終回車才能繼續(xù)執(zhí)行,這是不是意味著getch和getche是不帶緩沖的,而getc, fgetc和getchar是帶緩沖的?

A: 是的。按照標(biāo)準(zhǔn)來說,getc和fgetc在不涉及交互的時候是全緩沖模式(在緩沖區(qū)大小滿后提交數(shù)據(jù)),如果是涉及到交互(如標(biāo)準(zhǔn)輸入),那么是行緩沖;而getchar就行緩沖模式(回車會提交輸入緩沖區(qū)數(shù)據(jù),不過如果緩沖區(qū)已經(jīng)有數(shù)據(jù)了就不需要等待回車了).


Q: 既然fgetc是接收輸入的字符,返回值用char或者unsigned char不就行了,為什么用int呢?

A: 這個主要是因?yàn)槲募Y(jié)束或者讀寫文件出錯的標(biāo)志被規(guī)定成EOF,也就是-1導(dǎo)致的。unsigned char根本取不到-1這個值,而如果用char做返回值的話,它無法分辨0xFF字符和EOF,因?yàn)檫@兩個數(shù)值都被char認(rèn)為是-1,所以它也不能作為返回值。

舉個例子吧:

使用如下代碼向一個文件中只寫入0xFF字符,然后保存:

  1. #include <stdio.h>  
  2.   
  3. int main (int argc, const char * argv[])  
  4. {    
  5.     FILE *fp = fopen("test""w");  
  6.     if(fp)  
  7.     {  
  8.         fputc(0xFF, fp);  
  9.         fclose(fp);  
  10.     }  
  11.       
  12.     return 0;  
  13. }  

運(yùn)行完,用hexdump確認(rèn)此文件中的數(shù)據(jù)為0xFF:


然后使用如下代碼讀取test文件中的數(shù)據(jù),并打印出來:

  1. #include <stdio.h>  
  2.   
  3. int main (int argc, const char * argv[])  
  4. {    
  5.     FILE *fp = fopen("test""r");  
  6.     if(fp)  
  7.     {  
  8.         char ch;  
  9.   
  10.         printf("open test ok\n");  
  11.         while((ch = fgetc(fp)) != EOF)  
  12.         {  
  13.             printf("%d", ch);  
  14.         }  
  15.   
  16.         printf("read test end\n");  
  17.         fclose(fp);  
  18.     }  
  19.       
  20.     return 0;  
  21. }  

運(yùn)行后可以發(fā)現(xiàn)輸出結(jié)果如下,這就意味著fgetc執(zhí)行是遇到0xFF時被當(dāng)做了EOF而導(dǎo)致結(jié)束了。


Q: 上面提到getchar等同于getc(stdin), stdin到底是什么?

A: [Mac-10.7.1 Lion Intel-based]

stdio頭文件中:

  1. #define stdin   __stdinp  
  2. #define stdout  __stdoutp  
  3. #define stderr  __stderrp  
  1. extern FILE *__stdinp;  
  2. extern FILE *__stdoutp;  
  3. extern FILE *__stderrp;  

可以看到,它們只是FILE *類型的一個變量,對于任何一個應(yīng)用程序,使用c運(yùn)行時庫都會默認(rèn)自動為應(yīng)用程序創(chuàng)建這3個文件句柄。


Q: 還有關(guān)于字符串輸入的gets函數(shù),聽說使用它進(jìn)行輸入有風(fēng)險的,如何表現(xiàn)的?

A: 因?yàn)樗鼪]有對于輸入數(shù)據(jù)大小進(jìn)行確定,也就是可能導(dǎo)致輸入的數(shù)據(jù)過多覆蓋了不該覆蓋的數(shù)據(jù)導(dǎo)致出問題。

舉個例子:

  1. #include <stdio.h>  
  2.   
  3. #define PRINT_CH(ch)    printf(#ch" is %c\n", (ch));  
  4. #define PRINT_STR(str)  printf(#str" is %s\n", (str));  
  5.   
  6. int main (int argc, const char * argv[])  
  7. {    
  8.     char ch = 'a';  
  9.     char buf[4];  
  10.     char *ret = gets(buf);  
  11.     if(ret != NULL)  
  12.     {  
  13.         PRINT_STR(ret)  
  14.     }  
  15.     PRINT_CH(ch)  
  16.       
  17.     return 0;  
  18. }  

運(yùn)行:

如果輸入3個字符hel后回車,得到的結(jié)果是正常的:


如果輸入了4個字符hell后回車,結(jié)果如下:

可以看到ch字符的數(shù)據(jù)已經(jīng)不正常了;


如果輸入5個字符hello后回車,結(jié)果如下:

可以看到ch字符已經(jīng)被覆蓋為hello中的o了。這和剛剛輸入4個字符的方式都是發(fā)生了緩沖區(qū)溢出,也就是超過了緩沖區(qū)buf的大小,覆蓋了其它數(shù)據(jù)。

至于ch為什么是字符o, 這需要明白:ch和buf都被保存在棧中,且ch和buf相鄰,如果棧是從高到低,那么ch保存在高地址,數(shù)據(jù)hello將前4個字節(jié)保存在buf中,最后一個o就被保存在了ch所在的地址區(qū)域里面。


Q: 既然這樣,那么使用什么可以更好地避免緩沖區(qū)溢出呢?

A: 可以使用fgets函數(shù),它的一個參數(shù)就是緩沖區(qū)大小。

  1. char *fgets(char * restrict str, int size, FILE * restrict stream);  

如果讀取成功,函數(shù)返回讀取字符串的指針;

如果開始讀取直接遇到文件結(jié)尾,返回NULL,緩沖區(qū)數(shù)據(jù)和原來保持一致;

如果讀取出錯,返回NULL, 緩沖區(qū)數(shù)據(jù)不確定;


Q: fgets如果返回NULL,可能是直接遇到文件結(jié)束或者獲取出錯,怎么區(qū)分呢?

A: 這就需要如下兩個函數(shù)了:feof和ferror.

  1. int  feof(FILE *stream);  
  1. int  ferror(FILE *stream);  

如果遇到文件尾,那么feof返回非0的數(shù)值,否則返回0.

如果遇到文件操作錯誤,那么ferror返回非0的數(shù)值,否則返回0.


Q: 做一個測試吧,從一個文件中不斷讀取數(shù)據(jù),然后打印出來。

A: 首先,先寫個腳本,向一個文件中寫入100個字符,為依次重復(fù)10次0123456789.

  1. #!/bin/bash  
  2.   
  3. i=0  
  4. j=0  
  5.   
  6. while [ $i -lt 100 ];  
  7. do  
  8.     let "j = i % 10"  
  9.     echo -n $j >> numbers  
  10.     let "i = i + 1"  
  11. done  

執(zhí)行它,會在同目錄下找到一個numbers的文件,使用vi打開確認(rèn)一下內(nèi)容:

現(xiàn)在使用fgets函數(shù)將此文件的數(shù)據(jù)讀出來:

  1. #include <stdio.h>  
  2. #include <string.h>  
  3.   
  4. #define PRINT_CH(ch)    printf(#ch" is %c\n", (ch));  
  5. #define PRINT_STR(str)  printf(#str" is %s\n", (str));  
  6.   
  7. int main (int argc, const char * argv[])  
  8. {    
  9.     FILE *fp = fopen("numbers""r");  
  10.     if(fp)  
  11.     {  
  12.         char buf[11] = {0};  
  13.         while(fgets(buf, sizeof(buf), fp) != NULL)  
  14.         {  
  15.             if(!ferror(fp))  
  16.                 PRINT_STR(buf)  
  17.             else  
  18.                 PRINT_STR("ferror happens...")  
  19.               
  20.             memset(buf, 0, sizeof(buf));  
  21.         }  
  22.         fclose(fp);  
  23.     }  
  24.   
  25.     return 0;  
  26. }  

這里,為了分便顯示結(jié)果,緩沖區(qū)被設(shè)置大小為11,最后一個字節(jié)保存結(jié)束符\0, 前10個字符保存0~9.

運(yùn)行結(jié)果:


Q: fgets會自動在緩沖區(qū)結(jié)尾加上\0作為結(jié)束符是吧?

A: 是的。這和strcpy在這個方面是一致的。不過,fgets也有自己的特點(diǎn),正如gets一樣,遇到回車符,它會提前結(jié)束;如果遇到回車符,而且緩沖區(qū)還沒填滿,那么回車符會被填充到緩沖區(qū)中,最后再加上\0作為結(jié)束符。

看個例子:

  1. #include <stdio.h>  
  2. #include <string.h>  
  3.   
  4. #define PRINT_D(intValue)   printf(#intValue" is %lu\n", (intValue));  
  5. #define PRINT_CH(ch)        printf(#ch" is %c\n", (ch));  
  6. #define PRINT_STR(str)      printf(#str" is %s\n", (str));  
  7.   
  8. int main (int argc, const char * argv[])  
  9. {    
  10.     char buf[3];  
  11.     char *ret = fgets(buf, sizeof(buf), stdin);  
  12.     if(ret)  
  13.     {  
  14.         PRINT_D(strlen(buf))  
  15.         PRINT_STR(buf)  
  16.     }  
  17.       
  18.     return 0;  
  19. }  

運(yùn)行:

輸入h然后回車, 結(jié)果:

可以發(fā)現(xiàn)回車字符被保存在了buf中.它和gets在這方面不一致,這是需要注意的。


Q: 如果不希望到換行就結(jié)束,也可以指定緩沖區(qū)大小,該怎么辦?

A: 可以使用fread(不過它是適用于二進(jìn)制讀的函數(shù),用于文本方式可能在某些情況下出錯,需要小心).

  1. size_t  fread(void *restrict ptr, size_t size, size_t nitems, FILE *restrict stream);  

ptr: 表示讀入數(shù)據(jù)保存的地址;

size: 表示每組數(shù)據(jù)的大?。?/p>

nitems: 表示讀入數(shù)據(jù)的組數(shù);

stream: 表示文件指針;

返回值: 成功讀取數(shù)據(jù)的組數(shù);如果返回NULL,可能是直接遇到文件尾或者讀取出錯,需要用feof和ferror來區(qū)分;當(dāng)然,如果讀取不成功,返回值會比傳入的參數(shù)nitems要小。

這個函數(shù)使用的時候要注意:正因?yàn)樗怯糜诙M(jìn)制讀的,它不會在讀取ok后自動為緩沖區(qū)最后加上\0作為結(jié)束符,這個要小心;如果第二個和第三個參數(shù)互換了,那么返回值也會發(fā)生相應(yīng)改變,這也需要小心。

舉個例子吧:

  1. #include <stdio.h>  
  2. #include <string.h>  
  3.   
  4. #define PRINT_D(intValue)   printf(#intValue" is %lu\n", (intValue));  
  5. #define PRINT_CH(ch)        printf(#ch" is %c\n", (ch));  
  6. #define PRINT_STR(str)      printf(#str" is %s\n", (str));  
  7.   
  8. int main (int argc, const char * argv[])  
  9. {    
  10.     char buf[3] = {0};  
  11.     size_t ret = fread(buf, sizeof(buf) - 1, 1, stdin);  
  12.     if(ret == 1)  
  13.     {  
  14.         PRINT_STR(buf)  
  15.     }  
  16.       
  17.     return 0;  
  18. }  

可以看到,開始就將buf初始化為0了,fread的第二個參數(shù)是buf總大小減去1的數(shù)值,因?yàn)楹竺姘炎x取的數(shù)據(jù)當(dāng)成char *字符串,最后一個字節(jié)需要保存為\0.

第三個參數(shù)為1表示1組數(shù)組,第二個參數(shù)表示每組數(shù)據(jù)大小為sizeof(buf) - 1. 最后一個參數(shù)表示從標(biāo)準(zhǔn)輸入stdin讀取。

需要注意:正如之前說過,文件讀寫為交互設(shè)備時,是屬于行緩沖,需要輸入回車來提交緩沖區(qū)。

運(yùn)行:

輸入he并回車,可以看到打印了he.

如果輸入h并回車,那么效果如下:

可以發(fā)現(xiàn)回車字符被保存在了buf中。

在這里不用太小心,如果輸入多余2個字符,不會發(fā)生緩沖區(qū)溢出,因?yàn)閒read有參數(shù)已經(jīng)標(biāo)志了緩沖區(qū)大小。


Q: 上面是關(guān)于文件輸入,文件輸出的fputc, putc, putchar是不是也和fgetc, getc, getchar的關(guān)系類似?

A: 是的,使用man putc可以看到它們之間的關(guān)系。


Q: 標(biāo)準(zhǔn)中也沒有putch和putche么?

A: 是的,實(shí)際上在mac系統(tǒng)默認(rèn)下也沒發(fā)現(xiàn)它們的非標(biāo)準(zhǔn)實(shí)現(xiàn)。


Q: 字符串輸出的fputs和puts有什么區(qū)別么?

A: 

可以看到,fputs將字符串向指定文件輸出,但是并不會自動加上換行字符;而puts會自動加上換行字符。

另外,fputs返回EOF表示操作失敗,返回非負(fù)數(shù)表示成功;所以判斷fputs是否成功最好使用if(fputs(xxxx) != EOF)或者if(fputs(xxx) == EOF)來表示。

如下代碼:

  1. #include <stdio.h>  
  2. #include <string.h>  
  3.   
  4. #define PRINT_D(intValue)   printf(#intValue" is %lu\n", (intValue));  
  5. #define PRINT_CH(ch)        printf(#ch" is %c\n", (ch));  
  6. #define PRINT_STR(str)      printf(#str" is %s\n", (str));  
  7.   
  8. int main (int argc, const char * argv[])  
  9. {    
  10.     int ret = fputs("hello", stdout);  
  11.     if(ret != EOF)  
  12.     {  
  13.         PRINT_D(ret)  
  14.     }  
  15.       
  16.     return 0;  
  17. }  

執(zhí)行:

可以看到,輸出的hello后面并沒有換行。

puts函數(shù)返回值同fputs.

puts的使用:

  1. #include <stdio.h>  
  2. #include <string.h>  
  3.   
  4. #define PRINT_D(intValue)   printf(#intValue" is %lu\n", (intValue));  
  5. #define PRINT_CH(ch)        printf(#ch" is %c\n", (ch));  
  6. #define PRINT_STR(str)      printf(#str" is %s\n", (str));  
  7.   
  8. int main (int argc, const char * argv[])  
  9. {    
  10.     int ret = puts("hello");  
  11.     if(ret != EOF)  
  12.     {  
  13.         PRINT_D(ret)  
  14.     }  
  15.       
  16.     return 0;  
  17. }  

運(yùn)行結(jié)果:

可以看到puts輸出后自動加了換行。


Q: 文件輸入和輸出可以像printf和scanf一樣有格式化操作么?

A: 當(dāng)然可以了,printf和scanf只是文件輸入輸出的一個特例而已。

fprintf函數(shù)和fscanf函數(shù)可以實(shí)現(xiàn)此功能。

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

可以看到,它們和printf和scanf很相似,除了第一個參數(shù)表示文件指針,對于printf也就是fprintf(stdout, xxx)的特例;scanf也就是fscanf(stdin, xxx)的特例。

不過,fprintf還可以用文件指針stderr表示錯誤輸出,它是不帶緩沖的輸出。

fprintf的返回值表示成功輸出的字符字節(jié)個數(shù),printf的返回值和它一致。

fscanf返回值表示成功輸入的變量個數(shù)。

這里不做更多說明了。


Q: 一直提到的文件句柄,它到底是什么?

A: 

  1. typedef struct __sFILE {  
  2.     unsigned char *_p;  /* current position in (some) buffer */  
  3.     int _r;     /* read space left for getc() */  
  4.     int _w;     /* write space left for putc() */  
  5.     short   _flags;     /* flags, below; this FILE is free if 0 */  
  6.     short   _file;      /* fileno, if Unix descriptor, else -1 */  
  7.     struct  __sbuf _bf; /* the buffer (at least 1 byte, if !NULL) */  
  8.     int _lbfsize;   /* 0 or -_bf._size, for inline putc */  
  9.   
  10.     /* operations */  
  11.     void    *_cookie;   /* cookie passed to io functions */  
  12.     int (*_close)(void *);  
  13.     int (*_read) (void *, char *, int);  
  14.     fpos_t  (*_seek) (void *, fpos_tint);  
  15.     int (*_write)(void *, const char *, int);  
  16.   
  17.     /* separate buffer for long sequences of ungetc() */  
  18.     struct  __sbuf _ub; /* ungetc buffer */  
  19.     struct __sFILEX *_extra; /* additions to FILE to not break ABI */  
  20.     int _ur;        /* saved _r when _r is counting ungetc data */  
  21.   
  22.     /* tricks to meet minimum requirements even when malloc() fails */  
  23.     unsigned char _ubuf[3]; /* guarantee an ungetc() buffer */  
  24.     unsigned char _nbuf[1]; /* guarantee a getc() buffer */  
  25.   
  26.     /* separate buffer for fgetln() when line crosses buffer boundary */  
  27.     struct  __sbuf _lb; /* buffer for fgetln() */  
  28.   
  29.     /* Unix stdio files get aligned to block boundaries on fseek() */  
  30.     int _blksize;   /* stat.st_blksize (may be != _bf._size) */  
  31.     fpos_t  _offset;    /* current lseek offset (see WARNING) */  
  32. FILE;  

可以看到它封裝了操作文件的緩沖區(qū)、當(dāng)前位置等信息,這也能很好地體現(xiàn)帶緩沖的事實(shí)。比如,fopen函數(shù),它和open函數(shù)的一大區(qū)別就是是否使用用戶層緩沖。


Q: fopen和open的關(guān)系是什么樣子的?

A: open是POSIX標(biāo)準(zhǔn),也是基于Unix系統(tǒng)的系統(tǒng)調(diào)用。fopen是C語言標(biāo)準(zhǔn),它的內(nèi)部當(dāng)然必須調(diào)用open系統(tǒng)調(diào)用才能完成真正功能。


Q: 給個使用open, read, write, close函數(shù)的例子吧。

A: 

首先使用echo命令創(chuàng)建一個內(nèi)容為hello的文件,文件名為test.

  1. echo hello > test  

然后編寫如下代碼:

  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #include <fcntl.h>  
  4. #include <unistd.h>  
  5.   
  6. #define PRINT_D(intValue)   printf(#intValue" is %lu\n", (intValue));  
  7. #define PRINT_CH(ch)        printf(#ch" is %c\n", (ch));  
  8. #define PRINT_STR(str)      printf(#str" is %s\n", (str));  
  9.   
  10. int main (int argc, const char * argv[])  
  11. {  
  12.     char buf[32] = {0};  
  13.     ssize_t ret;  
  14.     int file = open("test", O_RDONLY);  
  15.   
  16.     if(file < 0)  
  17.     {  
  18.         perror("open file error");  
  19.         return -1;  
  20.     }  
  21.     ret = read(file, buf, sizeof(buf));  
  22.     if(ret > 0)  
  23.         PRINT_STR(buf)  
  24.           
  25.     close(file);  
  26.       
  27.     return 0;  
  28. }  

運(yùn)行,結(jié)果為:


Q: 我想在文件開頭插入一個字符,用如下的代碼怎么不行?

  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #include <fcntl.h>  
  4. #include <unistd.h>  
  5.   
  6. #define PRINT_D(intValue)   printf(#intValue" is %lu\n", (intValue));  
  7. #define PRINT_CH(ch)        printf(#ch" is %c\n", (ch));  
  8. #define PRINT_STR(str)      printf(#str" is %s\n", (str));  
  9.   
  10. int main (int argc, const char * argv[])  
  11. {  
  12.     FILE *fp = fopen("test""r+");  
  13.     if(!fp)  
  14.     {  
  15.         perror("fopen error");  
  16.         return -1;  
  17.     }  
  18.     fseek(fp, 0, SEEK_SET);  
  19.     if(fputc('a', fp) == EOF)  
  20.         perror("fputc error");  
  21.       
  22.     fclose(fp);  
  23.       
  24.     return 0;  
  25. }  

本來test文件里面的內(nèi)容是hello\n,執(zhí)行上面的程序后,為什么不是ahello\n,而是aello\n ?

A: 這個問題的原因在于fputc, fputs, fwrite等寫操作的函數(shù)均為覆蓋寫,不是插入寫導(dǎo)致的。如果需要將文件讀出來,將文件開頭插入字符a,然后將讀出的數(shù)據(jù)全部追加到文件后面,這樣才行。


Q: 一直聽到二進(jìn)制文件和文本文件,它們到底有什么區(qū)別?

A: 計算機(jī)底層最終只能處理所謂的二進(jìn)制文件,文本文件只是人們將文件的內(nèi)容做了抽象得到的一種特殊的二進(jìn)制文件。不過,它們是有一定區(qū)別的,可能在某些時候,不同的平臺,它們的表現(xiàn)也不同。但是從理論上來說,二進(jìn)制文件可能更節(jié)省空間,這一方面是因?yàn)槎M(jìn)制文件是文件最終的形式,另一方面是因?yàn)榭梢杂枚M(jìn)制的1比特表示數(shù)值1,甚至整形1,但是用文本方式,卻至少用1字節(jié)。另外,不同平臺對于回車換行,CR與LF, "\r\n"的表示方式不太一致,導(dǎo)致對于它的文本解讀和二進(jìn)制形式有不一致的地方。在mac上,文本形式的換行用'\n'表示,而在windows上,文本換行是\r\n, 所以有時會發(fā)現(xiàn)同一個文件在不同操作系統(tǒng)下的換行顯示會有不同。不過,二進(jìn)制和文本方式讀取也不是水火不相容的,正如下面的例子,顯示的結(jié)論沒什么不同:

假設(shè)test文件中保存hello\n,

文本方式讀:

  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #include <fcntl.h>  
  4. #include <unistd.h>  
  5.   
  6. #define PRINT_D(intValue)   printf(#intValue" is %lu\n", (intValue));  
  7. #define PRINT_CH(ch)        printf(#ch" is %c\n", (ch));  
  8. #define PRINT_STR(str)      printf(#str" is %s\n", (str));  
  9.   
  10. int main (int argc, const char * argv[])  
  11. {  
  12.     int ch;  
  13.     FILE *fp = fopen("test""rt");  
  14.     if(!fp)  
  15.     {  
  16.         perror("fopen error");  
  17.         return -1;  
  18.     }  
  19.     while ((ch = fgetc(fp)) != EOF)  
  20.     {  
  21.         printf("ch:%c %d\n", ch == '\n' ? 'N' : ch, ch);  
  22.     }  
  23.       
  24.     fclose(fp);  
  25.       
  26.     return 0;  
  27. }  

二進(jìn)制讀:

  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #include <fcntl.h>  
  4. #include <unistd.h>  
  5.   
  6. #define PRINT_D(intValue)   printf(#intValue" is %lu\n", (intValue));  
  7. #define PRINT_CH(ch)        printf(#ch" is %c\n", (ch));  
  8. #define PRINT_STR(str)      printf(#str" is %s\n", (str));  
  9.   
  10. int main (int argc, const char * argv[])  
  11. {  
  12.     int ch;  
  13.     FILE *fp = fopen("test""rb");  
  14.     if(!fp)  
  15.     {  
  16.         perror("fopen error");  
  17.         return -1;  
  18.     }  
  19.     while ((ch = fgetc(fp)) != EOF)  
  20.     {  
  21.         printf("ch:%c %d\n", ch == '\n' ? 'N' : ch, ch);  
  22.     }  
  23.       
  24.     fclose(fp);  
  25.       
  26.     return 0;  
  27. }  

二個應(yīng)用程序的運(yùn)行結(jié)果均為:

顯示為N的地方為換行字符。


Q: 有的時候,想要直接將一個整形數(shù)據(jù)以二進(jìn)制形式寫入文件中,有什么更分便的調(diào)用方式?

A: putw可以實(shí)現(xiàn)你要的功能。

  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #include <fcntl.h>  
  4. #include <unistd.h>  
  5.   
  6. #define PRINT_D(intValue)   printf(#intValue" is %lu\n", (intValue));  
  7. #define PRINT_CH(ch)        printf(#ch" is %c\n", (ch));  
  8. #define PRINT_STR(str)      printf(#str" is %s\n", (str));  
  9.   
  10. int main (int argc, const char * argv[])  
  11. {  
  12.     FILE *fp = fopen("test""w");  
  13.     if(!fp)  
  14.     {  
  15.         perror("fopen error");  
  16.         return -1;  
  17.     }  
  18.     putw(32767, fp);  
  19.   
  20.     fclose(fp);  
  21.       
  22.     return 0;  
  23. }  

運(yùn)行完后,用hexdump test查看test文件的十六進(jìn)制形式,可以發(fā)現(xiàn)32767對應(yīng)int類型大小的數(shù)據(jù)已經(jīng)保存在test中了。

當(dāng)然,如果不嫌麻煩,可以使用如下的代碼實(shí)現(xiàn)上面類似的功能:

  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #include <fcntl.h>  
  4. #include <unistd.h>  
  5.   
  6. #define PRINT_D(intValue)   printf(#intValue" is %lu\n", (intValue));  
  7. #define PRINT_CH(ch)        printf(#ch" is %c\n", (ch));  
  8. #define PRINT_STR(str)      printf(#str" is %s\n", (str));  
  9.   
  10. int main (int argc, const char * argv[])  
  11. {  
  12.     int n = 32767;  
  13.     char *pn = (char *)&n;  
  14.     int i;  
  15.     FILE *fp = fopen("test""w");  
  16.     if(!fp)  
  17.     {  
  18.         perror("fopen error");  
  19.         return -1;  
  20.     }  
  21.     for(i = 0; i < sizeof(n); ++i)  
  22.     {  
  23.         fputc(*(pn + i), fp);  
  24.     }  
  25.   
  26.     fclose(fp);  
  27.       
  28.     return 0;  
  29. }  

Q: 之前看過好多關(guān)于文件打開方式的字符串,"rb", "w+"等等,到底怎么很好地明白它們的打開方式?

A: 其實(shí)很簡單。以r開頭的方式打開,如果文件不存在必然失?。淮蜷_方式含有b即表示是二進(jìn)制方式,如果沒有b或者有t,那就說明是文本方式;如果打開方式中有+,那么表示可讀可寫;


Q: 很多時候,如果讀取輸入緩沖區(qū)的數(shù)據(jù)讀多了,想放回去怎么辦?

A: 可以使用ungetc函數(shù)。

  1. int  ungetc(intFILE *);  

舉個例子吧:

從標(biāo)準(zhǔn)輸入讀取一個十進(jìn)制整數(shù)和最后一個分隔符(不是十進(jìn)制整數(shù)字符),然后打印這個整數(shù)和最后的分隔符。

  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #include <fcntl.h>  
  4. #include <unistd.h>  
  5. #include <ctype.h>  
  6.   
  7. #define PRINT_D(intValue)   printf(#intValue" is %lu\n", (intValue));  
  8. #define PRINT_CH(ch)        printf(#ch" is %c\n", (ch));  
  9. #define PRINT_STR(str)      printf(#str" is %s\n", (str));  
  10.   
  11. int main (int argc, const char * argv[])  
  12. {  
  13.     int n = 0;  
  14.     int ch;  
  15.     char end_ch;  
  16.       
  17.     while ((ch = getchar()) != EOF && isdigit(ch))  
  18.     {  
  19.         n = 10 *n + (ch - '0');  
  20.     }  
  21.     if(ch != EOF)  
  22.     {  
  23.         ungetc(ch, stdin);  
  24.     }  
  25.     end_ch = getchar();  
  26.       
  27.     PRINT_D(n)  
  28.     PRINT_CH(end_ch)  
  29.       
  30.     return 0;  
  31. }  

運(yùn)行時,輸入1234;然后換行,

可以發(fā)現(xiàn),ungetc會將;字符重新放回輸入流中,是的end_ch被賦值為;字符;

如果沒有使用ungetc的話:

可以發(fā)現(xiàn),end_ch被賦值為輸入1234;后面的換行了。


Q: 關(guān)于輸入輸出重定向已經(jīng)聽說了很多了,c語言中有函數(shù)可以實(shí)現(xiàn)重定向嗎?

A: 是的, freopen函數(shù)可以實(shí)現(xiàn)這個作用。

  1. FILE *freopen(const char *restrict filename, const char *restrict mode, FILE *restrict stream);  

第一個參數(shù)表示需要重定向的文件位置;第二個參數(shù)表示重定向方式,如"w"為寫方式;第三個參數(shù)表示被重定向的文件句柄;

下面有個代碼將展示如何將stdout重定向到test文件,然后再恢復(fù)stdout的輸出;

代碼如下:

  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #include <fcntl.h>  
  4. #include <unistd.h>  
  5. #include <ctype.h>  
  6.   
  7. #define PRINT_D(intValue)   printf(#intValue" is %lu\n", (intValue));  
  8. #define PRINT_CH(ch)        printf(#ch" is %c\n", (ch));  
  9. #define PRINT_STR(str)      printf(#str" is %s\n", (str));  
  10.   
  11. int main (int argc, const char * argv[])  
  12. {  
  13.     FILE *file;  
  14.     int fd;  
  15.     fpos_t  pos;  
  16.       
  17.     // output to stdout  
  18.     fprintf(stdout, "1111");  
  19.       
  20.     // backup the stdout info  
  21.     fflush(stdout);  
  22.     fgetpos(stdout, &pos);  
  23.     fd = dup(fileno(stdout));  
  24.   
  25.     // redirect stdout to "test" file  
  26.     file = freopen("test""w", stdout);  
  27.     if(!file)  
  28.     {  
  29.         perror("freopen error");  
  30.         return -1;  
  31.     }  
  32.       
  33.     // now the stdout is redirected to "test" file, file "test" will contain "hello" str  
  34.     fprintf(stdout, "hello");  
  35.       
  36.     // restore the stdout  
  37.     fflush(stdout);  
  38.     dup2(fd, fileno(stdout));  
  39.     close(fd);  
  40.     clearerr(stdout);  
  41.     fsetpos(stdout, &pos);  
  42.       
  43.     // not the stdout is as the default state  
  44.     fprintf(stdout, "2222");  
  45.       
  46.     return 0;  
  47. }  

運(yùn)行結(jié)果:

可以看出,最開始的標(biāo)準(zhǔn)輸出和最后的標(biāo)準(zhǔn)輸出都正常顯示在屏幕,第二個標(biāo)準(zhǔn)輸出因?yàn)楸恢囟ㄏ蜉敵龅搅藅est文件中;查看test文件的內(nèi)容:

不過,如果是在windows平臺,恢復(fù)stdout的方式和上面的代碼可能不一致。


xichen

2012-5-12 18:08:31

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多