最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Linux系統(tǒng)下C語言中的標準IO總結(jié)

 更新時間:2024年01月05日 10:35:34   作者:coolhuhu~  
最近用到了C語言的標準IO庫,由于對其中的一些細節(jié)不是非常清楚,導致了許多Bug,花了好長時間來調(diào)試,所以在此做個筆記,這篇文章主要給大家介紹了關(guān)于Linux系統(tǒng)下C語言中標準IO的相關(guān)資料,需要的朋友可以參考下

本文對 Linux 下C語言的標準IO進行總結(jié),所有代碼示例均在 Ubuntu-20.04、GCC 11.3.0 環(huán)境下運行通過。

標準IO中的一些概念

流和FILE對象

在 Linux 操作系統(tǒng)中,提供給用戶操作文件的接口是“文件描述符”以及對應的函數(shù),例如 read,write等。而在C語言中,提供給用戶的文件操作的接口是“流(stream)”,當使用C語言中的標準I/O庫打開或創(chuàng)建一個文件時,就使得一個流與一個文件關(guān)聯(lián)起來。而這個 “流” 的概念,在程序上,使用 FILE 對象來表示,例如:

#include <stdio.h>

int main()
{
	// 打開或者創(chuàng)建一個文件,使用 FILE 對象與該文件進行綁定。
	// 也常把 fp 叫為 文件流
	FILE* fp = fopen("example.txt", "a");
	// ...
}

注意:一個進程預定了三個流,標準輸入、標準輸出和標準錯誤。而本文的重點討論對象是 文件流,也即 FILE 對象以及標準I/O中提供的操作 FILE 對象的一系列函數(shù)。

流的定向(stream’s orientation)

對于ASCII字符集,一個字符用一個字節(jié)表示。對于國際字符集,一個字符可用多個字節(jié)表示。標準I/O文件流可用于單字節(jié)或多字節(jié)(也稱“寬”字符)字符集。流的定向(stream’s orientation)決定所讀、寫的字符是單字節(jié)還是多字節(jié)的。

下面給出一個寬字符集輸出到標準輸出的示例:

#include <iostream>
#include <cstring>

int main() {

    const char* charString = "你好,世界!";
    std::cout << "Length of charString: " << std::strlen(charString) << std::endl;

    const char* multibyteString = u8"你好,世界!";  // UTF-8編碼的多字節(jié)字符串
    std::cout << "Length of multibyteString: " << std::strlen(multibyteString) << std::endl;

    const wchar_t* wideString = L"你好,世界!";  // UTF-16編碼的寬字符字符串
    std::wcout << L"Length of wideString: " << std::wcslen(wideString) << std::endl;
    return 0;
}

/*
運行結(jié)果為:
Length of charString: 18
Length of multibyteString: 18
Length of wideString: 6
*/

注意:下文討論的都是單字節(jié)的流向。

緩沖(buffer)

在 Linux 中,使用 read、write 函數(shù)對文件描述符進行讀寫操作屬于系統(tǒng)調(diào)用,用于直接讀寫磁盤文件。(其實也不是直接讀寫讀寫磁盤文件,Linux內(nèi)核中會維護高速緩沖區(qū)用于提高磁盤文件的讀寫效率,這部分內(nèi)容超出的本文的討論范疇,略過。)而C語言I/O標準庫提供的I/O操作在用戶態(tài),通常帶有緩沖(當然,也可以沒有緩沖),使用標準I/O庫提供的I/O操作先將數(shù)據(jù)寫入緩沖中,然后等待某個條件達成,在將緩沖中的數(shù)據(jù)寫入磁盤文件(調(diào)用 write 函數(shù))。標準I/O庫提供緩沖的目的是減少 read 和 write 調(diào)用的次數(shù),提高 I/O 效率。(而在實際的開發(fā)中,I/O緩沖的利用需要根據(jù)實際的場景來使用,并不是說有了緩沖,I/O效率就提高了。)

標準 I/O 提供了三種緩沖類型:

  • 全緩沖。對于寫操作,當緩沖區(qū)寫滿后,才將緩沖中的數(shù)據(jù)寫入文件;讀操作同理。對于寫操作,可以調(diào)用 flush 函數(shù)主動將緩沖中的數(shù)據(jù)寫入文件而不論緩沖是否被寫滿。下文將調(diào)用 flush 函數(shù)的操作稱為 ”刷新緩沖“。
  • 行緩沖。當讀或?qū)憯?shù)據(jù)遇到換行符時,將緩沖中的數(shù)據(jù)進行輸入或輸出。行緩沖的一個限制是:當緩沖已滿,即使未遇到換行符,也將其進行輸入輸出。
  • 不帶緩沖。即I/O操作直接寫入文件。

I/O標準庫中常用的函數(shù)

文件流的打開和關(guān)閉

下面三個函數(shù)可用于文件流的打開,其中 fopen 最為常用,先重點介紹該函數(shù),剩余兩個當遇到具體的使用場景時再來補充。在 Linux 中是可以使用 man 命令查看詳情。

#include <stdio.h>

/*
pathname參數(shù)表示打開的文件路徑名;
type參數(shù)指定對文件流的讀、寫方式。
若打開出錯,返回 NULL。
*/
FILE* fopen(const char* pathname, const char* type);

FILE* freopen(const char* pathname, const char* type, FILE* fp);

FILE* fdopen(int fd, const char* type);

對于 fopen 函數(shù),type 參數(shù)的值及表示的讀、寫方式如下所示:

  • r 或 rb,以讀的方式打開。
  • w 或 wb,以寫的方式打開;若指定文件名存在,則將該文件內(nèi)容清空;不存在,創(chuàng)建新文件。
  • a 或 ab,以追加寫的方式打開文件。若指定文件名不存在,創(chuàng)建新文件。
  • r 或 r+b 或 rb+,以讀寫的方式打開文件;指定文件名不存在,則出錯。
  • w+ 或 w+b 或 wb+,以讀寫的方式打開文件;若指定文件名存在,則將該文件內(nèi)容清空;不存在,創(chuàng)建新文件。
  • a+ 或 a+b 或 ab+,以讀寫的方式打開文件,讀寫操作在文件尾開始進行,若指定文件名不存在,創(chuàng)建新文件。

使用字符b作為type的一部分,使得標準I/O系統(tǒng)可以區(qū)分為文本文件和二進制文件。UNIX不對這兩種文件進行區(qū)分。
– 《UNIX 高級環(huán)境編程》

使用 fopen 函數(shù)開打的文件流默認是自帶緩沖的,緩沖模式為全緩沖。

fclose 函數(shù)用于關(guān)閉一個打開的文件流。注意,對于一個已經(jīng)關(guān)閉了的文件流調(diào)用 fclose 函數(shù),行為是未定義的。

#include <stdio.h>

/*
若成功,返回0;若出錯,返回 EOF
*/
int fclose(FILE* fp);

當調(diào)用 fclose 函數(shù)或者當一個進程正常終止(調(diào)用 exit 函數(shù)或從 main 函數(shù)返回),會先刷新緩沖。若是使用標準IO默認的緩沖,則會釋放緩沖。

給文件流設置自定義的緩沖

若希望自己掌控文件流的緩沖,可以自定義一個緩沖,將其于打開的文件流的進行綁定。主要有如下三個函數(shù)可以綁定自定義的緩沖:

#include <stdio.h>

void setbuf(FILE* fp, char* buf);

/*
	buf參數(shù)為指定緩沖區(qū),mode表示緩沖類型,size指定了緩沖的大小。
	成功返回0;出錯返回非0。
*/
void setvbuf(FILE* fp, char* buf, int mode, size_t size);

void setbuffer(FILE* fp, char* buf, size_t size);

mode 參數(shù)的可選值如下:

  • _IOFBF,全緩沖。
  • _IOLBF,行緩沖。
  • _IONBF,無緩沖。

對上面三個函數(shù)進行如下補充說明:

  • setbuf 等價于 setvbuf(fp, buf, buf ? _IOFBF : _IONBF, BUFFSIX) ;其中, BUFZSIZE 在標準庫的默認值,我的環(huán)境下為 8096。
  • setbuffer 等價于 setvbuf(fp, buf, buf ? _IOFBF : _IONBF, size)。
  • 若 buf 參數(shù)為空,則文件流為無緩沖。

在使用文件流的進行文件操作時,全緩沖的緩沖模式用得最多,因此推薦使用 setbuffer ,不用費力去記緩沖模式的參數(shù)值。

文件流的讀寫

打開文件流后,有三種類型的非格式化I/O操作:

  • 每次讀寫一個字符。一次讀寫一個字符。
  • 每次讀寫一行。一次讀寫一行,每一行以換行符終止。
  • 直接讀寫,即指定讀寫的字節(jié)數(shù)。每次IO操作讀寫某種類型的對象,每個對象具有指定的長度。

讀寫一個字符

對于讀一個字符,有如下三個函數(shù)可供選擇:

#include <stdio.h>
int getc(FILE* fp);
int fgetc(FILE* fp);
int getchar(void);
/*
以上三個函數(shù),成功返回讀取的字符,返回前將 unsigned char 類型轉(zhuǎn)換為 int 類型;若已到達文件尾端或出錯,返回 EOF。
*/

對上面三個函數(shù)進行補充說明:

  • getchar(void) 等價于 getc(stdin)。
  • 在《UNIX 環(huán)境高級編程》中,” getc 函數(shù)可能被實現(xiàn)為宏,而 fgetc 一定為函數(shù)“。因此推薦使用 fget 函數(shù),因為宏定義的參數(shù)存在副作用。

對于上述三個函數(shù)的出錯,在文件流 FILE 對象中,每個 FILE 對象維護了兩個標志:

  • 出錯標志;
  • 文件結(jié)束標志;

可以使用 ferror 和 feof 函數(shù)進行檢查:

#include <stdio.h>
/* 檢查 fp 指定的流是否發(fā)生了錯誤。若為真,則返回非0;否則,返回0 */
int ferror(FILE* fp);
/* 檢查 fp 指定的流是否到達文件尾。若為真,則返回非0;否則,返回0 */
int feof(FILE* fp);
/* 清楚上述兩個標志 */
void clearerr(FILE* fp);

在使用文件流進行文件操作時,一個好的編碼習慣是,使用 ferror 函數(shù)檢測讀寫后的文件流狀態(tài)。

對于寫一個字符,有如下三個函數(shù)可供選擇:

#include <stdio.h>
int putc(int c, FILE* fp);
int fputc(int c,  FILE* fp);
// putchar(c) 等價于 putc(c, stdout);
int putchar(int c);
/*
以上三個函數(shù),成功,返回c;若出錯,返回EOF。
*/

和 getc、fgetc 類似,putc 可能實現(xiàn)為宏,fputc 被定義為一個函數(shù),因此推薦使用 fputc。

下面給出幾個編碼示例:

假設文件中的內(nèi)容為 python,一個字符一個字符的把文件中的內(nèi)容輸出到標準輸出。

FILE* fp = fopen("example.txt", "a+");
int c;
while ((c = fgetc(fp)) != EOF) {
	printf("%c", (unsigned char)c);
}
fclose(fp); 
/*
輸出為:python
*/

假設文件中的內(nèi)容為 python,一個字符一個字符寫入文件。

FILE* fp = fopen("example.txt", "a+");
char buf[7] = "golang";
for (int i = 0; i < 6; ++i) {
	fputc(buf[i], fp);
}
fclose(fp);  
/*
文件中的內(nèi)容為:pythongolang
*/

(以上只是簡單的編碼示例,更復雜的操作可以結(jié)合文件流的定位來進行,碰到實際的場景在來補充。)

讀寫一行

對于讀一行,有以下兩個函數(shù)可供選擇:

#include <stdio.h>
// n 為指定的緩沖區(qū)大小
// 將 fp 文件中的內(nèi)容寫入 buf 中,直至遇到換行符或者buf寫滿。
char* fgets(char* buf, int n, FILE* fp);
// gets 從標準輸入進行讀
char* gets(char* buf);
/*
以上兩個函數(shù),若成功,返回buf;若已達到文件末尾或出錯,返回NULL。
*/

gets 函數(shù)不能指定緩沖區(qū)大小,建議只使用 fgets 函數(shù)。

對于寫一行,有以下兩個函數(shù)可供選擇:

#include <stdio.h>
// fputs 不會將換行符寫入到文件流中
int fputs(const char* str, FILE* fp);
// puts 將字符串輸出到標準輸出,會將換行符作為輸出。
int puts(const char* str);
/*
以上兩個函數(shù),若成功,返回非負責;若出錯,返回 EOF。
*/

建議只是用 fputs 函數(shù)。

直接讀寫

在《UNIX環(huán)境高級編程》一書中,直接讀寫也即二進制讀寫,指一次讀寫一個完整的結(jié)構(gòu),例如一個結(jié)構(gòu)體對象。通常用來讀寫指定的字節(jié)大小的數(shù)據(jù)。

常用的直接讀寫的函數(shù)如下:

#include <stdio.h>
// 若出錯或到達文件末尾,返回值可以小于 nobj;需要調(diào)用 ferror 或 feof 來判斷是哪一種情況。
size_t fread(void* ptr, size_t size, size_t nobj, FILE* fp);
// 若出錯,返回值小于 nobj
size_t fwrite(const void* ptr, size_t size, size_t nobj, FILE* fp);
/*
以上兩個函數(shù),返回讀寫的對象數(shù)量。
ptr 指向待寫入的對象
size 表示對象的大小
nobj 表示寫入的對象數(shù)量
*/

下面給出幾個編碼示例:

讀寫char數(shù)組形式的字符串。

char buffer[16] = "python";
FILE* wfp = fopen("test2.txt", "w");
fwrite(buffer, 1, strlen(buffer), wfp);
fclose(wfp);
printf("%s\n", buffer);
char buffer2[16];
FILE *rfp = fopen("test2.txt", "r");
fread(buffer2, 1, strlen(buffer), rfp);
fclose(rfp);
printf("%s\n", buffer2);

使用 fread 和 fwrite 讀寫一個類的示例對象(有bug)。(無意中測試出來的一個bug,暫未解決。一個初步的思路為,需要去學習了解 C++ 的對象模型,即一個C++的對象在內(nèi)存中是如何布局的,然后在深入 fread 和 fwrite 的源碼中,去了解,其底層是如何讀寫的。在此文中先留個坑,后面再來填補)

void test1()
{
    Person p1("Jack", 25);
    FILE* wfp = fopen("Person", "wb");
    fwrite((void*)&p1, sizeof(Person), 1, wfp);
    fclose(wfp);
    Person* p2 = new Person("Lisa", 19);
    std::cout << p2->name() << std::endl;
    FILE* rfp = fopen("Person", "rb");
    fread(p2, sizeof(Person), 1, rfp);
    fclose(rfp);
    std::cout << p2->name() << std::endl;
    // delete p2;   /* 若在這行執(zhí)行此語句,出現(xiàn) Segmentation fault */
    Person p3;
    FILE* rfp2 = fopen("Person", "rb+");
    fread(&p3, sizeof(Person), 1, rfp2);
    fclose(rfp2);
    std::cout << p3.name() << std::endl;
    /* 程序結(jié)束,Segmentation fault */
}

格式化讀寫

格式化輸出常用的有 printffprintfdprintfsprintfsnprintf 五個函數(shù),下面介紹最常用的 printf 和 fprintf 函數(shù)。

#include <stdio.h>
int printf(const char* format, ...);
int fprintf(FILE* fp, const char* format, ...);
/*
以上兩個函數(shù),成功,返回輸出的字符數(shù);出錯,返回復制。
format 表示格式化字符串;
... 為C語言的可變參數(shù),需要與 format 中的格式化進行匹配。
*/

格式化輸入常用的有如 scanffscanf 和 sscanf 。

對于標準庫中格式化讀寫的更多細節(jié),太過瑣碎,可參考:https://en.cppreference.com/w/cpp/io/c/fscanf

多線程的安全性

上述的章節(jié)中介紹的文件流的讀寫函數(shù)都是線程安全的,會在正常進行磁盤文件讀寫時進行加鎖操作。標準庫中也提供非線程安全的版本,它們都以 _unlocked 后綴結(jié)尾。例如對于 fread 和 fwrite 的非線程安全版本為 fread_unlocked 和 fwrite_unlocked。

總結(jié)

到此這篇關(guān)于Linux系統(tǒng)下C語言中的標準IO的文章就介紹到這了,更多相關(guān)C語言標準IO內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 如何使用C++獲取指定的重載函數(shù)地址

    如何使用C++獲取指定的重載函數(shù)地址

    重載函數(shù)是完全不同的幾個函數(shù),有不同的函數(shù)地址,下面這篇文章主要給大家介紹了關(guān)于如何使用C++獲取指定的重載函數(shù)地址的相關(guān)資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-06-06
  • C語言實現(xiàn)簡單通訊錄功能

    C語言實現(xiàn)簡單通訊錄功能

    這篇文章主要為大家詳細介紹了C語言實現(xiàn)簡單通訊錄功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • C語言中scanf的用法舉例

    C語言中scanf的用法舉例

    本節(jié)介紹輸入函數(shù)?scanf?的用法,scanf?和?printf?一樣,非常重要,而且用得非常多,所以一定要掌握,這篇文章主要介紹了C++中scanf的用法舉例,需要的朋友可以參考下
    2022-11-11
  • C++實現(xiàn)簡易五子棋游戲

    C++實現(xiàn)簡易五子棋游戲

    這篇文章主要為大家詳細介紹了C++實現(xiàn)簡易五子棋游戲,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-07-07
  • C++基于EasyX圖形庫實現(xiàn)2048小游戲

    C++基于EasyX圖形庫實現(xiàn)2048小游戲

    這篇文章主要為大家詳細介紹了C++基于EasyX圖形庫實現(xiàn)2048小游戲,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-02-02
  • C++實現(xiàn)酒店管理系統(tǒng)

    C++實現(xiàn)酒店管理系統(tǒng)

    這篇文章主要為大家詳細介紹了C++實現(xiàn)酒店管理系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • C++ OpenCV實戰(zhàn)之形狀識別

    C++ OpenCV實戰(zhàn)之形狀識別

    本案例通過使用OpenCV中的approxPolyDP進行多邊形近似,進而進行基礎形狀識別(圓、三角形、矩形、星形…),快跟隨小編一起動手嘗試一下
    2022-07-07
  • C語言實現(xiàn)自動給QQ好友發(fā)窗口抖動

    C語言實現(xiàn)自動給QQ好友發(fā)窗口抖動

    這篇文章主要介紹了C語言實現(xiàn)自動給QQ好友發(fā)窗口抖動,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-11-11
  • c語言字符串函數(shù)strstr,strtok,strerror的使用和實現(xiàn)

    c語言字符串函數(shù)strstr,strtok,strerror的使用和實現(xiàn)

    C語言中的字符串處理函數(shù)如strtok、strstr和strerror對于字符串的處理有著重要的作用,strtok函數(shù)用于分割字符串,它通過sep參數(shù)指定的分隔符來分割str參數(shù)指定的字符串,并返回分割后的每個子字符串
    2024-10-10
  • C++雙向鏈表的增刪查改操作方法講解

    C++雙向鏈表的增刪查改操作方法講解

    相較單鏈表,雙向鏈表除了data與next域,還多了一個pre域用于表示每個節(jié)點的前一個元素。這樣做給雙向鏈表帶來了很多優(yōu)勢。本文主要介紹了雙向鏈表的實現(xiàn),需要的可以參考一下
    2023-03-03

最新評論

宁安市| 上犹县| 北海市| 封开县| 阿巴嘎旗| 德令哈市| 敦煌市| 兴宁市| 海城市| 永平县| 南安市| 楚雄市| 大宁县| 桃源县| 富川| 密云县| 柘城县| 肥城市| 阿拉善右旗| 辽宁省| 监利县| 左贡县| 五原县| 宜黄县| 佛学| 无锡市| 南丹县| 集安市| 临邑县| 张家川| 唐河县| 涟源市| 简阳市| 康乐县| 三都| 平罗县| 桃园县| 进贤县| 嵊州市| 青浦区| 襄汾县|