linux程序设计——文件操作(第三章)
上篇是文件操作(第三章上),文章中的代码在文件操作(代码下载)。
3.6 格式化输出和输入
3.6.1 printf、fprintf和sprintf函数
printf函数能够对各种不同类型的参数进行格式化编排和输出。每个参数在输出流中的表示形式由格式化参数format控制,它是一个包含需要输出的普通字符和转换控制符代码的字符串。#include <stdio.h>
int printf(const char* format, ...);
int sprintf(char* s, const char* format, ...);
int fprintf(FILE* stream, const char* format, ...);
printf函数把自己的输出送到标准输出,fprintf函数把自己的输出送到一个指定的文件流。sprintf函数把自己的输出和一个结尾孔字符写到作为参数传递过来的字符串s里。
printf("some numbers: %d,%d and %d\n", 1, 2, 3);
%d,%i 输出十进制整数
%o,%x 输出八进制或者十六进制整数
%c 输出一个字符
%s 输出一个字符串
%d 输出一个浮点数
%e 输出科学计数法格式的浮点数
%g 输出(双精度)浮点数
printf函数返回一个整数一表明它输出的字符个数。
sprintf的返回值里并没有算上结尾的那个空字符。
3.6.2 scanf、fscanf和sscanf函数
scanf函数从一个文件流里读取数据,并把数据值放到以指针参数形式传递过来的地址处的变量中。#include <stdio.h>
int scanf(const char *format, ...);
int fscanf(FILE* stream, const char* format,...);
int sscanf(const char* s, const char* format, ..);
scanf系列函数中,那些普通字符是用于指定在输入数据里必须出现的字符。例如:
int num;
scanf("hello %d", &num);
这个scanf调用只有在标准输入中接下来的五个字符匹配"hello"的情况下才会成功。
使用%c控制符从输入中读取一个字符,它不会跳过起始的空白字符。
使用%s控制符来扫描字符串,则会跳过起始的空白字符,并且会在字符串里出现的第一个空白字符处停下来,所以最好使用它来读取单词而不是一般意义上的字符串。
使用%[]控制符读取由一个字符集和中的字符构成的字符串。格式字符串%[A-Z]将读取一个由大写字母构成的字符串。如果字符集中第一个字符是^,将表示将读取一个不属于由该字符集和中的字符构成的字符串。因此,读取一个其中带空格的字符串,并且在遇到第一个逗号时停止,可以使用%[^,]。
给定下面输入行:
hello, 1234, 5.678, X, string to the end of the line
char s[256];
int n;
float f;
char c;
scanf("hello,%d,%g,%c,%[^\n], &n,&f,&c,s);
scanf函数的返回值是它成功读取的数据项个数,如果在读取第一个数据项时失败了,它的返回值将是0。如果在匹配第一个数据项之前就已经到达了输入的尾,就返回EOF。
3.6.3 其他流函数
stdio函数库里还有一些其他的函数使用流参数或标准流stdin、stdout和stderr,如下所示:fgetpos:获得文件流的当前(读写)位置
fsetpos:设置文件流的当前(读写)位置
编写copy_stdio.c程序
运行这个程序,发现虽然不如底层数据库复制版本块(copy_block.c),但是比那个系统调用每次复制一个字符的版本快很多(copy_system.c)。这是因为stdio库在FILE结构里使用了一个内部缓冲区,只有在缓冲区时才进行底层系统调用。
3.6.4 文件流错误
为了表明错误,许多stdio库函数会返回一个超出范围的值,比如空指针或者EOF常数。此时,错误由外部变量errno指出:#include <errno.h>
extern int errno;
因为许多函数都可能改变errno的值,它的值只有在函数调用失败时才有意义。必须在函数调用失败之后立刻对其进行检查。
也可以通过检查文件流的状态来确定是否发生了错误,或者是否到达了文件尾。
#include <stdio.h>
int ferror(FILE* stream);
int feof(FILE* stream);
void clearerr(FILE* stream);
3.7 文件和目录的维护
标准库和系统调用为文件和目录的创建与维护提供了全面的支持。3.7.1 chmod系统调用
可以通过chmod系统调用来改变文件或目录的访问权限。这构成了shell程序chmod的基础。该函数原型如下:
#include <sys/stat.h>
int chmod(const char* path, mode_t mode);
path参数指定的文件被修改为具有mode参数给出的访问权限。参数mode的定义与open系统调用中的一样,也是对所要求的访问权限进行按位OR操作。
3.7.2 chown系统调用
超级用户可以使用chown系统调用来改变一个文件的属主。#include <sys/types.h>
#include <unistd.h>
int chown(const char *path, uid_t owner, gid_t group);
这个调用使用的是用户ID和组ID的数字值(通过getuid和getgid调用获得)和一个用于限定谁可以修改属主的系统值。
3.7.3 unlink、link和symlink系统调用
可以使用unlink系统调用来删除一个文件。unlink系统调用删除一个文件的目录项并减少它的链接数。它在成功时返回0,失败时返回-1。函数原型如下:
#include <unistd.h>
int unlink(const char* path);
int link(const char *path1, const char *path2);
int symlink(const char *path1, const char *path2);
如果一个文件的链接数减少到零,并且没有进程打开它,这个文件就会被删除。事实上,目录项总是被立刻删除,但文件所占用的空间要等到最后一个进程(如果有的话)关闭它之后才会被系统回收。rm程序使用的就是这个调用。
link系统调用将创建一个指向已有文件path1的新链接。新目录项由path2给出。
3.7.4 mkdir和rmdir系统调用
可以使用mkdir和rmdir系统调用来创建和删除目录#include <sys/types.h>
#include <sys/stat.h>
int mkdir(const char* path, mode_t mode);
mkdir系统调用用于创建目录,它相当于mkdir程序,mkdir调用将参数作为新建目录的名字。
#include <unistd.h>
int rmdir(const char* path);
rmdir系统调用用于删除目录,但是只有在目录为空时才可以。
3.7.5 chdir系统调用和getcwd函数
程序可以像用户在文件系统里那样浏览目录,就像shell里使用cd命令切换目录一样,程序使用的是chdir系统调用#include <unistd.h>
int chdir(const char *path);
程序可以通过调用getcwd函数来确定自己的当前工作目录。
#include <unistd.h>
char *getcwd(char *buf, size_t size);
getcwd函数把当前目录的名字写到给定的缓冲区buf里。
3.8 扫描目录
linux系统上一个常见的问题就是扫描目录,也就是确定一个特定目录下存放的文件。在shell程序设计中,这很容易做到——只需要让shell做一次表达式的通配符扩展。与目录操作有关的函数在dirent.h头文件中生命。它们使用一个名为DIR的结构作为目录操作的基础。被称为目录流的指向这个结构的指针被用来完成各种目录操作。
3.8.1 opendir函数
opendir函数的作用是打开一个目录并建立一个目录流。#include <sys/types.h>
#include <dirent.h>
DIR* opendir(const char* name);
3.8.2 readdir函数
readdir函数返回一个指针,该指针指向的结构里保存着目录流dirp中下一个目录项的有关资料。#include <sys/types.h>
#include <dirent.h>
struct dirent* readdir(DIR* dirp);
3.8.3 telldir函数
telldir函数的返回值记录着一个目录流里的当前位置。#include <sys/types.h>
#include <dirent.h>
long int telldir(DIR* dirp);
3.8.4 seekdir函数
seekdir函数的作用是设置目录流dirp的目录项指针,loc的值用来设置指针位置,它应该通过一个telldir调用获得。#include <sys/types.h>
#include <dirent.h>
void seekdir(DIR* dirp, long int loc);
3.8.5 closedir函数
closedir函数关闭一个目录流并释放与之相关联的资源。#include <sys/types.h>
#include <dirent.h>
int closedir(DIR* dirp);
编写printdir.c程序,实现一个简单的目录列表功能。
3.10 /proc文件系统
linux将一切事物看作为文件,硬件设备在文件系统中也有相应的条目。我们使用底层系统调用这样一种特殊方式通过/dev目录中的文件访问硬件。linux提供了一个特殊的文件系统procfs,它通常以/proc目录的形式呈现。该目录中包含了许多特殊文件用来对驱动程序和内核信息进行更高层的访问。
例如/proc/cpuinfo给出的是cpu的详细信息。
/proc/meminfo和/proc/version分别给出的是内存使用情况和内核版本信息。
/proc/net/socket文件给出网络套接字的使用统计
/proc目录中以数字命名的子目录用于提供正在运行的程序的信息。
每个进程都有一个唯一的标识符:一个在1-32000的数字。ps命令给出当前正在运行进程的列表。
3.11 高级主题
3.11.1 fcntl系统调用
fcntl系统调用对底层文件描述符提供了更多的操作方法。#include <fcntl.h>
int fcntl(int fildes, int cmd);
int fcntl(int fildes, int cmd, long arg);
3.11.2 mmap函数
mmap(内存映射)函数的作用是建立一段可以被两个或者更多个程序读写的内存。一个程序对它所做出的修改可以被其他程序看见。mapp函数创建一个指向一段内存区域的指针,该内存区域与可以通过一个打开的文件描述符访问的文件的内容相关联。
#include <sys/mman.h>
void *mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off);
3.12 小结
这章主要学习linux提供的直接访问文件和设备的方法,介绍建立在这些底层函数之上的库函数是如何为程序设计问题提供灵活的解决方案的。郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。