I/O 函數(shù) Return value 總結(jié), 字符處理函數(shù) 除外
(1) returnType 為 整型/size_t, return value 可能不用于表示個(gè)數(shù), 此時(shí)一般以 0 表示 success
, nonZero 表示 fail
Return
0 , success
nonZero, else
(2) returnType 為 int/size_t, return value 可能用于表示 個(gè)數(shù), 此時(shí)一般表示 successful 處理的個(gè)數(shù)
fread
fwrite
1 類型
1.1 file position indicator: fpos_t // 類型
可用于 store
(被 fgetpos) 和 restore
(被 fsetpos) 文件 position
1.2 size_t
無符號(hào)整型
sizeof 結(jié)果類型
1.3 FILE: 存 file stream
2 Predefined standard streams
stdin
stdout
stderr
3 File 訪問
fopen // opens a file
fclose // closes a file
fflush // synchronizes an output stream with the actual file
setvbuf // sets the buffer, and its size for a file stream
setbuf // sets the buffer for a file stream
(1) FILE* fopen(const char* filename, const char* mode);
Return
ptr to file stream, success
NULL , else
POSIX requires that `errno be set` in this case
(2) int fclose(FILE* stream );
I/O buf
data 丟掉 / write
from/to stream
(3) int fflush(FILE* stream);
[1] fflush(stdout/outputDevice);
writes unwritten data (from stream's buffer) to associated output device.
[2] fflush(stdin);
the behavior is undefined.
非 C標(biāo)準(zhǔn), `gcc 不支持`
Note: If stream is a null pointer, all open output streams are flushed
(4) int setvbuf(FILE* stream, char* buffer, int bufMode, size_t bufSize );
bufMode: _IOFBF / _IOLBF / _IONBF
(5) void setbuf(FILE* stream, char* buffer);
Sets internal buffer
for stream. 至少 BUFSIZ characters
long
Note
[1] 若 BUFSIZ 并非合適的 buffer size, setvbuf 可被用來 change it
封裝 setvbuf, 只留下 前2個(gè)參數(shù)
buffer == NULL/!= NULL
時(shí), 分別等價(jià)于
setvbuf(stream, NULL, _IONBF, 0) // 關(guān) buf
setvbuf(stream, buffer, _IOFBF, BUFSIZ) // 全 buf + BUFSIZ(512 Byte)
Note
[2] 應(yīng)該用于 stream 關(guān)聯(lián)到 文件后, 任何其他操作前
[3] 常見錯(cuò)誤: stdin/stdout 的 buffer 數(shù)組 lifetime 先于 程序/main 結(jié)束
問題: 程序離開 main 后, 準(zhǔn)備 clear buf, 但 buffer lifetime 已結(jié)束
#include <stdio.h>
int main()
{
int c;
char buf[BUFSIZ];
setbuf(stdout,buf);
while( (c=getchar())!= EOF)
putchar(c);
}
gcc 下 匯編可看出問題
solution: buffer array 放 main 之外
#include <stdio.h>
char buf[BUFSIZ];
int main()
{
int c;
setbuf(stdout,buf);
while( (c=getchar())!= EOF)
putchar(c);
}
應(yīng)用
[1] 關(guān) stdout buf, 以 立即輸出
setbuf(stdout, NULL)
//<=>
setvbuf(stdout, NULL, _IOFBF, 0)` 設(shè)為全緩沖
[2] 關(guān) stdin buf, 即 清空 stdin buf
setbuf(stdin, NULL)
// <=>
void clear_stdin_buf()
{
int ch;
while( (ch = getchar() ) != '\n' && ch != EOF)
;
}
#include <stdio.h>
char buf[BUFSIZ];
int main()
{
// 指定 stdout 緩沖區(qū) 為 buf
setbuf(stdout, buf);
// 把 hello\n 放 stdout buf, 這行執(zhí)行完, 屏幕上 不打印 hello
puts("hello");
// 這行執(zhí)行完, 屏幕上 打印 hello
fflush(stdout);
}
4 Direct input/output: fread/fwrite
fread
reads from a file
fwrite
writes to a file
file position indicator 自動(dòng)前移
(1) size_t fread(void* buffer, size_t size, size_t count, FILE* stream);
好像對(duì) 每個(gè) object, 調(diào) size 次 fgetc, reinterpreted 為 unsigned char 數(shù)組
Return: 成功 read 的 objects 數(shù), 可能 <= count: if error 或 end-of-file occurs
fread 不區(qū)分 end-of-file 和 error, callers 必須用 feof 和 ferror 去判斷 which occurred
(2) size_t fwrite(const void* buffer, size_t size, size_t count, FILE* stream );
好像對(duì) 每個(gè) object, 調(diào) size 次 fputc, 將 size 個(gè) unsigned chars(視為1個(gè)object) 寫進(jìn) stream
Return: 成功 write 的 objects 數(shù), 可能 <= count: if error occurs
#include <stdio.h>
#include <stdlib.h> // malloc
enum { SIZE = 3 };
int main(void)
{
double a[SIZE] = { 1.,2.,3. };
FILE* fp = fopen("test.bin", "wb"); // must use binary mode
fwrite(a, sizeof(*a), SIZE, fp); // writes an array of doubles
fclose(fp);
// === (2) 解析 二進(jìn)制文件: 知道文件所存 object 類型
fp = fopen("test.bin", "rb");
fseek(fp, 0, SEEK_END); // set stream 上 文件位置指針 = offset/Bytes + whence = 文件尾
size_t fileSize = ftell(fp); // 求 文件位置指針 相對(duì) 文件首 偏移 Bytes = fileSize
rewind(fp); // set stream 上 文件位置指針 = 文件首
size_t objectsCount = fileSize / sizeof(double);
double* buf = (double *)malloc(fileSize); // allocate buf memory
size_t ret = fread(buf, sizeof(double), objectsCount, fp);
if (ret == objectsCount)
{
puts("Array read successfully, contents: ");
for (size_t i = 0; i < objectsCount; ++i)
printf("%f ", buf[i]);
putchar('\n');
}
else // error handling
{
if (feof(fp)) // 1) EOF
printf("Error reading test.bin: unexpected end of file\n");
else if (ferror(fp)) // 2) error
perror("Error reading test.bin");
}
free(buf);
fclose(fp);
}
Array read successfully, contents:
1.000000 2.000000 3.000000
5 文件定位
ftell
returns the current file position indicator
fgetpos
gets the file position indicator
fseek
moves the file position indicator to a specific location in a file
fsetpos
moves the file position indicator to a specific location in a file
rewind
moves the file position indicator to the beginning in a file
set
文件位置: para 數(shù) 1 / 2 / 3 => rewind / fsetpos / fseek
set stream 文件位置指針 = 文件頭 / pos 所指處 / offset + whence 處
(1) void rewind(FILE* stream)
(2) int fsetpos(FILE* stream, const fpos_t* pos)
moves file position indicator to 文件中 特定位置
If a read or write error
occurs, the error indicator (ferror)
for the stream is set
(3) int fseek(FILE* stream, long int offset, int whence)
whence = SEEK_SET/SEEK_CUR/SEEK_END
get
文件位置: para 數(shù) 1 / 2 => ftell/fgetpos
get stream 文件位置
= 當(dāng)前文件位置指針 相對(duì) 文件首的偏移( Bytes 數(shù) ) / pos 所指處
(4) long int ftell(FILE* stream)
Note: 必須以 二進(jìn)制 打開
(5) int fgetpos(FILE* stream, fpos_t* pos)
gets file position indicator
剛打開文件時(shí), pos 記錄文件起始位置: fpos_t pos; fgetpos(fp, &pos);
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
int main(void)
{
enum { SIZE = 3 };
// (1) fwrite a file
FILE* fp = fopen("test.bin", "wb");
assert(fp);
double arr[SIZE] { 1.1, 2.2, 3.3};
int rc = fwrite(arr, sizeof(double), SIZE, fp);
assert(rc == SIZE);
fclose(fp);
// (2) fread one by one
fp = fopen("test.bin", "rb");
double d;
fpos_t pos;
fgetpos(fp, &pos); // [1] store start pos of file
rc = fread(&d, sizeof(d), 1, fp); // [2] read first double
assert(rc == 1);
printf("First value in the file: %.1f\n", d);
if(fsetpos(fp, &pos) != 0) // [3] set/move file position back to start of file
{
if (ferror(fp))
{
perror("fsetpos()");
fprintf(stderr,"fsetpos() failed in file %s at line # %d\n", __FILE__,__LINE__-5);
exit(EXIT_FAILURE);
}
}
rc = fread(&d, sizeof(d), 1, fp); // [4] read again: the first double
assert(rc == 1);
printf("First value in the file again: %.1f\n", d);
rc = fread(&d, sizeof(d), 1, fp); // [5] read next elem: the second double
assert(rc == 1);
printf("Second value in the file: %.1f\n", d);
fclose(fp);
// (3) demo error handling
rc = fsetpos(stdin, &pos);
if (rc)
perror("could not fsetpos stdin");
}
First value in the file: 1.1
First value in the file again: 1.1
Second value in the file: 2.2
could not fsetpos stdin: Bad file descriptor
6 Error handling
clearerr
clears errors
feof
checks for the end-of-file
ferror
checks for a file error
perror
`displays` a character string corresponding of the `current error to stderr`
feof / ferror 返回類型 int
return
NonZero, EOF Flag / Error Flag is set
0, false
(1) void clearerr(FILE* stream)
Resets(重置 <=> 取消) EOF indicator 和 Error flag(for given stream)
(2) int feof(FILE* stream)
Checks if EOF indicator (associated with stream) is set
<=>
Checks if `end of (the given file) stream has been reached`
(3) int ferror(FILE* stream)
Check if Error flags is set
<=>
Checks the given stream for errors.
(4) void perror(const char *str)
print 系統(tǒng)變量 errno 中存儲(chǔ)的 `error code 的 文本描述` 到 stderr
print: str + 冒號(hào) + 空格 + error code 的 文本描述
#include <stdio.h>
#include <stdlib.h>
// #include <assert.h>
int main(void)
{
// === (1) 打開 不存在的文件
FILE* pf= fopen("non_existent", "r");
if (pf == NULL)
perror("fopen() failed");
else
fclose(pf);
// === (2) 創(chuàng)建臨時(shí)文件
FILE* tmpf = tmpfile();
fputs("abcde\n", tmpf);
rewind(tmpf);
int ch;
// exit: tmpf 文件指針 已越過 the last character => 到達(dá) EOF <=> EOF indicator is set
while ( (ch = fgetc(tmpf) ) != EOF)
printf("%c", ch);
if ( feof(tmpf) ) // feof(tmpf) == true/nonZero // assert( feof(pf) ); // true, assert 順利通過
puts("EOF indicator is set <=> EOF reached successfully");
else if (ferror(tmpf) )
puts("I/O error when reading");
clearerr(tmpf); // clear eof
if ( feof(tmpf) )
puts("EOF indicator set");
else
puts("EOF indicator cleared\n");
}
Note: 文件位置指針 指向 end-of-file
時(shí), 并 不 set FILE 結(jié)構(gòu)中 EOF flag, 再執(zhí)行一次 讀文件操作, 才會(huì) set EOF flag, 此時(shí)調(diào) feof(fp) 才 return true
=>
問題: feof(fp) -> 讀 -> print 出錯(cuò)
解決: 讀 -> EOF 檢測(cè) (!= EOF 或 !feof(fp) )-> print
solution1: 讀 -> while( ! feof(fp) ) -> print -> 讀
solution2: while (讀 != EOF) -> print
// === Prob:
#include <stdio.h>
int main()
{
FILE *fp=NULL;
int ch;
fp = fopen("myfile.txt","w");
fputs("abc", fp); // {a, b, c, end-of-file}
fclose(fp);
fp = fopen("myfile.txt","r");
if(fp == NULL)
perror ("Error opening file");
// fp: 指向 'c'
// feof -> 讀 -> 輸出
// feof(fp): return false -> 讀 'c': fp 移到 end-of-file -> 輸出 'c'
// feof(fp): return false -> 讀 end-of-file: p 不移, set EOF flag -> 輸出 EOF
// feof(fp): return true -> exit
while( !feof(fp) )
{
ch = fgetc(fp);
printf("%c: %x\n", ch, ch);
}
fclose(fp);
}
a: 61
b: 62
c: 63
: ffffffff // EOF 的 hex 值 = 0xffffffff = -1
// === solution1:
#include <stdio.h>
int main()
{
FILE *fp=NULL;
int ch;
fp = fopen("myfile.txt","w");
fputs("abc", fp); // {a, b, c, end-of-file}
fclose(fp);
fp=fopen("myfile.txt","r");
if(fp == NULL)
perror ("Error opening file");
ch = getc ( fp );
while( !feof(fp) )
{
printf("%c: %x\n", ch, ch);
ch = getc(fp);
}
fclose(fp);
}
// === solution2:
#include <stdio.h>
int main()
{
FILE* fp = NULL;
int ch;
fp = fopen("myfile.txt", "w");
fputs("abc", fp); // {a, b, c, end-of-file}
fclose(fp);
fp = fopen("myfile.txt", "r");
if (fp == NULL)
perror("Error opening file");
while ( (ch = fgetc(fp) ) != EOF)
printf("%c: %x\n", ch, ch);
if (fp)
fclose(fp);
}
a: 61
b: 62
c: 63
7 Operations on files
remove
erases a file
rename
renames a file
tmpfile
returns a pointer to a temporary file
(1) int remove(const char* filename)
(2) int rename(const char* oldFilename, const char* newFilename)
(3) FILE* tmpfile(void)
8 格式化 IO
8.1 從右到左:
將 data 按 格式化輸出
write 到 stream / stdout / buf(char*) / buf
int fprintf (FILE* stream, const char* format, ...)
int printf ( const char* format, ...)
int sprintf (char* buf, const char* format, ...)
int snprintf(char* buf, size_t size, const char* format, ...)
Return
傳輸?shù)?字符數(shù),
負(fù)數(shù) , output error
Note
(1) printf: 默認(rèn) 無緩沖
(2) snprintf: size 不應(yīng)該超過 caller 所控制的 buf 區(qū)間
(3) sprintf / snprintf
1) format 中: 最后一個(gè) %s, 無 '\0' -> 加 '\0', 有 '\0' -> 保留; 其他 %s, 去 '\0'
2) 效率 高于 字符串操作函數(shù)
3) 更靈活: 可將 想要的結(jié)果 write到 指定 buf
#include <stdio.h>
int main()
{
FILE *fp;
fp = fopen( "file.txt", "w" );
char name[20] = "xianyang";
if( fp != NULL )
fprintf( fp, "hello %s\n", name );
fclose(fp);
}
#include <stdio.h>
int main()
{
char buf[10];
char str1[] = {'a', 'b', 'c'};
char str2[] = {'d', 'e', 'f'};
sprintf(buf, "%.3s%.3s\n", str1, str2);
printf("%s", buf);
//sprintf(buf, "%s%s", str1, str2); //Don't do that!
}
abcdef
8.2 從 stream / stdin / buf 讀 格式化輸入
到 指定變量
int fscanf(FILE* stream, const char* format, ...)
int scanf ( const char* format, ...)
int sscanf(const char* str, const char* format, ...)
Return
成功匹配和賦值的個(gè)數(shù)(>= 0), success/第1個(gè)賦值就 matching failure
EOF , input fail 或 end-of-file
Note
[1] 格式字符串中
任何 單個(gè) 空白符
會(huì) 消耗
輸入中 所有 連續(xù)空白符
, 好像 循環(huán) isspace
空白符: 空格(" ") 換行("\n") Tab("\t")
[2] 遇 分隔符: 跳過
遇 非 matching 格式的值: 從 input Buffer 跳出
, 不跳過 也不取出
(1) scanf 4步曲
例: 鍵盤輸入 1 2 3
1) 鍵盤輸入
以 字符形式
存入 input Buffer
2) 控制符
%d 將字符 轉(zhuǎn)化為相應(yīng)格式 的 值 (10進(jìn)制 123)
對(duì) %d 而言, 空白符
是 data 分隔符
3) 通過 取地址 i(&i)
找到 變量 i 的地址
4) put 值
123 to 變量 i
Note
[1] 除非必要, 雙引號(hào)內(nèi) 不要用 非 input 控制符 ( 包括 \n: 不起換行的作用 )
, 否則就是 自找麻煩; 而且 對(duì)用戶而言, 輸入越簡(jiǎn)單越好
[2] 多變量輸入
時(shí), input 控制符 之間
要用 空格 回車 Tab
分隔
[3] scanf/printf
中, 非 I/O 控制符
要 原樣 input/output
[4] 每個(gè) scanf 前 clear stdin Buffer
, 可從 無殘留的 stdin Buffer
讀取
#include <stdio.h>
// empty stdin_cache
void clear_stdin_cache()
{
int ch;
while( (ch = getchar() ) != '\n' && ch != EOF)
;
}
int main()
{
int a;
int count;
scanf("%d", &a);
printf("%d\n", a);
// 清空 stdin 緩存
// setbuf(stdin, NULL); // 等價(jià)于 clear_stdin_cache();
clear_stdin_cache();
count = scanf("a= %d", &a);
if(count > 0)
printf("a= %d\n", a);
}
(2) fscanf
[1] 每讀完1項(xiàng), 遇 分隔符 結(jié)束
=> fp 指向 "hello " 時(shí), 用 %s 只能取出 "hello", 空格 要用 %c 取出
[2] 與 fgets
區(qū)別: fgets 遇 空格(" ") 不結(jié)束
#include <stdio.h>
int main()
{
int uVar;
float fVar;
char str[100];
char ch;
FILE* fp;
fp = fopen("file.out", "w+");
if (fp == NULL)
printf("The file fscanf.out was not opened\n");
else
{
fprintf(fp, "%s %d%f%c", "hello", 10, 3.14159, 'x');
// set 指針至文件首
fseek(fp, 0L, SEEK_SET);
// 從文件中 讀 數(shù)據(jù)
fscanf(fp, "%s", str); // Note: 遇 空格 結(jié)束, fp 移動(dòng)到 空格 的 next 位置
int pos = ftell(fp);
printf("%d\n", pos); // 5: 空格位置
fscanf(fp, "%2d", &uVar); // 遇 空格 (對(duì) %s %d 來說是 分隔符): 跳過
fscanf(fp, "%f", &fVar);
fscanf(fp, "%c", &ch);
printf("%s\n", str);
printf("%d\n", uVar);
printf("%f\n", fVar);
printf("%c\n", ch);
fclose(fp);
}
}
5
hello
10
3.141590
x
(3) sscanf: %*s: 過濾掉 對(duì)應(yīng)匹配的 %s
#include <stdio.h>
int main()
{
char buf[50];
// 給定字符串 "hello, world", 僅保留 "world"
sscanf("hello, world", "%*s%s", buf); // 過濾掉 "hello,"
printf("%s\n", buf); //world
}
9 非格式化 I/O: 字符處理函數(shù)
9.1 fputc / putc / putchar: Writes a character
to stream / stream / stdout
+ advances position indicator
of stream
return
寫入的 字符, success
EOF, write error-> set Error Flag
(1) int fputc(int ch, FILE* stream)
(2) int putc (int ch, FILE* stream)
(3) int putchar(int ch)
fputc / putc 僅 1 點(diǎn)不同
某些 庫(kù)中, putc 用 宏實(shí)現(xiàn)
putahcr() <=> getc( stdout )
9.2 fgetc / getc / getchar: Read
a character from stream/stream/stdin
return
所讀到的 character
提升為 int 值, 以 `include EOF 值`, success
`file position indicator` 移到 `next character`
EOF, 遇 end-of-file / read Error
=> `sets EOF/Error Flags => feof() / ferror() == true
Note
里奇開發(fā) C 時(shí), 沒 terminal input 的概念, 輸入
都是從 文件
讀取
文件
以 行
為單位, 最后1行 結(jié)束后, 還要用 end-of-file
來標(biāo)志 文件結(jié)束
=>
遇 newline("\n", 回車 認(rèn)為 == "\n")
, 程序才認(rèn)為 輸入 ( scanf getchar gets 等 ) 結(jié)束
(1) int fgetc(FILE* stream );
(2) int getc(FILE* stream );
(3) int getchar ( void );
fgetc / getc 僅 1 點(diǎn)不同: 某些 庫(kù)中, getc 用 宏實(shí)現(xiàn)
getchar() <=> getc(stdin)
實(shí)現(xiàn) maybe
#define getchar() getc(stdin)
fgetc() / getc() 經(jīng)典用法
int c; // (1) note: int, not char, required to handle EOF
// (2) standard C I/O file reading loop
while ( (c = fgetc(fp) ) != EOF ) // 用 getc 也可
{
putchar(c);
}
if ( ferror(fp) )
{
puts("I/O error when reading");
}
else if ( feof(fp) )
{
puts("End-of-file reached successfully");
}
getchar: 回車后, '\n' 及 之前字符 放 stdin buf
, getchar 才開始 從 stdin buf 讀 1 個(gè)字符
, 并 回顯
到屏幕
EOF: end of file
Windows/Unix 值都是 -1
1) 可作 文本文件 結(jié)束標(biāo)志
文本文件 data 以 `字符 的 ASCII 碼值 ( 0-127 ) ` 存放,
無 -1
2) 不可作 二進(jìn)制文件 結(jié)束標(biāo)志
-> feof(fp) 可判 二進(jìn)制/文本 文件 是否結(jié)束
二進(jìn)制文件 有 -1
#include <stdio.h>
//文本文件 復(fù)制到 另一個(gè)文件
void filecopy( FILE *fpin, FILE *fpout )
{
int ch;
ch = getc ( fpin );
while ( !feof( fpin ) )
{
putc( ch, fpout );
ch = getc ( fpin ); // fp indicator 指向 EOF -> getc -> return EOF + set feof -> exit while
}
}
int main ()
{
FILE*fpin,*fpout;
fpin = fopen ("file1.dat","r");
fpout = fopen ("file2.dat","w");
filecopy( fpin,fpout );
fclose ( fpin );
fclose ( fpout );
}
文件1 依次讀取:
a(97) b(98) \n(10) c(99) -1(end-of-file)
=> windows: 行尾 只有 '\n', 可認(rèn)為 下一行首 '\r' 被 覆蓋
windows 下 輸入 EOF 的方法:回車 -> ctrl + z -> 回車
// 經(jīng)典例子: copy input to output
#include <stdio.h>
#include <ctype.h>
int main()
{
int ch;
while( ( ch = getchar() ) != EOF )
putchar( tolower( ch ) );
}
9.3 fputs / puts: Write C風(fēng)格字符串
to stream / stdout
Note:
[1] not Write '\0'
[2] fputs/puts: (file)不加 / (stdout)加 newline("\n")
Return
>= 0, success
EOF, error -> + sets Error Flag
(1) int fputs(const char* buf, FILE* stream);
(2) int puts (const char* buf);
9.4 fgets / gets: 從 stream/stdin Read a line(1行)
Note:
[1] fgets/gets: newline
copied into buf / 去 newline
( 遇 newline, not copied into buf )
[2] 加 '\0'
return
buf, success
NULL, end-of-file 或 read error -> + `sets EOF/Error Flags
(1) char* fgets(char* buf, int n, FILE* stream)
讀 `stream`, until
1) `讀 n-1 個(gè)字符`
2) 遇 newline
3) 遇 end-of-file
whichever happens first
(2) char* gets (char* buf) // stdin
讀 `stdin`, until
1) 遇 newline
2) 遇 end-of-file
whichever happens first
#include <stdio.h>
int main()
{
char str[50];
gets(str);
printf("%s", str);
}