这部分之前有所总结:
参看:C语言再学习 -- 文件
一、流
-
#include <wchar.h>
-
int fwide(FILE *stream, int mode);
1、参数解析
2、函数功能
3、返回值
4、示例说明
-
#include<stdio.h>
-
#include <wchar.h>
-
int main()
-
{
-
int res = fwide (
stdin,
6);
-
printf(
"%d\n",res);
-
return
0;
-
}
-
输出结果:
-
1
二、FILE 对象
当打开一个流时,标准 I/O 函数 fopen 返回一个指向 FILE 对象的指针。该对象通常是一个结构,它包含了标准 I/O 库为管理该流需要的所有信息,包括用于实际 I/O 的文件描述符,指向用于该流缓冲区的指针、缓冲区的长度、当前在缓冲区中的字符数以及出错标志等。
-
struct _IO_FILE {
-
int _flags;
/* High-order word is _IO_MAGIC; rest is flags. */
-
#define _IO_file_flags _flags
-
-
/* The following pointers correspond to the C++ streambuf protocol. */
-
/* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
-
char* _IO_read_ptr;
/* Current read pointer */
-
char* _IO_read_end;
/* End of get area. */
-
char* _IO_read_base;
/* Start of putback+get area. */
-
char* _IO_write_base;
/* Start of put area. */
-
char* _IO_write_ptr;
/* Current put pointer. */
-
char* _IO_write_end;
/* End of put area. */
-
char* _IO_buf_base;
/* Start of reserve area. */
-
char* _IO_buf_end;
/* End of reserve area. */
-
/* The following fields are used to support backing up and undo. */
-
char *_IO_save_base;
/* Pointer to start of non-current get area. */
-
char *_IO_backup_base;
/* Pointer to first valid character of backup area */
-
char *_IO_save_end;
/* Pointer to end of non-current get area. */
-
-
struct _IO_marker *_markers;
-
-
struct _IO_FILE *_chain;
-
-
int _fileno;
-
#if 0
-
int _blksize;
-
#else
-
int _flags2;
-
#endif
-
_IO_off_t _old_offset;
/* This used to be _offset but it's too small. */
-
-
#define __HAVE_COLUMN /* temporary */
-
/* 1+column number of pbase(); 0 is unknown. */
-
unsigned
short _cur_column;
-
signed
char _vtable_offset;
-
char _shortbuf[
1];
-
-
/* char* _save_gptr; char* _save_egptr; */
-
-
_IO_lock_t *_lock;
-
#ifdef _IO_USE_OLD_IO_FILE
-
};
在 /usr/include/stdio.h 也可以看到对标准输入、标准输出、标准错误的定义。
-
/* Standard streams. */
-
extern
struct _IO_FILE *stdin;
/* Standard input stream. */
-
extern
struct _IO_FILE *stdout;
/* Standard output stream. */
-
extern
struct _IO_FILE *stderr;
/* Standard error output stream. */
-
-
/* C89/C99 say they're macros. Make them happy. */
-
#define stdin stdin
-
#define stdout stdout
-
#define stderr stderr
三、缓冲
标准 I/O 库提供缓冲的目的是尽可能减少使用 read 和 write 调用的次数。它也对每个 I/O 流自动地进行缓冲管理,从而避免了应用程序需要考虑这一点所带来的麻烦。1、标准 I/O 提供了以下 3 种类型的缓冲。
(1)全缓冲
(2)行缓冲
(3)不带缓冲
2、ISO要求下列缓冲特征
3、函数 setbuf 和 setvbuf
四、打开流
-
#include <stdio.h>
-
FILE *fopen(const char *path, const char *mode);
-
FILE *fdopen(int fd, const char *mode);
-
FILE *freopen(const char *path, const char *mode, FILE *stream);
1、参数解析
mode:C 字符串,包含了文件访问模式,模式如下:
模式字符串 |
|
“r” | 以只读方式打开文件,该文件必须存在 |
“r+” | 以只读写方式打开文件,该文件必须存在 |
“w” | 打开只写文件,若文件存在则文件长度清零,即该文件内容会消失。 若文件不存在则建立该文件 |
“w+” | 打开可读写文件,若文件存在则文件长度清零,即该文件内容会消失。 若文件不存在则建立该文件。 |
“a” | 以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在, 写入的数据会被加到文件尾,即文件原先的内容会被保留。(EOF符保留) |
“a+” | 以附加方式打开可读写的文件。若文件不存在,则会建立文件,如果文件存在, 写入的数据会被加到文件尾后,即文件原先的内容会被保留。(原来的EOF符不保留) |
“rb”, “wb”, “ab”, “ab+”, “a+b”, “wb+”, “w+b”, “ab+”, “a+b” | 与前面的模式相似,只是使用二进制模式而非文本模式打开文件 |
2、函数功能
3、返回值
4、示例说明
-
//示例一 fopen函数
-
#include<stdio.h>
-
#include <stdlib.h>
-
-
int main (void)
-
{
-
FILE*fp =
NULL;
-
fp = fopen(
"abc.txt",
"r");
-
if(
NULL == fp)
-
perror(
"fail to fopen"),
exit (
1);
-
-
fclose (fp);
-
fp =
NULL;
-
return
0;
-
}
-
//示例二 freopen 函数
-
#include<stdio.h>
-
#include <stdlib.h>
-
-
int main (void)
-
{
-
FILE*fp =
NULL;
-
fp = freopen(
"abc.txt",
"w",
stdout);
-
if(
NULL == fp)
-
perror(
"fail to freopen"),
exit (
1);
-
-
printf (
"hello world!\n");
-
-
fclose (
stdout);
-
fclose (fp);
-
fp =
NULL;
-
return
0;
-
}
-
查看 abc.txt
-
# cat abc.txt
-
hello world!
-
//示例三 fdopen 函数
-
#include <stdio.h>
-
#include <unistd.h>
-
-
int main (void)
-
{
-
FILE * fp = fdopen (STDOUT_FILENO,
"w+");
-
fprintf (fp,
"%s\n",
"hello!");
-
fclose(fp);
-
-
return
0;
-
}
-
输出结果:
-
hello!
5、示例总结
6、扩展部分
五、读和写流 (文件输入/输出)
1、输入函数
-
#include <stdio.h>
-
int fgetc(FILE *stream);
-
int getc(FILE *stream);
-
int getchar(void);
(1)返回值
成功返回下一个字符,若已到达文件尾端或出错,则返回 EOF(2)函数比较
最简单的如++i,这个表达式执行后,i 的值会改变,这样的表达式是不应该在宏调用里出现的。
(3)示例说明
-
//示例一 fgetc 函数
-
#include <stdio.h>
-
-
int main (void)
-
{
-
FILE *fp;
-
int c;
-
fp = fopen (
"abc.txt",
"r");
-
while ((c = fgetc (fp)) != EOF)
-
{
-
if (c ==
'b')
-
fputc (c,
stdout);
-
}
-
printf (
"\n");
-
fclose (fp);
-
return
0;
-
}
-
输出结果:
-
b
-
//示例二 getc 函数
-
#include<stdio.h>
-
int main()
-
{
-
char c;
-
-
printf(
"请输入字符:");
-
c = getc(
stdin);
//等同函数 getchar
-
printf(
"输入的字符:");
-
putc(c,
stdout);
-
-
return(
0);
-
}
-
输出结果:
-
请输入字符:f
-
输入的字符:f
-
//示例三 getchar 函数
-
#include <stdio.h>
-
-
int main (void)
-
{
-
int c;
-
while((c=getchar())!=EOF)
-
putchar(c);
-
return
0;
-
}
(4)示例总结
2、输出函数
-
#include <stdio.h>
-
int fputc(int c, FILE *stream);
-
int putc(int c, FILE *stream);
-
int putchar(int c);
(1)函数比较
putchar函数,输出到显示器
putc函数,将字符输入到文件
把stdout作为putc()函数的第二个参数。stdout是在stdout中定义的与标准输出相关的文件指针,所以putc (ch, stdout) 和 putchar ( )的作用是一样的。
(2)示例说明 同上
六、每次一行 I/O (字符串输入/输出)
1、下面两个函数提供了每次输入一行的功能。
-
#include <stdio.h>
-
char *fgets(char *s, int size, FILE *stream);
-
char *gets(char *s);
(1)函数比较
(2)示例说明
-
//示例一 fgets函数
-
#include <stdio.h>
-
int main (void)
-
{
-
char name [
20];
-
char *ptr;
-
ptr = fgets (name,
20,
stdin);
-
printf (
"%s?, hi %s!\n", name, ptr);
-
return
0;
-
}
-
输出结果:
-
JOY
-
JOY
-
?, hi JOY
-
!
-
//示例二 gets 函数
-
#include <stdio.h>
-
int main (void)
-
{
-
char name[
50];
-
char *ptr;
-
while ((ptr = gets (name)) !=
NULL)
-
{
-
printf (
"name is %s\n", name);
-
printf (
"ptr is %s\n", ptr);
-
break;
-
}
-
return
0;
-
}
-
输出结果:
-
HELLO
-
name is HELLO
-
ptr is HELLO
2、fputs 和 puts 提供每次输出一行的功能
-
#include <stdio.h>
-
int fputs(const char *s, FILE *stream);
-
int puts(const char *s);
(1)函数比较
函数 fputs 将一个以 null 字节终止的字符串写到指定的流,尾端的终止符 null 不写出。注意,这并不一定是每次输出一行,因为字符串不需要换行符作为最后一个非 null 字节。通常,在 null 字节之前是一个换行符,但并不要求总是如此。(2)示例说明
-
//示例一 fputs函数
-
#include <stdio.h>
-
int main (void)
-
{
-
char name [
20];
-
fgets (name,
20,
stdin);
-
fputs (name,
stdout);
-
return
0;
-
}
-
输出结果:
-
hello
-
hello
-
//示例二 puts函数
-
#include <stdio.h>
-
int main (void)
-
{
-
char name[
50];
-
char *ptr;
-
while ((ptr = gets (name)) !=
NULL)
-
{
-
puts (name);
-
}
-
return
0;
-
}
七、二进制 I/O
-
#include <stdio.h>
-
size_t fread(
void* ptr,
size_t size,
size_t nmemb, FILE* stream);
-
size_t fwrite(
const
void* ptr,
size_t size,
size_t nmemb, FILE* stream);
1、函数用法
这些函数有以下两种常见的用法。(1)读或写一个二进制数组。
-
//fwrite 函数
-
#include <stdio.h>
-
#include <stdlib.h>
-
-
int main (void)
-
{
-
FILE *fp = fopen (
"abc.dat",
"wb");
-
char data[
10] =
"123456789";
-
if (fwrite (&data[
2],
sizeof (
char),
4, fp) !=
4)
-
perror (
"fail to fwrite"),
exit (
1);
-
return
0;
-
}
-
查看 abc.data
-
# cat abc.dat
-
3456
-
//fread 函数
-
#include <stdio.h>
-
int main()
-
{
-
int arr[
5]={},size=
0,num=
0;
//不要老是打错了好吗,仔细仔细再仔细。
-
FILE *p_file=fopen(
"a.bin",
"r");
-
if(p_file)
-
{
-
-
-
size=fread(arr,
sizeof(
int),
5,p_file);
//就是个数
-
printf(
"size是%d\n",size);
-
for(num=
0;num<=
4;num++)
-
{
-
printf(
"%d ",arr[num]);
-
}
-
printf(
"\n");
-
fclose(p_file);
-
p_file=
NULL;
-
}
-
return
0;
-
}
(2)读或写一个结构。例如,可以编写如下程序:
-
//fwrite 函数
-
#include <stdio.h>
-
#include <string.h>
-
#include <stdlib.h>
-
-
typedef
struct
-
{
-
int n;
-
float m;
-
char name[
20];
-
}Ptr;
-
-
int main (void)
-
{
-
Ptr p;
-
strcpy (p.name,
"hello");
//注意字符串不能直接赋值
-
p.n =
11;
-
p.m =
12.9;
-
-
FILE *fp = fopen (
"abc.dat",
"wb");
-
-
if (fwrite (&p,
sizeof (Ptr),
1, fp) !=
1)
-
perror (
"fail to fwrite"),
exit (
1);
-
-
return
0;
-
}
-
//fread 函数
-
#include <stdio.h>
-
#include <stdlib.h>
-
#include <string.h>
-
typedef
struct
-
{
-
int id;
-
float salary;
-
char name[
20];
-
}person;
-
int main()
-
{
-
person p={};
-
FILE *p_file=fopen(
"person.bin",
"rb");
-
if(p_file)
-
{
-
while(
1)
-
{
-
if(!fread(&p,
sizeof(person),
1,p_file))
-
{
-
break;
-
}
-
fread(&p,
sizeof(person),
1,p_file);
//二进制写操作
-
printf(
"id是%d,工资是%g,姓名是%s\n",p.id,p.salary,p.name);
-
}
-
fclose(p_file);
-
p_file=
NULL;
-
}
-
return
0;
-
}
2、使用二进制 I/O 的基本问题
八、定位流
1、ftell 和 fseek 函数。
-
#include <stdio.h>
-
int fseek(FILE *stream, long offset, int whence);
-
long ftell(FILE *stream);
-
void rewind(FILE *stream);
2、ftello 和 fseeko 函数。
-
#include <stdio.h>
-
int fseeko(FILE *stream, off_t offset, int whence);
-
off_t ftello(FILE *stream);
3、fgetpos 和 fsetpos 函数
-
#include <stdio.h>
-
-
int fgetpos(FILE *stream, fpos_t *pos);
-
int fsetpos(FILE *stream, fpos_t *pos);
-
返回值:成功返回
0,失败返回非
0
(1)示例说明
-
#include <stdio.h>
-
#include <string.h>
-
-
int main (void)
-
{
-
FILE *fp;
-
fpos_t filepos;
-
fp = fopen (
"abc.txt",
"w+");
-
-
fgetpos (fp, &filepos);
-
fputs (
"hello world!", fp);
-
-
fsetpos (fp, &filepos);
-
fputs (
"这将覆盖之前的内容", fp);
-
-
fclose (fp);
-
return
0;
-
}
-
查看 abc.ttx
-
# cat abc.txt
-
这将覆盖之前的内容
(2)示例总结
首先我们使用 fgetpos() 函数获取文件的初始位置,接着我们向文件写入 Hello, World!,然后我们使用 fsetpos() 函数来重置写指针到文件的开头,重写文件。九、格式化 I/O
1、格式化输出
-
#include <stdio.h>
-
-
int printf(const char *format, ...);
-
int fprintf(FILE *stream, const char *format, ...);
-
int dprintf(int fd, const char *format, ...);
-
int sprintf(char *str, const char *format, ...);
-
int snprintf(char *str, size_t size, const char *format, ...);
(1)函数比较
printf 将格式化数据写到标准输出;(2)示例说明
-
//示例一 fprintf 函数
-
#include <stdio.h>
-
int main()
-
{
-
FILE *p_file = fopen(
"b.txt",
"w");
-
if(p_file)
-
{
//fprintf函数可以把数据按照格式记录到文本文件中
-
fprintf(p_file,
"%c,%g,%d\n",
'c',
3.14,
46);
-
fclose(p_file);
-
p_file=
NULL;
-
}
-
return
0;
-
}
-
查看 b.txt
-
# cat b.txt
-
c,
3.14,
46
-
//示例二 dprintf 函数
-
#include <stdio.h>
-
#include <sys/stat.h>
-
#include <sys/types.h>
-
#include <fcntl.h>
-
#include <stdlib.h>
-
-
int main (void)
-
{
-
int fd = open (
"a.txt", O_WRONLY | O_CREAT | O_TRUNC,
0644);
-
if (
-1 == fd)
-
perror (
"fail to fopen"),
exit (
1);
-
-
dprintf (fd,
"%c, %g, %d\n",
'A',
5.12,
68);
-
-
close (fd);
-
return
0;
-
}
-
输出结果:
-
# cat a.txt
-
A,
5.12,
68
-
//示例三 sprintf 函数
-
#include <stdio.h>
-
-
#define SIZE 30
-
int main (void)
-
{
-
char str[SIZE];
-
sprintf (str,
"%s %s %d\n",
"I",
"love",
512 );
-
puts (str);
-
return
0;
-
}
-
输出结果:
-
I love
512
(3)示例总结
例:rv = printf ("hello"); 结果为rv = 5;
再有,需要掌握的 printf( ) 格式转换说明符,这里就不讲了。
示例三:注意,sprintf 函数可能会造成由 buf 指向的缓冲区的溢出。调用者有责任确保该缓冲区足够大。
溢出错误如下:
-
# ./a.out
-
str =
12345678901234567890
-
len =
20
-
***
stack smashing detected ***: ./a.out terminated
-
======= Backtrace: =========
-
/lib/i386-linux-gnu/libc.so
.6(__fortify_fail+
0x45)[
0xb7677dd5]
-
/lib/i386-linux-gnu/libc.so
.6(+
0xffd8a)[
0xb7677d8a]
-
./a.out[
0x80484d5]
-
/lib/i386-linux-gnu/libc.so
.6(__libc_start_main+
0xf3)[
0xb75914d3]
-
./a.out[
0x80483a1]
-
======= Memory
map: ========
-
08048000
-08049000 r-xp
00000000
08:
01
2102158 /home/tarena/project/c_test/a.out
-
08049000
-0804a000 r--p
00000000
08:
01
2102158 /home/tarena/project/c_test/a.out
-
0804a000
-0804b000 rw-p
00001000
08:
01
2102158 /home/tarena/project/c_test/a.out
-
094f1000
-09512000 rw-p
00000000
00:
00
0 [heap]
-
b7547000-b7563000 r-xp
00000000
08:
01
1704884 /lib/i386-linux-gnu/libgcc_s.so
.1
-
b7563000-b7564000 r--p
0001b000
08:
01
1704884 /lib/i386-linux-gnu/libgcc_s.so
.1
-
b7564000-b7565000 rw-p
0001c000
08:
01
1704884 /lib/i386-linux-gnu/libgcc_s.so
.1
-
b7577000-b7578000 rw-p
00000000
00:
00
0
-
b7578000-b7717000 r-xp
00000000
08:
01
1704863 /lib/i386-linux-gnu/libc
-2.15.so
-
b7717000-b7719000 r--p
0019f000
08:
01
1704863 /lib/i386-linux-gnu/libc
-2.15.so
-
b7719000-b771a000 rw-p
001a1000
08:
01
1704863 /lib/i386-linux-gnu/libc
-2.15.so
-
b771a000-b771d000 rw-p
00000000
00:
00
0
-
b772d000-b7731000 rw-p
00000000
00:
00
0
-
b7731000-b7732000 r-xp
00000000
00:
00
0 [vdso]
-
b7732000-b7752000 r-xp
00000000
08:
01
1704843 /lib/i386-linux-gnu/ld
-2.15.so
-
b7752000-b7753000 r--p
0001f000
08:
01
1704843 /lib/i386-linux-gnu/ld
-2.15.so
-
b7753000-b7754000 rw-p
00020000
08:
01
1704843 /lib/i386-linux-gnu/ld
-2.15.so
-
bf960000-bf981000 rw-p
00000000
00:
00
0 [
stack]
-
已放弃 (核心已转储)
因为缓冲区溢出会造成程序不稳定甚至安全隐患,为了解决这种缓冲区溢出问题,引入了 snprintf 函数。例如:
-
#include <stdio.h>
-
#include <stdlib.h>
-
-
int main (void)
-
{
-
char str[
10] = {
0};
-
int len =
snprintf (str,
sizeof (str),
"12345678901234567890");
-
printf (
"str = %s\n", str);
-
printf (
"len = %d\n", len);
-
return
0;
-
}
-
输出结果:
-
str =
123456789
-
len =
20
在该函数中,缓冲区长度是一个显示参数,超过缓冲区尾端的所有字符都被丢弃。如果缓冲区足够大,snprintf 函数就会返回写入缓冲区的字符数。与 sprintf 相同该返回值不包括结尾的 null 字节。
以上这 2 个函数的返回值,成功返回将要存入数组的字符数,若编码出错,返回负值。
2、格式化输入
-
#include <stdio.h>
-
int scanf(const char *format, ...);
-
int fscanf(FILE *stream, const char *format, ...);
-
int sscanf(const char *str, const char *format, ...);
-
3个函数返回值:赋值的输入项数;若输入出错或在任一转换前已到达文件尾端,返回 EOF
(1)函数比较
scanf 族用于分析输入字符串,并将字符序列转换成指定类型的变量。在格式之后的各参数包含了变量的地址,用转换结果对这些变量赋值。(2)示例说明
-
//示例一 fscanf 函数
-
#include <stdio.h>
-
int main()
-
{
-
char ch=
0;
-
float fnum=
0.0;
-
int num=
0;
-
FILE *p_file=fopen(
"b.txt",
"r");
-
if(p_file)
-
{
-
//fscanf函数可以从文件中按照格式把数据拷贝到内存的存储区里
-
fscanf(p_file,
"%c %g %d",&ch,&fnum,&num);
-
//拷贝到存储区我们就可以打印出来
-
printf(
"%c %g %d\n",ch, fnum, num);
-
fclose(p_file);
-
p_file=
NULL;
-
}
-
return
0;
-
}
-
输出结果:
-
c
3.14
46
-
//示例二 sscanf 函数
-
#include <stdio.h>
-
-
#define SIZE 30
-
int main (void)
-
{
-
char str[SIZE];
-
sscanf (
"12345",
"%s", str);
-
puts (str);
-
return
0;
-
}
-
输出结果:
-
12345
(3)示例总结
示例一:fscanf 函数工作方式和 scanf 相似,区别在于前者需要第一个参数来指定合适的文件流。示例二:参看:C语言函数sscanf()的用法
-
status =
scanf (
"%ld", &num);
-
while (status ==
1)
-
{
-
status =
scanf (
"%ld", &num);
-
}
-
/*当输入整数则执行while循环,例如输入Q,则scanf返回值为0,循环终止*/
-
也可用下列形式代替:
-
while(
scanf (
"%ld", &num) ==
1) {}
十、函数 fileno
-
#include <stdio.h>
-
int fileno(FILE *stream);
-
返回值:与该流相关联的文件描述符
1、函数功能
2、示例说明
-
//示例一
-
#include <stdio.h>
-
int main(void)
-
{
-
FILE *fp;
-
int fd;
-
fp = fopen(
"/etc/passwd",
"r");
-
fd = fileno(fp);
-
printf(
"fd = %d\n", fd);
-
fclose(fp);
-
return
0;
-
}
-
输出结果:
-
fd =
3
-
//示例二
-
#include <stdio.h>
-
#include <unistd.h>
-
#include <stdlib.h>
-
int main (void)
-
{
-
int fd = fileno (
stdout);
-
int newfd = dup (fd);
-
if (
-1 == newfd)
-
perror (
"Fail to dup"),
exit (
-1);
-
-
printf (
"newfd = %d\n", newfd);
-
write (newfd,
"hello world\n",
12);
-
return
0;
-
}
-
输出结果:
-
newfd =
3
-
hello world