[APUE]標準IO庫(上)

来源:http://www.cnblogs.com/orlion/archive/2016/12/29/6230841.html
-Advertisement-
Play Games

一、流和FILE對象 系統IO都是針對文件描述符,當打開一個文件時,即返回一個文件描述符,然後用該文件描述符來進行下麵的操作,而對於標準IO庫,它們的操作則是圍繞流(stream)進行的。 當打開一個流時,標準IO函數fopen返回一個指向FILE對象的指針。該對象通常是一個結構,它包含了IO庫為管 ...


一、流和FILE對象
  系統IO都是針對文件描述符,當打開一個文件時,即返回一個文件描述符,然後用該文件描述符來進行下麵的操作,而對於標準IO庫,它們的操作則是圍繞流(stream)進行的。
當打開一個流時,標準IO函數fopen返回一個指向FILE對象的指針。該對象通常是一個結構,它包含了IO庫為管理該流所需要的所有信息:用於實際IO的文件描述符,指向流緩存的指針,緩存的長度,當前在緩存中的字元數,出錯標誌等等。
我們稱指向FILE對象的指針(類型為FILE *)為文件指針。
二、緩存
  標準IO提供緩存的目的是儘可能減少使用read和write調用的數量。標準IO提供了三種類型的緩存
    (1) 全緩存。在這種情況下,當填滿標準IO緩存後才進行實際IO操作。對於駐在磁碟上的文件通常是由標準IO實施全緩存的。在一個流上執行第一次IO操作時,相關標準IO函數通常調用malloc獲得所需的緩存。
    術語刷新(flush)說明標準IO緩存的寫操作。緩存可由標準IO常式自動地刷新(例如當填滿一個緩存時),或者可以調用函數flush刷新一個流。在UNIX環境中刷新有兩種意思。在標準IO庫方面,刷新意味著將緩存中的內容寫入到磁碟上(該緩存可以只是局部填寫     的)。在終端驅動程式方面,刷新表示丟棄已存在緩存中的數據。
    (2) 行緩存。在這種情況下,當在輸入和輸出中遇到換行符時,標準IO庫執行IO操作。這允許我們一次輸入一個字元(用標準IO fputc函數),但只有在寫了一行之後才進行實際IO操作。當流涉及一個終端時(例如標準輸入和標準輸出),典型的使用行緩存。
    對於行緩存有兩個限制,一是:因為標準IO庫用來收集每一行的緩存長度是固定的,所以只要填滿了緩存,那麼即使還沒有寫一個換行符,也進行IO操作。第二是:任何時候只要通過標準輸入輸出庫要求從(a)一個不帶緩存的流(b)一個行緩存的流(它預先要求從內    核得到數據)得到輸入數據,那麼就會造成刷新所有行緩存輸出流。在(b)中帶了一個在擴號中的說明的理由是,所需的數據可能已在緩存中,它並不要求內核在需要該數據時才進行操作。很明顯,從不帶緩存的一個流中進行輸入((a)項)要求當時從內核中得到數    據。
    (3) 不帶緩存。標準IO庫不對字元進行緩存。標準出錯流通常不帶緩存,這可以使出錯信息儘快的顯示出來。

  ASSI C要求以下緩存特征:
    (1) 當且僅當標準輸入和標準輸出不涉及交互作用設備時,他們才是全緩存的。
    (2) 標準出錯絕不是全緩存的。
  對於任何一個流如果我們不喜歡系統預設則可以通過以下兩個函數來更改緩存類型:

#include <stdio.h>
void setbuf(FILE *fp, char *buf);
int setvbuf(FILE *fp, char *buf, int mode, size_t size);

  這些函數必須在流打開後和對該流進行任何操作之前調用。
  可以使用setbuf打開或關閉緩存機制。為了帶緩存進行IO,參數buf必須指向一個長度為BUFSIZ的緩存(該常數定義在stdio.h中)。通常在此之後該流就是全緩存的,但是該流與終端設備相關,那麼某些系統也可將其設置為行緩存。為了關閉緩存,將buf設置為NULL。
  使用setvbuf可以精確的說明所需的緩存類型。由mode參數指定:

_IOFBF 全緩存
 _IOLBF 行緩存,
_IONBF 不帶緩存

  如果指定了一個不帶緩存的流,則忽略buf和size參數。如果指定全緩存或行緩存,則buf和size可以選擇地指定一個緩存及長度。如果該流是帶緩存的,而buf是NULL,則標準IO庫將自動的為該流分配適當長度的緩存,適當長度是指struct stat結構體中的st_blksize所指定的值。如果系統不能為為該流決定此值(例如該流涉及一個設備或一個管道),則分配長度為BUFSIZ的緩存。
  下表列出了這兩個函數的動作,以及它們的各個選擇項

  

  如果在一個函數中分配了一個自動變數類的標準IO緩存,則從該函數返回之前必須關閉該流。SVR4將緩存的一部分用於ta自己的管理操作,所以可以存放在緩存中的實際數據位元組數小於size。一般來說,應由系統選擇緩存的長度,並自動分配緩存。在這樣處理時,標準IO庫在關閉此流時將自動關閉釋放緩存。
  可以在任何時候強制刷新一個流:

#include <stdio.h>
int fflush(FILE *fp);

  此函數使該流所有的數據傳遞到內核,如果fp是NULL,則此函數刷新所有輸出流。

三、打開流

#include <stdio.h>
FILE *fopen(const char *pathname, const char *type);
FILE *freopen(const char *pathname, const char *type, FILE fp);
FILE *fdopen(int fileds, const char *type);
返回值: 成功文件指針,失敗NULL

  三個函數的區別:

    (1)fopen打開路徑為pathname的文件。
    (2)freopen在一個特定流上(由fp指定)打開pathname文件。如果該流已打開則先關閉。此函數一般用於將一個文件打開為一個預定義的流:標準輸入、標準輸出和標準出錯。
    (3)fdopen取一個現存的文件描述符(可能從open\dup\dup2\fcntl\pipe函數得到此文件描述符)並使一個標準的IO流與該文件描述符結合。此函數常用於由創建管道和網路通信通道函數獲得的插入符。因為這些特殊類型的文件不能用標準IO fopen打開
  type參數指定對該IO流的讀、寫方式,ANSI C規定type參數可以有15種值:

  

  使用字元b作為type的一部分使得標準IO系統可以區分文本文件或二進位文件。因為UNXI並不對這兩種文件區分所以在UNIX環境下指定b作為type的一部分實際上並無作用。
  對於fdopen,type參數的意義有些區別。因為文件描述符已打開,所以fdopen為寫而打開並不截短該文件。另外,標準IO添加方式也不能用於創建文件,因為如果一個文件描述符引用一個文件則該文件一定已經存在。
  當用添加類型打開一個文件後則每次寫都將數據寫到文件當前尾端處。如果有多個進程用標準IO的方式打開同一個文件,則來自每個進程的數據都將正確的寫到文件中。

  當以讀和寫打開一文件時(type中+號),具有如下限制:
    如果中間沒有fflush、fseek、fsetpos或rewind,則在輸出的後面不能直接跟隨輸入。
    如果中間沒有fseek、fsetpos、或rewind或者一個輸出操作沒有到達文件尾端,則在輸入操作之後不能直接跟隨輸出。
  下表是打開一個流的六種不同的方式:
  


  在指定w或a類型創建一個新文件時,無法說明文件的存取許可位,POSIX.1要求以這種方式創建的文件具有以下許可權:

S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH

  除非流引用終端設備,否則按系統預設,它被打開時是全緩存。若流引用終端設備則該流是行緩存。
  用fclose關閉一個打開的流:

#include <stdio.h>
int fclose(FILE *fp);
返回值: 成功0,出錯EOF

  在該文件關閉之前,刷新緩存中的輸出數據。緩存中的輸入數據被丟棄。如果標準IO庫已經為該流自動分配了一個緩存則釋放該緩存。
  當一個進程正常終止時(直接調用exit()函數或者從main函數中返回),則所有帶未寫緩存數據的標準IO流都被刷新,所有打開的標準IO流都被關閉。

四、讀和寫流
  一旦打開了流,則可以在以下三種不同類型的非格式化IO中進行選擇,對其進行讀寫:
    (1) 每次一個字元的IO。一次讀或寫一個字元,如果流是帶緩存的,則標準IO函數處理所有緩存。
    (2) 每次一行的IO。使用fgets和fputs一次讀或寫一行。每行都以一個新行符結束。當調用fgets時應說明能處理的最大行長。
    (3) 直接IO。 fwrite和fread函數支持這種類型的IO。每次IO操作讀或寫某種數量的對象,每個對象具有指定的長度。這兩個函數常用於從二進位文件中讀或寫一個結構。

  直接IO(direct IO)這個術語來自ANSI C標準,有時也被稱為:二進位IO、一次一個對象IO、面向記錄的IO或面向結構的IO。

  1.輸入函數
  以下三個函數用於一次讀一個字元:

#include <stdio.h>
int getc(FILE *fp);
int fgetc(FILE *fp);
int getchar(void);
返回值:成功則為下一個字元,如果已處於文件尾端或出錯則為EOF

  getchar等同於getc(stdin)。前兩個函數的區別是getc可被實現為巨集,而fgetc則不能。(這裡的可被實現為巨集即大多數UNIX系統中getc的實現是這樣:在<stdio.h>中#define getc(FILE *fp) xxx(FILE *fp),即getc不是一個函數而是一個巨集)這意味著:
    (1) getc的參數不應當是具有副作用的表達式。
    (2) 因為fgetc一定是個函數,所以可得到其地址。這就允許將fgetc的地址作為一個參數傳送給另一個函數。
    (3) 調用fgetc所需時間可能長於調用getc,因為調用函數通常所需的時間長於調用巨集。

  這三個函數以unsigned char類型轉換為int的方式返回下一個字元。返回int的原因是函數可以返回一個負值(指示出錯或已到達文件尾端),在<studio.h>中常數EOF常要求是一個負值,其值經常是-1。所以不能將這三個函數的返回值放到一個字元變數中。

  不管是出錯還是到達文件尾端,這三個函數都返回同樣的值。為了區分這兩種不同的情況,須調用ferror或feof。

#include <stdio.h>
int ferror(FILE *fp);
int feof(FILE *fp);
返回值:條件為真返回非0值,否則0
void clearerr(FILE *fp);

  在大多數實現的FILE對象中,為每個流保持了兩個標誌

  • 出錯標誌
  • 文件結束標誌

  

  從一個流讀之後,可以調用ungetc將字元再送回到流中。

#include <stdio.h>
int ungetc(int c, FILE *fp);

  送回到流中的字元又可以從流中讀出,但讀出字元的順序與送回的順序相反。回送的字元不一定是上一次讀到的字元。EOF不能回送。但是當已到達文件尾端時仍可以回送一個字元。下次讀將返回該字元,再次讀則返回EOF。之所以可以這樣做的原因是一次成功的ungetc調用會清除該流的文件結束指示。

  當正在讀一個輸入流,併進行某種形式的分字或分記號操作時,會經常用到回送字元操作。有時需要先看一看下一個字元以決定如何處理當前字元。然後就需要方便的將剛查看的字元送回,以便下一次調用getc時返回該字元。

 

  2. 輸出函數

  

#include <stdio.h>
int putc(int c, FILE *fp);
int fputc(int c, FILE *fp);
int putchar(int c);
返回值:成功返回c,出錯EOF

  putchar等同於putc(c, stdout)

 

五、 每次一行IO

  下麵兩個函數提供每次輸入一行的功能:

#include <stdio.h>
char *fgets(char *buf, int n, FILE *fp);
char *fgets(char *buf);

  gets從標準輸入讀。對於fgets必須指定緩存buf的長度n。此函數會一直讀到下一個新行符為止,但是不超過n-1個字元,讀入的字元被送入到緩存。該緩存以null字元結尾。如果該行包括最後一個新行符的字元數超過n-1,則只返回一個不完整的行,而且緩存總是以null字元結尾。對fgets的下一次調用會繼續該行

  gets函數不被推薦使用因為不能指定緩存的長度,gets並不將新行符存入緩存中。

#include <stdio.h>
int fputs(const char *str, FILE *fp);
int puts(const char *str);
返回值:成功返回非負值,出錯EOF

  函數fputs將一個以null符終止的字元串寫入到指定的流,終止符null不寫出。這並不一定是每次輸出一行,因為它並不要求在null符之前一定是新行符,但是通常在null符之前是一個新行符。

  puts將一個以null符終止的字元串寫入到標準輸出,終止符不寫出。但是puts然後將一個新行符寫到標準輸出


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1、什麼是指針? 指針就是地址。 為了說清楚什麼是指針,必須先弄清楚數據在記憶體中是如何存儲的,又是如何讀取的。 如果在程式中定義了一個變數,在對程式進行編譯時,系統就會給這個變數分配記憶體單元。編譯系統根據程式中定義的變數的類型,分配一定長度的空間。 記憶體區的每一個位元組都會有一個編號,這就是“地址”, ...
  • 前提: 1.已安裝python版本(一般都是2.X) 2.已安裝easy_install python安裝,記得配置Python的環境變數,例如:我的直接在Path上加 G:\Python 驗證安裝Python成功:cmd 輸入python 或者python -V 這是下載easy_install的 ...
  • 關於volatile,找了一堆資料看,看完後想找一個方法去做測試,測了很久,感覺跟沒有一樣。 這本書《深入理解Java記憶體模型》,對volatile描述中有這樣一個比喻的說法,如下代碼所示,對a的讀寫相當於對b的同步讀寫。 也就是說,volatile只保證了讀與寫的同步,每次讀取都是得到最新值,每次 ...
  • 1. 前言 Java和C++之間顯著的一個區別就是對記憶體的管理。和C++把記憶體管理的權利賦予給開發人員的方式不同,Java擁有一套自動的記憶體回收系統(Garbage Collection,GC)簡稱GC,可以無需開發人員干預而對不再使用的記憶體進行回收管理。 垃圾回收技術(以下簡稱GC)是一套自動的內 ...
  • 為什麼要用抽象類: 子類萬一沒有重寫正確,也沒有提示。 假設父類的代碼如下 子類中的代碼如下:對父類中的方法的重寫 這樣的代碼,在Eclipse中是不會報錯的; 抽象類:不知道是具體什麼東西的類。 寫法:abstract class 類名 抽象方法:不知道是具體是做什麼的方法; 寫法:abstrac ...
  • feof是C語言標準庫函數函數,其原型在stdio.h中,其功能是檢測流上的文件結束符。 函數原型: 1 #include<stdio.h> 2 #include<stdlib.h> 3 int main(){ 4 FILE *pt,*pt1; 5 char ch; 6 pt=fopen("in.t ...
  • 新版六合彩投註網站系統源碼 (測試完整可商業運行) 帶新開獎結果》》》》》 網站系統可以支持代理開設,股東發展,普通會用等常用的功能。 玩法齊全》連碼自由對碰》各類玩法內附說明。資料庫文件 hs001 修改資料庫連接 文件夾 configs 文件config後臺管理 admin admin前臺會員目 ...
  • 數組與集合的區別: Java中的數組是存放同一類型數據的集合。數組可以存放基本數據類型的數據,也可以存放引用數據類型的數據,但數組的長度是固定的。 集合只能存放引用數據類型的數據,不能存放基本數據類型的數據,但集合的數目是可變的。 所有的集合類都存放在java.util包中 List介面是元素有序, ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...