Linuxシステムプログラミング学習ノート(二)C標準I/Oライブラリ


標準I/O
Buffered I/O:
Buffered I/Oは、システム呼び出しの回数を減らし、パフォーマンスを向上させることができます.blockを1つ読むたびに整数倍の効率が向上します.
1、Standard I/O:
C言語の標準IOライブラリstdioは、プラットフォームにまたがるuser-bufferingのスキームを提供しています.
2、ファイルポインタ:
標準I/Oは、ファイル記述子を直接操作するのではなく、ファイルポインタを使用してファイルを操作します.
FILE *fp
開いているファイルはストリームと呼ばれ、入力ストリーム、出力ストリーム、入出力ストリーム
3、ファイルを開く:

#include <stdio.h>

FILE *fopen(const char *path, const char *mode);

modeはpathで表されるファイルを開く方法を説明します.
r:ファイルを開く、読み取り可能、ストリームの位置はファイルの先頭にある
r+:ファイルを開き、読み書きができ、ストリームの位置はファイルの先頭にある
w:ファイルを開き、書き込み可能で、存在する場合は、空にします.作成は存在しません.ストリームの場所はファイルの先頭にあります.
w+:ファイルを開き、読み取り可能、書き込み可能、存在する場合は空、存在しない場合は作成され、ストリームの位置はファイルの先頭にあります.
a:appendモード、書き込み可能、存在しない場合は作成します.ストリームの場所はファイルの最後にあります.
a+:appendモード、作成が存在しない場合は読み取りと書き込みが可能です.ストリームの場所はファイルの最後にあります.
例:

File *fp;

fp = fopen("/etc/manifest","r");
if(! fp )
	/* error */

4、ファイル記述子で開く:

#include <stdio.h>

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

modeの意味はfopenと同様で、w、w+を除いてファイルは空になりません.
例:

FILE *fp;
int fd;

fd = open("/home/kidd/map.txt",O_RDONLY);
if(fd == -1)
  /* error */
fp = fdopen(fd,"r");
if(!fp)
 /* error */

読み取り可能なfdですが、fdopenがmodeをwに設定したらどうなりますか?両者が一致しないとNULLが返され、設定されます.
errnoはEIVALです.
5、ストリームを閉じる:
fcloseはファイルを閉じることができます.

#include <stdio.h>

int fclose(FILE *fp);

bufferと書かれていないデータはまずfushedされます.正常に0を返し、失敗してEOFを返します.
6、すべてのストリームを閉じる:
fcloseallは、現在のプロセスのすべてのストリームを閉じます.

#define _GNU_SOURCE

#include<stdio.h>

int fcloseall(void);

常に0を返します.
7、ストリームから読む:
1)1文字ずつ読みます.
fgetc:

#include <stdio.h>

int fgetc(FILE *fp);

戻り読み出し文字はint値に変換され、主にファイルの最後にEOFを返すことを考慮したため、-1はassciコードの範囲外である.

int c;
while((c = fgetc(fp) != EOF){
	printf("%c
",(char)c); }

2)文字を押し戻す:
ungetc:
この関数によってpeekストリームの文字を読み取ることができ、必要な文字を超えると、
この文字は戻ります.

#include <stdio.h>

int ungetc(int c, FILE *fp);

正常にcを返し、失敗してEOFを返します.
3)1行を読み取る:
fgetsは1行の文字列を読み込むことができます.

#include <stdio.h>

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

読み取りは改行またはEOFに遭遇し、strに成功し、NULLに失敗しました.
例:

char buf[LINE_MAX];//LINE_MAX in <limits.h>du
if(!fgets(buf,LINE_MAX,fp)
	/* error */

4)リードバイナリ数:
fread:

#include <stdio.h>

size_t fread(void *buf, size_t size, size_t nr, FILE *fp);

cの構造体などの複雑なバイナリ数を読み取ることができます.
nr個のデータを読み、各サイズはsizeバイトです.読み込み要素の個数を正常に返し、エラーを示すかEOFを返します.
ただしferror()とfeof()を使用しないと区別できません.
異なる機械の変数の大きさのため、それに対して、補完して、バイトの順序は異なって、機械にまたがる性を持っていません.
例:

char buf[64];
size_t nr;

nr = fread(buf,sizeof(buf),1,fp);
if(nr == 0) 
	/* error */

8、書き込みフロー:
1)文字を書き込みます.

#include <stdio.h>

int fputc(int c, FILE *fp);

cが正常に返され、EOFが失敗しました.

if( fputc('p',fp) ) 
  /* error */

2)文字列を書き込みます.
fputs:

#include <stdio.h>

int fputs(const char *str, FILE *stream);

2)0で終わる文字列を書き込み、非負の数を返すことに成功し、EOFを返すことに失敗しました.
例:

FILE *fp;

if(!(fp = fopen("journal.txt","a"))
  /* error */
if(fputs("The ship is made of wood.
",fp) == EOF) /*error*/ if(fclose(fp) == EOF) /*error*/

3)バイナリデータを書き込む:
fwrtieは、複雑な構造データをファイルに格納することができます.

#include <stdio.h>

size_t fwrite(void *buf,size_t size, size_t nr, FILE *fp);

右bufが指すデータをファイルfpのnr個の要素に書き込み、各要素の長さはsizeバイトである.
9、Buffered I/O使用例:

#include <stdio.h>

int main(void){
	FILE *in,*out;
	struct pirate{
		char name[100];
		unsigned long booty;
		unsigned int beard_len;
	}p,blackbeard = {"Edward Teach",950,48};
	
	out = fopen("data","w");

	if(! out ){
		perror("fopen");
		return 1;
	}

	if(!fwrite(&blackbeard,sizeof(struct pirate),1,out)){
		perror("fwrite");
		return 1;
	}

	if(fclose(out)){
		perror("fclose");
		return 1;
	}

	in = fopen("data","r");
	if( !in ){
		perror("fopen");
		return 1;
	}

	if(! fread(&p, sizeof(struct pirate),1,in)){
		perror("fread");
		return 1;
	}

	if(fclose(in)){
		perror("fclose");
		return 1;
	}

	printf("name=\"%s\" booty=%lu beard_len=%u
",p.name,p.booty,p.beard_len); return 0; }

10、Seeking a Stream
1)fseekは標準IO lseekと類似している:

#include <stdio.h>

int fseek(FILE *fp,long offset, int whence);

whence可取:SEEK_SET、SEEK_CUR,SEEK_END、lseekと同じ意味です.
2)fsetpos:

#include <stdio.h>

int fsetpos(FILE *fp,fpos_t *pos);

とfseekでwhenceをSEEK_に設定SETは同じです.
3)rewind:
ファイル位置をfseek(fp,0,SEEK_SET)に相当するようにリセットします.

#include <stdio.h>

void rewind(FILE *fp);

rewindは値を返さず、errnoで成功したかどうかを判断できます.

errno = 0;
rewind(stream);
if(errno)
  /* error */

11、現在のファイルフローの場所を取得する:
1)ftell:

#include <stdio.h>

long ftell(FILE *stream);

位置の戻りに成功し、-1の戻りに失敗し、errnoが設定されました.
2)fgetpos:

#include <stdio.h>

int fgetpos(FILE *stream, fpos_t *pos);

正常に0を返し、posを設定し、失敗して-1を返します.
12、flush a Stream:
fflush:

#include <stdio.h>

int fflush(FILE *fp);

fpによって示される書き込みされていないすべてのデータは、カーネルのbufferに書き込まれる.
正常に0を返し、失敗してEOFを返し、errnoを設定します.
writeはユーザーのbufferに書き込まれ、fflushはユーザーのbufferのデータをカーネルbufferに書き込むことができます.
ディスクへの書き込みは保証されません.
13、エラーとファイルの末尾:
freadのような標準的なI/Oは、errorとEOFを区別するメカニズムを提供していないため、与えられたストリームがエラーに遭遇したかファイルに遭遇したかを確認します.
エンディングは役に立ちます.標準I/Oには2つのインタフェースがあります.
1)ferror:

#include <stdio.h>

int ferror(FILE *fp);

0以外を返すと設定エラー、0はなしを示します.
2)feof

#include <stdio.h>

int feof(FILE *fp);

テストストリームにEOFが設定されているかどうかをテストし、ゼロ以外の設定を返します.0はありません.
3)エラーとEOF設定をクリアする:

#include<stdio.h>

void clearerr(FILE *fp);

例:

if(ferror(f))
	printf("Error on f!
"); if(feof(f)) printf("EOF on f!
"); clearerr(f);

14、関連するファイル記述子を取得する:

#include <stdio.h>

int fileno(FILE *fp);

関連するファイル記述子を正常に返し、失敗したら-1を返します.
標準I/OとIOシステム呼び出しの混在は推奨されません.
15、制御buffer:
a、標準I/Oは3種類のユーザーBufferを実現した:
1)Unbuffered:
キャッシュがなく、データはカーネルに直接渡されます.stderrはデフォルトでこの方法を使用します.
2)Line-buffered:
bufferは行をキャッシュし、改行に遭遇するとカーネルにコミットします.stdoutではデフォルトでこの方法が使用されます.
3)Block-buffered
ファイルに関するI/OのデフォルトはBlock-bufferedです.
b、フローのbufferタイプを設定する:
setvbuf:

#include <stdio.h>

int setvbuf(FILE *stream, char *buf, int mode, size_t size);

mode:
_IONBF:bufferなし
_IOLBF: Line-buffered
_IOFBF: block-buffered/full buffered
を除いてIONBFのほか、他の2つは、ユーザーがsize独自のbufを提供する必要があります.
提供されたbufferは、ファイルが閉じられている間も表示されます.そうしないと、表示される閉じ操作に失敗します.
次の例ではバグがあります.

#include <stdio.h>

int main(void){
	char buf[BUFSIZ];
	
	setvbuf(stdout,buf,_IOFBF,BUFSIZ);
	printf("Arrr!
"); return 0; }

プログラムが終了すると暗黙的にファイルが閉じられ、失敗します.上のプログラムreturnの前に表示されたcloseを付けてstdoutを閉じるか、
グローバルなbufを使用します.
16、フォーマットI/O:
1)可変パラメータファミリの使用:

#include <stdio.h>

//          ,    -1
int printf(const char *restrict format, ...);
int fprintf(FILE *restrict fp, const char * restrict format,...);

//       buf   ,    ,
int sprintf(char *restrict buf,const char * restrict format,...);
//     buf   ,        
int snprintf(char *restrict buf, size_t n, const char *restrict format,...);
//         buf     ,    -1。

2)va_の使用Listファミリー:
va_Listはで定義します.

#include <stdio.h>
#include <stdarg.h>

int vprintf(const char *restrict format, va_list arg);
int vfprintf(FILE *restrict fp, const char * restrict format,va_list arg);
int vsprintf(char *restrict buf,const char * restrict format,va_list arg);
int vsnprintf(char *restrict buf, size_t n, const char *restrict format,va_list arg);

17、フォーマット入力:
1)可変パラメータファミリの使用:

#include <stdio.h>

int scanf(const char *restrict format,...);
int fscanf(FILE * restrict fp, const char * restrict format,...);
int sscanf(const char *restrict buf, const char *restrict format,...);

2)va_の使用list族

#include <stdio.h>
#include <stdarg.h>

int vscanf(const char *restrict format,va_list arg);
int vfscanf(FILE * restrict fp, const char * restrict format,va_list arg);
int vsscanf(const char *restrict buf, const char *restrict format,va_list arg);

参照先:
1、《Linux system programming》
2、《Unix system programming》
3、《Advanced Programming in the Unix Environment》