/*********************************************************************/
1.文件概念和类型
2.标准I/O
3.流
4.标准I/O相关函数
A.fopen-----fclose 流的打开、关闭
B.fgetc-----fputc 按字符输入、输出
C.fgets-----fputs 按行输入、输出
D.fread-----fwrite 按指定对象输入/输出
E.fflush----ftell/fseek/rewind 流的刷新和定位
F.sprintf----fprintf 格式化输入
G:time()用来获取系统时间(秒数)
localtime()将系统时间转换成本地时间
sleep()实现程序睡眠
5.文件I/O
a.文件I/O概念
b.文件I/O相关函数:
b.1: open函数 用来创建或打开一个文件:
b.2: close函数 用来关闭一个打开的文件:
b.3: read / write/lseek函数 从文件中读写数据并定位文件
b.4: opendir/readdir/closedir函数 打开目录文件/读取目录流中内容/关闭目录文件
b.5: 修改文件访问权限 – chmod/fchmod
获取文件属性 – stat/lstat/fstat
6.库 :
A:静态库
B: 动态库
/*********************************************************************/
1.文件概念和类型:
a. 一组相关数据的有序集合。
b. 文件类型:
Ø 常规文件 r
Ø 目录文件 d
Ø 字符设备文件 c
Ø 块设备文件 b
Ø 管道文件 p //进程间通信
Ø 套接字文件 s //进程间通信,网络编程
Ø 符号链接文件 l //快捷方式。
2.标准I/O
ANSI C 中定义的一组用于输入和输出的一系列函数。
标准 I/O 通过缓冲机制减少系统调用,实现更高的效率。
3.流:
a. //---FILE结构体----
标准IO用一个结构体类型来存放打开的文件的相关信息
标准I/O的所有操作都是围绕FILE来进行
b.//流的缓冲类型
全缓冲
当流的缓冲区无数据或无空间时才执行实际I/O操作
行缓冲
当在输入和输出中遇到换行符(‘\n’)时,进行I/O操作
当流和一个终端关联时,典型的行缓冲
无缓冲
数据直接写入文件,流不进行缓冲
c.//标准I/O预定义3个流,程序运行时自动打开
标准输入流0 STDIN_FILENO stdin 行缓存
标准输出流1 STDOUT_FILENO stdout 行缓存
标准错误流2 STDERR_FILENO stderr 无缓存
4.标准I/O相关函数
A.fopen-----fclose 流的打开、关闭
B.fgetc-----fputc 按字符输入、输出
C.fgets-----fputs 按行输入、输出
D.fread-----fwrite 按指定对象输入/输出
E.fflush----ftell/fseek/rewind 流的刷新和定位
F.sprintf----fprintf 格式化输入
G:time()用来获取系统时间(秒数)
localtime()将系统时间转换成本地时间
sleep()实现程序睡眠
A.fopen-----fclose 流的打开、关闭
/*********************************************************************/
//fopen:
1.头文件: #include<stdio.h>
2.函数原型: FILE *fopen (const char *path, const char *mode);
3. 参数: path: 包含打开流的文件路径和文件名
mode: 文件打开方式
------------------------------------------------------------------
mode:
“r” 或 “rb” 以只读方式打开文件,文件必须存在。
“r+” 或 ”r+b” 以读写方式打开文件,文件必须存在。
“w” 或 “wb” 以只写方式打开文件,若文件存在则文件长度清为0。若文件
不存在则创建。
“w+” 或 “w+b” 以读写方式打开文件,其他同”w”。
“a” 或 “ab” 以只写方式打开文件,若文件不存在则创建;向文件写入的数
据被追加到文件末尾。
“a+” 或 “a+b” 以读写方式打开文件。其他同”a”
// 当给定”b”参数时,表示以二进制方式打开文件
-----------------------------------------------------------------
4.返回值: 成功时返回流指针;出错时返回NULL
5.//示例:
#include <stdio.h>
int main(int argc, char *argv[]){
FILE *fp;
if ((fp = fopen(“test.txt”, “r+”)) == NULL) {
printf(“fopen error\n”);
return -1;
}
……
return 0;
}
//fclose:
int fclose(FILE *stream);
Øfclose()调用成功返回0,失败返回EOF,并设置errno
Ø流关闭时自动刷新缓冲中的数据并释放缓冲区
Ø当一个程序正常终止时,所有打开的流都会被关闭。
Ø流一旦关闭后就不能执行任何操作
//处理错误信息:
extern int errno;
void perror(const char *s);
char *strerror(int errno);
标准I/O – 处理错误信息
Øerrno 存放错误号
Øperror先输出字符串s,再输出错误号对应的错误信息
Østrerror根据错误号返回对应的错误信息
/**********************************************************************/
fgetc fputc 都是真正的函数,但是getc putc getchar putchar 都是#define指令定义的宏
B.fgetc-----fputc 按字符输入、输出
C.fgets-----fputs 按行输入、输出
/**********************************************************************/
1. 读写一个字符:fgetc()/fputc()一次读/写一个字符
/******fgetc******/
头文件: #include <stdio.h>
函数原型:int fgetc(FILE *stream); //指定流中读取
int getc(FILE *stream); //指定流中读取
int getchar(void); //从stdin(标准输入)读取
参数: 输入的文件流
返回值: 成功读取的字符
若到文件末尾或者出错返回EOF
getchar( ) == fgetc(stdin)
/******fputc******/
头文件: #include <stdio.h>
函数原型: int fputc(int c, FILE *stream);
int putc(int c, FILE *stream);
int putchar(int c);
参数同上:
返回值: 成功时返回写入的字符;出错时返回EOF
putchar(c)等同于fputc(c, stdout)
函数示例:
//标准输入输出流:
int ch;
ch = fgetc(stdin);
printf(“%c\n”, ch);
fputc(‘a’, stdout);
putchar(‘\n’);
/***************************/
//指定流输入输出:
FILE *fp;
int ch, count = 0;
if ((fp = fopen(argv[1], “r”)) == NULL) {
perror(“fopen”);
return -1;
}
while ((ch = fgetc(fp)) != EOF) {
count++;
}
printf(“total %d bytes\n”, count);
FILE *fp;
int ch;
if ((fp = fopen(argv[1], “w”)) == NULL) {
perror(“fopen”); return -1;
}
for(ch = ‘a’; ch <=‘z’; ch++) {
fputc(ch, fp);
}
--------------------------------------------------------------------------
2.读写一行:fgets()和fputs()一次读/写一行
/*****fgets********/
头文件: #include <stdio.h>
函数原型 char *gets(char *s);
char *fgets(char *s, int size, FILE *stream);
参数: s: 存放输入字符串的缓冲区首地址
size: 输入字符大小
stream:指定流
返回值: 成功时返回s,到文件末尾或出错时返回NULL
// 注意事项:
遇到’\n’或已输入size-1个字符时返回,总是包含’\0’
gets不推荐使用,容易造成缓冲区溢出,不在结尾添加换行符
//函数示例:
#define N 6
char buf[N];
fgets(buf, N, stdin);
printf(“%s”, buf);
/*******fputs*********/
头文件: #include <stdio.h>
函数原型: int puts(const char *s);
int fputs(const char *s, FILE *stream);
返回值: 成功时返回输出的字符个数;出错时返回EOF
//注意事项:
puts将缓冲区s中的字符串输出到stdout,并追加’\n’
fputs将缓冲区s中的字符串输出到stream
//函数示例:
FILE *fp;
char buf[] = “hello world”;
if ((fp = fopen(argv[1], “a”)) == NULL) {
perror(“fopen”);
return -1;
}
fputs(buf, fp);
/**********************************************************************/
二进制IO,把数据效率最高的方法是二进制形式写入。
D.fread-----fwrite----按指定对象输入/输出
/**********************************************************************/
读写若干个对象:fread()/fwrite() 每次读/写若干个对象,而每个对象具有相同的长度
//头文件:
#include<stdio.h>
//函数原型:
size_t fread(void *ptr, size_t size, size_t n, FILE *fp);
size_t fwrite(const void *ptr, size_t size, size_t n, FILE *fp);
//参数:
ptr: 存放读写记录的缓冲区首地址
size: 读写记录的大小
n: 读写记录的个数
fp: 指定流文件
//返回值:
成功返回读写的对象个数;出错时返回EOF
既可以读写文本文件,也可以读写数据文件
//函数示例:
int s[10];
// if (fread(s, sizeof(int), 10, fp) < 0) {
perror(“fread”);
return -1;
}
struct student {
int no;
char name[8];
float score;
} s[] = {{ 1, “zhang”, 97}, {2, “wang”, 95}};
// fwrite(s, sizeof(struct student), 2, fp);
// 复制文件示例:
#include <stdio.h>
#define N 64
int main(int argc, char *argv[])
{
FILE *fps, *fpd;
int buf[N];
int n;
if (argc < 3) {
printf("Usage : %s <src_file> <dst_file>\n", argv[0]);
return -1;
}
if ((fps = fopen(argv[1], "r")) == NULL) {
perror("fopen src file");
return -1;
}
if ((fpd = fopen(argv[2], "w")) == NULL) {
perror("fopen dst file");
return -1;
}
while ((n = fread(buf, 1, N, fps)) > 0) {
fwrite(buf, 1, n, fpd);
}
fclose(fps);
fclose(fpd);
return 0;
}
/**********************************************************************
E.fflush----ftell/fseek/rewind 流的刷新和定位
/**********************************************************************/
//头文件:
#include <stdio.h>
//刷新函数原型:
int fflush(FILE *fp);
//返回值:
成功时返回0;出错时返回EOF
将流缓冲区中的数据写入实际的文件
Linux下只能刷新输出缓冲区
-------------------------------------------------------------------------
//定位函数原型:
long ftell(FILE *stream);
long fseek(FILE *stream, long offset, int whence);
void rewind(FILE *stream);
//返回值:
ftell() 成功时返回流的当前读写位置,出错时返回EOF
fseek() 定位一个流,成功时返回0,出错时返回EOF
rewind()将流定位到文件开始位置
//参数:
whence参数:SEEK_SET/SEEK_CUR/SEEK_END
offset参数:偏移量,可正可负
读写流时,当前读写位置自动后移
/**********************************************************************/
F.sprintf----fprintf 格式化输入
/**********************************************************************/
//头文件:
#include <stdio.h>
//函数原型:
int printf(const char *fmt, …); //标准输出格式化输出
int fprintf(FILE *stream, const char *fmt, …); //指定流格式化输出
int sprintf(char *s, const char *fmt, …); //指定缓冲区格式化输出
//返回值:
成功时返回输出的字符个数;出错时返回EOF
//示例:
以指定格式 “年-月-日” 分别写入文件和缓冲区
int year, month, date;
FILE *fp;
char buf[64];
year = 2014; month = 10; date = 26;
fp = fopen(“test.txt”, “a+”);
fprintf(fp, “%d-%d-%d\n”, year, month, date);
sprintf(buf, “%d-%d-%d\n”, year, month, date);
G:time()用来获取系统时间(秒数)
localtime()将系统时间转换成本地时间
sleep()实现程序睡眠
/**********************************************************************/
//时间函数示例:
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
int main()
{
FILE *fp;
int line = 0;
char buf[64];
time_t t; //时间变量类型定义
struct tm *tp;
if ((fp = fopen("test.txt", "a+")) == NULL) {
perror("fopen");
return -1;
}
while (fgets(buf, 64, fp) != NULL)
{
if (buf[strlen(buf)-1] == '\n') line++;
}
while ( 1 )
{
time(&t);
tp = localtime(&t); //获取当地时间:
fprintf(fp, "%02d, %d-%02d-%02d %02d:%02d:%02d\n", ++line, tp->tm_year+1900, tp->tm_mon+1,
tp->tm_mday, tp->tm_hour, tp->tm_min, tp->tm_sec);
fflush(fp);
sleep(1);
}
return 0;
}
/**********************************************************************/
5.文件I/O
// a.文件I/O概念
.posix(可移植操作系统接口)定义的一组函数
.不提供缓冲机制,每次读写操作都引起系统调用
.每个打开的文件都对应一个文件描述符
.文件描述符是一个非负整数。Linux为程序中每个打开的文件分配一个文件描述符
.文件描述符从0开始分配,依次递增。
.文件IO操作通过文件描述符来完成
// b.文件I/O相关函数:
b.1: open函数 用来创建或打开一个文件:
b.2: close函数 用来关闭一个打开的文件:
b.3: read/write/lseek函数 从文件中读写数据并定位文件
b.4: opendir/readdir/closedir函数 打开目录文件/读取目录流中内容/关闭目录文件
b.5: 修改文件访问权限 – chmod/fchmod
获取文件属性 – stat/lstat/fstat
b.1:open函数用来创建或打开一个文件:
// 头文件:
#include <fcntl.h>
//函数原型:
int open(const char *pathname, int flags, mode_t mode);
//参数:
//pathname: 被打开的文件名(可包括路径名)。、
//flags:
O_RDONLY:只读方式打开文件。
O_WRONLY:可写方式打开文件。 这三个参数互斥
O_RDWR: 读写方式打开文件。
O_CREAT: 如果该文件不存在,就创建一个新的文件,并用第三的参数为其设置权限
O_EXCL: 如果使用O_CREAT时文件存在,则可返回错误消息。这一参数可测试文件是否存在。
O_NOCTTY:使用本参数时,如文件为终端,那么终端不可以作为调用open()系统调用的那个进程的控制终端。
O_TRUNC: 如文件已经存在,那么打开文件时先删除文件中原有数据
O_APPEND:以添加方式打开文件,所以对文件的写操作都在文件的末尾进行
//mode 被打开文件的存取权限,为8进制表示法。
//返回值:
成功时返回文件描述符;出错时返回EOF
//注意事项:
打开文件时使用两个参数
创建文件时第三个参数指定新文件的权限
只能打开设备文件
//函数示例:
1.以只写方式打开文件1.txt。如果文件不存在则创建,如果文件存在则清空:
int fd;
if ((fd = open(“1.txt”, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) {
perror(“open”);
return -1;
}
2.以读写方式打开文件1.txt。如果文件不存在则创建,如果文件存在则报错:
int fd;
if ((fd = open(“1.txt”, O_RDWR|O_CREAT|O_EXCL, 0666)) < 0) {
if (errno == EEXIST) {
perror(“exist error”);
}
else {
perror(“other error”);
}
}
b.2 :close函数用来关闭一个打开的文件:
// 头文件:
#include <unistd.h>
// 函数原型:
int close(int fd);
// 返回值:
成功时返回0;出错时返回EOF
//注意事项:
程序结束时自动关闭所有打开的文件
文件关闭后,文件描述符不再代表文件
b.3: read/write/lseek函数 从文件中读写数据并定位文件
//头文件:
#include <unistd.h>
//函数原型:
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, void *buf, size_t count);
off_t lseek(int fd, off_t offset, intt whence);
//参数:
buf是读写数据的缓冲区
count不应超过buf大小
offset/whence同fseek完全一样
whence参数:SEEK_SET/SEEK_CUR/SEEK_END
offset参数:偏移量,可正可负
//返回值:
read/write:
成功时返回实际读取的字节数;出错时返回EOF
读到文件末尾时返回0
lseek:
成功时返回当前的文件读写位置;出错时返回EOF
//函数示例:
//复制文件:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#define N 64
int main(int argc, char *argv[])
{
int fds, fdt, n;
char buf[N];
if (argc < 3)
{
printf("Usage : %s <src_file> <dst_file>\n", argv[0]);
return -1;
}
if ((fds = open(argv[1], O_RDONLY)) == 1)
{
fprintf(stderr, "open %s : %s\n", argv[1], sterror(errno));
return -1;
}
if ((fdt = open(argv[2], O_WRONLY|O_CREAT|O_TRUNC, 0666)) == -1)
{
fprintf(stderr, "open %s : %s\n", argv[2], strerror(errno));
return -1;
}
while ((n = read(fds, buf, N)) > 0)
{
write(fdt, buf, n);
}
close(fds);
close(fdt);
return 0;
}
---------------------------------------------------------------------------------
b.4: opendir/readdir/closedir函数
打开目录文件/读取目录流中内容/关闭目录文件
--------------------------------------------------------------------------------
//头文件:
#include <dirent.h>
//函数原型:
DIR *opendir(const char *name);
struct dirent *readdir(DIR *dirp);
int closedir(DIR *dirp);
//参数:
name:目录名字
DIR: 用来描述一个打开的目录文件的结构体类型
//返回值:
opendir:
成功时返回目录流指针;出错时返回NULL
readdir:
struct dirent是用来描述目录流中一个目录项的结构体类型
包含成员char d_name[256] 参考帮助文档
成功时返回目录流dirp中下一个目录项;
出错或到末尾时时返回NULL
closedir:
成功时返回0;出错时返回EOF
//函数示例:
int main(int argc, char *argv[]){
DIR *dirp;
struct dirent *dp;
if (argc < 2) {
printf(“Usage : %s <directory>\n”, argv[0]); return -1;
}
// if ((dirp = opendir(argv[1])) == NULL) {
perror(“opendir”); return -1;
}
// while ((dp = readdir(dirp)) != NULL) {
printf(“%s\n”, dp->d_name);
}
// closedir(dirp);
}
b.5: 修改文件访问权限 – chmod/fchmod 获取文件属性 – stat/lstat/fstat
--------------------------------------------------------------------------------
//头文件:
#include <sys/stat.h>
//函数原型:
int chmod(const char *path, mode_t mode);
int fchmod(int fd, mode_t mode);
int stat(const char *path, struct stat *buf);
int lstat(const char *path, struct stat *buf);
int fstat(int fd, struct stat *buf);
//参数:
chmod ("test.xtx",0666);
path: 文件名:
// struct stat:是存放文件属性的结构体类型:
Ø mode_t st_mode; 类型和访问权限
Ø uid_t st_uid; 所有者id
Ø uid_t st_gid; 用户组id
Ø off_t st_size; 文件大小
Ø time_t st_mtime; 最后修改时间
//文件类型 – st_mode
通过系统提供的宏来判断文件类型:
st_mode & 0170000
Ø S_ISREG(st_mode) 0100000
Ø S_ISDIR(st_mode) 0040000
Ø S_ISCHR(st_mode) 0020000
Ø S_ISBLK(st_mode) 0060000
Ø S_ISFIFO(st_mode) 0010000
Ø S_ISLNK(st_mode) 0120000
Ø S_ISSOCK(st_mode) 0140000
//文件访问权限 – st_mode
通过系统提供的宏来获取文件访问权限:
Ø S_IRUSR 00400 bit:8
Ø S_IWUSR 00200 7
Ø S_IXUSR 00100 6
Ø S_IRGRP 00040 5
Ø S_IWGRP 00020 4
Ø S_IXGRP 00010 3
Ø S_IROTH 00004 2
Ø S_IWOTH 00002 1
Ø S_IXOTH 00001 0
//函数示例:
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
int main(int argc, char *argv[])
{
struct stat buf;
int n;
struct tm *tp;
if (argc < 2)
{
printf("Usage : %s <file>\n", argv[0]);
return -1;
}
if (lstat(argv[1], &buf) < 0)
{
perror("lstat");
return -1;
}
switch (buf.st_mode & S_IFMT)
{
case S_IFREG:
printf("-");
break;
case S_IFDIR:
printf("d");
break;
}
for (n=8; n>=0; n--)
{
if (buf.st_mode & (1<<n))
{
switch (n % 3)
{
case 2:
printf("r");
break;
case 1:
printf("w");
break;
case 0:
printf("x");
break;
}
}
else
{
printf("-");
}
}
printf(" %lu", buf.st_size);
tp = localtime(buf.st_mtime);
printf(" %d-%02d-%02d", tp->tm_year+1900, tp->tm_mon+1, tp->tm_mday);
printf(" %s\n", argv[1]);
return 0;
}
/**********************************************************************/
6.库 :
Ø 库是一个二进制文件,包含的代码可被程序调用
Ø 标准C库、数学库、线程库……
Ø 库有源码,可下载后编译;也可以直接安装二进制包
Ø /lib /usr/lib
库是事先编译好的,可以复用的代码。
Ø 在OS上运行的程序基本上都要使用库。使用库可以提高开发效率。
Ø Windows和Linux下库文件的格式不兼容
Ø Linux下包含静态库和共享库
A:静态库
编译(链接)时把静态库中相关代码复制到可执行文件中
• 程序中已包含代码,运行时不再需要静态库
• 程序运行时无需加载库,运行速度更快
• 占用更多磁盘和内存空间
• 静态库升级后,程序需要重新编译链接
// a.创建静态库:
1.确定库中函数的功能、接口
2.编写库源码hello.c
#include <stdio.h>
void hello(void) {
printf(“hello world\n”);
return;
}
3.编译生成目标文件
$ gcc -c hello.c -Wall
4.创建静态库 hello
$ ar crs libhello.a hello.o
5.查看库中符号信息
$nm libhello.a
hello.o:
0000000 T hello
U puts
// b.链接静态库
1.编写应用程序test.c
#include <stdio.h>
void hello(void);
int main() {
hello();
return 0;
}
2.编译test.c 并链接静态库libhello.a
$ gcc -o test test.c -L. -lhello
$ ./test
hello world
-------------------------------------------------------------
B: 动态库 、共享库
1.共享库特点:
编译(链接)时仅记录用到哪个共享库中的哪个符号,不复制共享库中相关代码
• 程序不包含库中代码,尺寸小
• 多个程序可共享同一个库
• 程序运行时需要加载库
• 库升级方便,无需重新编译程序
• 使用更加广泛
2.共享库创建
a.确定库中函数的功能、接口
b.编写库源码hello.c
#include <stdio.h>
void hello(void) {
printf(“hello world\n”);
return;
}
c.编译生成目标文件
$ gcc -c -fPIC hello.c bye.c -Wall
d.创建共享库 common
$ gcc -shared -o libcommon.so.1 hello.o bye.o
e.为共享库文件创建链接文件
$ ln -s libcommon.so.1 libcommon.so
f.符号链接文件命名规则
lib<库名>.so
3.链接共享库
a.编写应用程序test.c
#include <stdio.h>
#include “common.h”
int main() {
hello();
bye();
return 0;
}
b.编译test.c 并链接共享库libcommon.so
$ gcc -o test test.c -L. -lcommon
4.加载共享库
a.执行程序
$ ./test
./test: error while loading shared libraries: libcommon.so
cannot open shared object file : No such file or directory
b.添加共享库的加载路径
$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
$ ./test
hello world
bye!
5.为了让系统能找到要加载的共享库,有三种方法 :
Ø 把库拷贝到/usr/lib和/lib目录下
Ø 在LD_LIBRARY_PATH环境变量中添加库所在路径
Ø 添加/etc/ld.so.conf.d/*.conf文件,执行ldconfig刷新