I/O 函数 Return value 总结, 字符处理函数 除外
(1) returnType 为 整型/size_t, return value 可能不用于表示个数, 此时一般以 0 表示 success
, nonZero 表示 fail
Return
0 , success
nonZero, else
(2) returnType 为 int/size_t, return value 可能用于表示 个数, 此时一般表示 successful 处理的个数
fread
fwrite
1 类型
1.1 file position indicator: fpos_t // 类型
可用于 store
(被 fgetpos) 和 restore
(被 fsetpos) 文件 position
1.2 size_t
无符号整型
sizeof 结果类型
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标准, `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个参数
buffer == NULL/!= NULL
时, 分别等价于
setvbuf(stream, NULL, _IONBF, 0) // 关 buf
setvbuf(stream, buffer, _IOFBF, BUFSIZ) // 全 buf + BUFSIZ(512 Byte)
Note
[2] 应该用于 stream 关联到 文件后, 任何其他操作前
[3] 常见错误: stdin/stdout 的 buffer 数组 lifetime 先于 程序/main 结束
问题: 程序离开 main 后, 准备 clear buf, 但 buffer lifetime 已结束
#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);
}
应用
[1] 关 stdout buf, 以 立即输出
setbuf(stdout, NULL)
//<=>
setvbuf(stdout, NULL, _IOFBF, 0)` 设为全缓冲
[2] 关 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 缓冲区 为 buf
setbuf(stdout, buf);
// 把 hello\n 放 stdout buf, 这行执行完, 屏幕上 不打印 hello
puts("hello");
// 这行执行完, 屏幕上 打印 hello
fflush(stdout);
}
4 Direct input/output: fread/fwrite
fread
reads from a file
fwrite
writes to a file
file position indicator 自动前移
(1) size_t fread(void* buffer, size_t size, size_t count, FILE* stream);
好像对 每个 object, 调 size 次 fgetc, reinterpreted 为 unsigned char 数组
Return: 成功 read 的 objects 数, 可能 <= count: if error 或 end-of-file occurs
fread 不区分 end-of-file 和 error, callers 必须用 feof 和 ferror 去判断 which occurred
(2) size_t fwrite(const void* buffer, size_t size, size_t count, FILE* stream );
好像对 每个 object, 调 size 次 fputc, 将 size 个 unsigned chars(视为1个object) 写进 stream
Return: 成功 write 的 objects 数, 可能 <= 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) 解析 二进制文件: 知道文件所存 object 类型
fp = fopen("test.bin", "rb");
fseek(fp, 0, SEEK_END); // set stream 上 文件位置指针 = offset/Bytes + whence = 文件尾
size_t fileSize = ftell(fp); // 求 文件位置指针 相对 文件首 偏移 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 数 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 数 1 / 2 => ftell/fgetpos
get stream 文件位置
= 当前文件位置指针 相对 文件首的偏移( Bytes 数 ) / pos 所指处
(4) long int ftell(FILE* stream)
Note: 必须以 二进制 打开
(5) int fgetpos(FILE* stream, fpos_t* pos)
gets file position indicator
刚打开文件时, 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 系统变量 errno 中存储的 `error code 的 文本描述` 到 stderr
print: str + 冒号 + 空格 + 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) 创建临时文件
FILE* tmpf = tmpfile();
fputs("abcde\n", tmpf);
rewind(tmpf);
int ch;
// exit: tmpf 文件指针 已越过 the last character => 到达 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
时, 并 不 set FILE 结构中 EOF flag, 再执行一次 读文件操作, 才会 set EOF flag, 此时调 feof(fp) 才 return true
=>
问题: feof(fp) -> 读 -> print 出错
解决: 读 -> EOF 检测 (!= 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
传输的 字符数,
负数 , output error
Note
(1) printf: 默认 无缓冲
(2) snprintf: size 不应该超过 caller 所控制的 buf 区间
(3) sprintf / snprintf
1) format 中: 最后一个 %s, 无 '\0' -> 加 '\0', 有 '\0' -> 保留; 其他 %s, 去 '\0'
2) 效率 高于 字符串操作函数
3) 更灵活: 可将 想要的结果 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
成功匹配和赋值的个数(>= 0), success/第1个赋值就 matching failure
EOF , input fail 或 end-of-file
Note
[1] 格式字符串中
任何 单个 空白符
会 消耗
输入中 所有 连续空白符
, 好像 循环 isspace
空白符: 空格(" ") 换行("\n") Tab("\t")
[2] 遇 分隔符: 跳过
遇 非 matching 格式的值: 从 input Buffer 跳出
, 不跳过 也不取出
(1) scanf 4步曲
例: 键盘输入 1 2 3
1) 键盘输入
以 字符形式
存入 input Buffer
2) 控制符
%d 将字符 转化为相应格式 的 值 (10进制 123)
对 %d 而言, 空白符
是 data 分隔符
3) 通过 取地址 i(&i)
找到 变量 i 的地址
4) put 值
123 to 变量 i
Note
[1] 除非必要, 双引号内 不要用 非 input 控制符 ( 包括 \n: 不起换行的作用 )
, 否则就是 自找麻烦; 而且 对用户而言, 输入越简单越好
[2] 多变量输入
时, input 控制符 之间
要用 空格 回车 Tab
分隔
[3] scanf/printf
中, 非 I/O 控制符
要 原样 input/output
[4] 每个 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); // 等价于 clear_stdin_cache();
clear_stdin_cache();
count = scanf("a= %d", &a);
if(count > 0)
printf("a= %d\n", a);
}

(2) fscanf
[1] 每读完1项, 遇 分隔符 结束
=> fp 指向 "hello " 时, 用 %s 只能取出 "hello", 空格 要用 %c 取出
[2] 与 fgets
区别: fgets 遇 空格(" ") 不结束
#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);
// 从文件中 读 数据
fscanf(fp, "%s", str); // Note: 遇 空格 结束, fp 移动到 空格 的 next 位置
int pos = ftell(fp);
printf("%d\n", pos); // 5: 空格位置
fscanf(fp, "%2d", &uVar); // 遇 空格 (对 %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: 过滤掉 对应匹配的 %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: 字符处理函数
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 点不同
某些 库中, putc 用 宏实现
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
里奇开发 C 时, 没 terminal input 的概念, 输入
都是从 文件
读取
文件
以 行
为单位, 最后1行 结束后, 还要用 end-of-file
来标志 文件结束
=>
遇 newline("\n", 回车 认为 == "\n")
, 程序才认为 输入 ( scanf getchar gets 等 ) 结束
(1) int fgetc(FILE* stream );
(2) int getc(FILE* stream );
(3) int getchar ( void );
fgetc / getc 仅 1 点不同: 某些 库中, getc 用 宏实现
getchar() <=> getc(stdin)
实现 maybe
#define getchar() getc(stdin)
fgetc() / getc() 经典用法
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 个字符
, 并 回显
到屏幕
EOF: end of file
Windows/Unix 值都是 -1
1) 可作 文本文件 结束标志
文本文件 data 以 `字符 的 ASCII 码值 ( 0-127 ) ` 存放,
无 -1
2) 不可作 二进制文件 结束标志
-> feof(fp) 可判 二进制/文本 文件 是否结束
二进制文件 有 -1
#include <stdio.h>
//文本文件 复制到 另一个文件
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' 被 覆盖
windows 下 输入 EOF 的方法:回车 -> ctrl + z -> 回车
// 经典例子: 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风格字符串
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 个字符`
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);
}
网友评论