• UNIX环境编程学习笔记(12)——文件I/O之目录操作


    lienhua34
    2014-09-18

    1 引言

    在 UNIX 系统中,目录是一种特殊的文件类型。我们可以使用 open 函数来打开目录,获取文件描述符,然后调用 stat 函数来获取目录的属性信息,但是我们却不能够使用 read 函数来读取目录内容。例如,下面例子所示,

    #include <stdlib.h>
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <sys/stat.h>
    #define BUF_LEN 2014
    int
    main(void)
    {
        int fd;
        char buffer[BUF_LEN];
        struct stat statbuf;
        if ((fd = open("mydir", O_RDONLY)) < 0) {
            printf("open mydir error: %s
    ", strerror(errno));
            exit(-1);
        }
        if (fstat(fd, &statbuf) < 0) {
            printf("fstat error: %s
    ", strerror(errno));
            exit(-1);
        }
        if (S_ISDIR(statbuf.st_mode)) {
            printf("mydir is directory
    ");
        }
        if (read(fd, buffer, BUF_LEN) < 0) {
            printf("read mydir error: %s
    ", strerror(errno));
            exit(-1);
        }
        printf("mydir: %s
    ", buffer);
        exit(0);
    }

    编译该程序,生成文件 readdir,然后运行 readdir,

    lienhua34:demo$ ls -l
    drwxrwxr-x 2 lienhua34 lienhua34 4096
    9月 18 20:22 mydir
    lienhua34:demo$ gcc -o readdir readdir.c
    lienhua34:demo$ ./readdir
    mydir is directory
    read mydir error: Is a directory

    通过上面运行的结果,我们不难发现 read 函数读取目录时报错了。UNIX 提供了一套专门针对目录的操作函数。

    2 创建目录

    mkdir 函数提供了创建目录的功能。

    #include <sys/stat.h>

    int mkdir(const char *pathname, mode_t mode);

    返回值:若成功则返回0,若出错则返回-1.

    mkdir 函数一个新的空目录。其中,. 和.. 目录项是自动创建的。这里指定的 mode 与进程的文件模式创建屏蔽字共同决定了新目录的文件访问权限(参考“文件访问权限的屏蔽和更改”。)

    注:在“文件访问权限与进程访问控制”中,我们了解到目录的执行权限用于文件的搜索,所以在调用 mkdir 函数指定 mode 参数时至少设置1 个执行权限位。

    3 删除目录

    rmdir 函数可以删除一个空目录。空目录是只包含. 和.. 这两项的目录。

    #include <unistd.h>

    int rmdir(const char *pathname);

    返回值:若成功则返回0,若出错则返回-1.

    调用 rmdir 函数会使目录的链接计数减 1. 如果目录的链接计数减为0,且没有其他进程打开此目录,则释放目录占有的空间。如果在链接计数达到 0 时,存在进程打开了此目录,则在函数返回前删除以后一个链接及.和.. 项。另外,在此目录中不能再创建新文件。

    例子 1:

    下面程序是删除一个非空目录 mydir 失败的例子。

    #include <stdlib.h>
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    #include <unistd.h>
    int
    main(void)
    {
        if (rmdir("mydir") < 0) {
            printf("rmdir mydir error: %s
    ", strerror(errno));
        } else {
            printf("succ rmdir mydir");
        }
        exit(0);
    }

    编译该程序,生成 rmdirdemo,然后运行该文件,

    lienhua34:demo$ ls -l mydir
    总用量 8
    -rw-rw-r-- 1 lienhua34 lienhua34 9 9月 18 20:21 file1
    -rw-rw-r-- 1 lienhua34 lienhua34 9 9月 18 20:22 file2
    lienhua34:demo$ gcc -o rmdirdemo rmdirdemo.c
    lienhua34:demo$ ./rmdirdemo
    rmdir mydir error: Directory not empty

    例子 2:

    下面程序删除空目录 emptydir,在进程尚未退出之前,在该目录下新建一个文件,

    #include <stdlib.h>
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <sys/stat.h>
    int
    main(void)
    {
        int fd;
        if ((fd = open("emptydir", O_RDONLY)) < 0) {
            printf("open emptydir error: %s
    ", strerror(errno));
            exit(-1);
        }
        if (rmdir("emptydir") < 0) {
            printf("rmdir emptydir error: %s
    ", strerror(errno));
            exit(-1);
        }
        printf("rmdir emptydir succ
    ");
        if (creat("emptydir/foo", S_IRUSR | S_IWUSR) < 0) {
            printf("creat error: %s
    ", strerror(errno));
            exit(-1);
        }
        printf("creat succ
    ");
        exit(0);
    }

    编译该程序,生成文件 rmdirdemo,然后运行该文件,

    lienhua34:demo$ ls -l emptydir
    总用量 0
    lienhua34:demo$ gcc -o rmdirdemo rmdirdemo.c
    lienhua34:demo$ ./rmdirdemo
    rmdir emptydir succ
    creat error: No such file or directory

    4 读取目录内容

    对某个目录具有访问权限的任一用户都可读该目录,但是,之后内核才能够写目录。另外,目录的实际格式依赖于 UNIX 系统,特别是其文件系统的具体设计和实现。为了使应用程序与这些具体的实现细节隔离开,UNIX 系统提供了一套与读目录有关的例程。

    #include <dirent.h>
    DIR *opendir(const char *pathname);
    返回值:若成功则返回真挚,若出错则返回NULL。

    struct dirent *readdir(DIR *dp);
    返回值:若成功则返回指针,若在目录结尾或出错则返回NULL。

    void rewinddir(DIR *dp);

    int close(DIR *dp);
    返回值:若成功则返回0,若出错则返回-1.

    long telldir(DIR *dp);
    返回值:与dp关联的目录中的当前位置。

    void seekdir(DIR *dp, long loc);

    头文件 <dirent.h> 中定义的 dirent 结构与实现有关。不过,其至少包含下面两个成员:

    struct dirent {
        ino_t d_ino;     /* i-node number */
        char d_name[NAME_MAX + 1];    /* filename */
    }

    opendir 函数打开一个目录,并执行初始化操作,使第一个 readir 读目录中的第一个目录项。

    例子:

    下面程序编译目录 mydir 下的所有目录项,并打印各个目录项的名字。

    #include <stdlib.h>
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    #include <dirent.h>
    int
    main(void)
    {
        DIR *dp;
        struct dirent *dirp;
        if ((dp = opendir("mydir")) == NULL) {
            printf("opendir mydir error: %s
    ", strerror(errno));
            exit(-1);
        }
        while ((dirp = readdir(dp)) != NULL) {
            printf("%s
    ", dirp->d_name);
        }
        if (closedir(dp) < 0) {
            printf("closedir error: %s
    ", strerror(errno));
            exit(-1);
        }
        exit(0);
    }

    编译该程序,生成文件 readdir,然后运行该文件,

    lienhua34:demo$ gcc -o readdir readdir.c
    lienhua34:demo$ ./readdir
    ..
    file2
    .
    file1

    从上面程序的运行结果知道,读取目录中各目录项的顺序没有按照字母顺序进行排列。

    5 切换进程的工作目录

    每个进程都有一个当前工作目录,此目录是搜索所有相对路径名的起点。进程可以调用 chdir 或 fchdir 函数更改当前工作目录。

    #include <unistd.h>

    int chdir(const char *pathname);

    int fchdir(int filedes);

    两个函数的返回值:若成功则返回0,否则返回-1.

    当前工作目录是进程的一个属性,更改进程的当前工作目录只影响调用 chdir 函数的进程本身,对其他进程则没有影响。

    另外,UNIX 还提供了 getcwd 函数来获取进程的当前工作目录。

    #include <unistd.h>

    char *getcwd(char *buf, size_t size);

    返回值:若成功则返回buf,若出错则返回NULL。

    getcwd 函数的第一个参数是缓冲地址 buf,另一个是缓冲的长度 size(单位:字节)。该缓冲必须有足够的长度以容纳绝对路径名再加上一个 null终止字符。

    例子:

    下面程序先打印当前工作目录,然后调用 chdir 来切换工作目录,最后调用 getcwd 获取并打印新的工作目录。

    #include <stdlib.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <errno.h>
    #include <string.h>
    #define BUF_LEN 2014
    int
    main(void)
    {
        char buf[BUF_LEN];
        if (getcwd(buf, BUF_LEN) < 0) {
            printf("getcwd error: %s
    ", strerror(errno));
            exit(-1);
        }
        printf("cwd before chdir: %s
    ", buf);
        if (chdir("/home/lienhua34/program") < 0) {
            printf("chdir error: %s
    ", strerror(errno));
            exit(-1);
        }
        if (getcwd(buf, BUF_LEN) < 0) {
            printf("getcwd error: %s
    ", strerror(errno));
            exit(-1);
        }
        printf("cwd before chdir: %s
    ", buf);
        exit(0);
    }

    编译该程序,生成文件 cwddemo,然后运行该文件,

    lienhua34:demo$ gcc -o cwddemo cwddemo.c
    lienhua34:demo$ ./cwddemo
    cwd before chdir: /home/lienhua34/program/c/apue/ch04/demo
    cwd before chdir: /home/lienhua34/program

    (done)

  • 相关阅读:
    在PHP中截取字符串
    SQL查看一张表中是否存在记录
    最多两位小数,如123.45的转换结果为:壹百贰拾叁元肆角伍分
    SQL将金额转换为汉子
    SQL 汉字转换成拼音首字母 首字母查
    SQL生成随机数
    Linux gsoap2.6 webservices
    cxGrid的一些使用方法
    不修改forms.pas单元就可以去掉MDI窗口的滚动条
    今天,我种下了一朵小蓝花
  • 原文地址:https://www.cnblogs.com/lienhua34/p/3982271.html
Copyright © 2020-2023  润新知