I/O函数学习

来源:互联网 发布:java web编程宝典 编辑:程序博客网 时间:2024/05/08 08:38

一、标准I/O函数:

1.fopen/fclose

        #include <stdio.h>

        FILE *fopen(const char *path, const char *mode);

        返回值:成功返回文件指针,错误返回NULL ,并设置errno,FILE *指针称为不透明指针(Opaque Pointer)或句柄(handle) 。

        int fclose(FILE *fp);

        返回值:成功返回0,错误返回EOF ,并设置errno

2.stdin/stdout/stderr

        因为在程序启动时(在main函数还没开始执行之前)会自动把终端设备打开三次,分别赋给三个FILE *指针stdin、stdout和stderr,这三个文件指针是libc中定义的全局变量,在stdio.h中声明,printf向stdout写,而scanf从stdin读,后面我们会看到,用户程序也可以直接使用这三个文件指针。

        if ((fp = fopen("/hom/download/file1","r")) == NULL)

        {

                fputs("ERROR open file /home/download/file1/n",stderr);

                exit(1);

         }

3.errno与perror函数

       很多系统函数在错误返回时将错误原因记录在libc定义的全局变量errno中,打印errno时,打印出来的只是一个整数值,所以用perror或strerror函数將errno解释成字符串再打印。一个系统函数错误返回后应马上检查errno,在检查errno之前不能再调用其他系统函数。

        #include <stdio.h>
        #include <stdlib.h>
        #include <errno.h>

        int main(void)
        {
                FILE *fp = fopen("abcd","r");
                if(fp ==NULL)
               {
                        //perror("Open file abcd");
                        printf("errno: %d/n",errno);
                        exit(1);
                }
                return 0;
        }
         结果:

        errno: 2
        如果不注销//perror("Open file abcd");此行的结果为:

       Open file abcd: No such file or directory
       errno: 29

        strerror函数可以根据错误号返回错误原因字符串。

        #include <string.h>

        char *strerror (int errnum);

        有些函数的错误码并不保存在errno中,而是通过返回值返回,就不能调用perror打印错误原因了,这时strerror就派上用场了fputs(strerror(n),stderr);

4.以字节为单位的I/O函数:fgetc/fputc/getchar/putchar

        int fgetc(FILE *stream);

        int getchar(void); //相当于调用fgetc(stdin)

        返回值:成功返回读到的字节,出错或读到文件末尾时返回EOF

        int fputc (int c,FILE *stream);

        int putchar(int c);//相当于调用fputc(c,stdout)

        返回值:成功返回写入的字节,出错返回EOF

例子:

#include <stdio.h>

#include <stdlib.h>

int main(void)

{

         FILE *fp; int ch;

        if ( (fp = fopen("file2", "w+")) == NULL)

        {

                perror("Open file file2/n");

                exit(1);

        }

        while ( (ch = getchar()) != EOF)

                fputc(ch, fp);

        rewind(fp); //将文件指针重新指向一个流的开头

        while ( (ch = fgetc(fp)) != EOF)

                putchar(ch);

        fclose(fp);

        return 0;

}

编写一个简单的文件拷贝程序:
#include <stdio.h>
int main(int argc,char *argv[])
{
        FILE *input,*output;
        int letter;
        char *srcFile=NULL;
        char *szTargetFile=NULL;
        srcFile=argv[1];
        szTargetFile=argv[2];
        if((input = fopen(srcFile,"r")) == NULL)
        {
                printf("can't open %s /n",argv[1]);
        }
        else if((output = fopen(szTargetFile,"w")) == NULL)
        {
                printf("can't open %s /n",argv[2]);
        }
        else
        {
                while((letter = fgetc(input)) != EOF)
                {
                        fputc(letter,output);
                }
                fclose(input);
                fclose(output);
        }
        return 0;
}

5.操作读写位置的函数:fseek/ftell/rewind

        int fseek(FILE *stream, long offset. int whence);        //whence:SEEK_SET(从开头移动offset个字节),SEEK_CUR,SEEK_END

        返回值:成功返回0,出错返回-1并设置errno

        int ftell(FILE *stream);

        返回值:成功返回当前读写位置,出错返回-1并设置errno

        void rewind(FILE *stream);

6.以字符串为单位的I/O函数

  1. #include <stdio.h>

  2. char *fgets(char *s, int size, FILE *stream);    //适合文本文件,且文本中不要含'/0'
  3. char *gets(char *s);   //慎用,最好不用
  4. fgets从指定的文件中读一行字符到调用者提供的缓冲区中.
  5. gets从标准输入读入一行字符到调用者提供的缓冲区
  6. 返回值:成功时s指向哪返回的指针就指向哪,出错或读到文件末尾时返回NULL
  7.  
  8. int fputs(const char *s, FILE *stream);
  9. int puts(const char *s);
  10. fputs将缓冲区的字符串写入文件stream,但并不写入结尾的'/0'.
  11. puts将字符串s写到标准输出(不包含结尾的'/0'),然后自动写一个到标准输出。
  12. 返回值:成功返回一个非负整数,出错返回EOF

7.以记录为单位的I/O函数

fread和fwrite用于读写记录,这里的记录是指一串固定长度的字节,比如一个int,一个结构体或者一个定长数组。

  1. #include <stdio.h>

  2. size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
  3. size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
  4. //返回值:读或写的记录数,成功时返回的记录数等于nmemb,出错或读到文件末尾时返回的记录数小于nmemb,也可
  5. //能返回0

练习:

Code:
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3.  
  4. struct record
  5. {
  6.         char name[10];
  7.         int age;
  8. };
  9.  
  10. int main(void)
  11. {
  12.         struct record array[2] = {{"ken",24},{"knuth",28}};
  13.         FILE *fp = fopen("file1","w");
  14.         if (fp == NULL)
  15.         {
  16.                 perror("open file file1");
  17.                 exit(1);
  18.         }
  19.         fwrite(array,sizeof(struct record),2,fp);
  20.         fclose(fp);
  21.         return 0;  
  22. }
Code:
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3.  
  4. struct record
  5. {
  6.         char name[10];
  7.         int age;
  8. };
  9.  
  10. int main(void)
  11. {
  12.         struct record array[2];
  13.         FILE *fp = fopen("file1","r");
  14.         if (fp == NULL)
  15.         {
  16.                 perror("open file file1");
  17.                 exit(1);
  18.         }
  19.         fread(array,sizeof(struct record),2,fp);
  20.         printf("Name1:%s/tAge1:%d/n",array[0].name,array[0].age);
  21.         printf("Name2:%s/tAge2:%d/n",array[1].name,array[1].age);
  22.         fclose(fp);
  23.         return 0;  
  24. }

结果:
[root@localhost fread]# od -tx1 -tc -Ax file1
000000 6b 65 6e 00 00 00 00 00 00 00 00 00 18 00 00 00
         k   e   n  /0  /0  /0  /0  /0  /0  /0  /0  /0 030  /0  /0  /0
000010 6b 6e 75 74 68 00 00 00 00 00 00 00 1c 00 00 00
         k   n   u   t   h  /0  /0  /0  /0  /0  /0  /0 034  /0  /0  /0
000020
[root@localhost fread]# ./fread
Name1:ken             Age1:24
Name2:knuth        Age2:28

8.格式化io函数

printf函数

  1. #include <stdio.h>
  2. int printf(const char *format, ...);//格式化打印到标准输出
  3. int fprintf(FILE *stream, const char *format, ...);//打印到指定的文件stream中
  4. int sprintf(char *str, const char *format, ...);
  5. //并不打印到文件,而是打印到用户提供的缓冲区str中并在末尾加'/0'
  6. int snprintf(char *str, size_t size, const char *format, ...);//指定了缓冲区的长度size
  7.  
  8. #include <stdarg.h>
  9. int vprintf(const char *format, va_list ap);
  10. int vfprintf(FILE *stream, const char *format, va_list ap);
  11. int vsprintf(char *str, const char *format, va_list ap);
  12. int vsnprintf(char *str, size_t size, const char *format, va_list ap);
  13. 返回值:成功返回格式化输出的字节数(不包括字符串的结尾'/0'),出错返回一个负值。

scanf函数

  1. #include <stdio.h>
  2. int scanf(const char *format, ...);//从标准输入读字符
  3. int fscanf(FILE *stream, const char *format, ...);//从指定的文件stream中读字符
  4. int sscanf(const char *str, const char *format, ...);//从指定的字符串str中读字符
  5.  
  6. #include <stdarg.h>
  7. int vscanf(const char *format, va_list ap);
  8. int vfscanf(FILE *stream, const char *format, va_list ap);
  9. int vsscanf(const char *str, const char *format, va_list ap);
  10. 返回值:返回成功匹配和负值的参数个数,成功匹配的参数可能少于所提供的负值参数,返回0表示一个都不匹配,出错或者读到文件或字符串末尾时返回EOF并设置errno

例子:

  1. #include <stdio.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <errno.h>
  5. #include <stdarg.h>
  6. #include <string.h>
  7.  
  8. #define MAXLINE 80
  9.  
  10. void err_sys(const char *fmt, ...)
  11. {
  12.         int err = errno;
  13.         char buf[MAXLINE+1];
  14.         va_list ap; va_start(ap, fmt);
  15.         vsnprintf(buf, MAXLINE, fmt, ap);
  16.         snprintf(buf+strlen(buf), MAXLINE-strlen(buf), ": %s", strerror(err));
  17.         strcat(buf, "/n");
  18.         fputs(buf, stderr);
  19.         va_end(ap);
  20.         exit(1);
  21. }
  22.  
  23. int main(int argc, char *argv[])
  24. {
  25.         FILE *fp;
  26.         if (argc != 2)
  27.         {
  28.                 fputs("Usage: ./a.out pathname/n", stderr);
  29.                 exit(1);
  30.         }
  31.         fp = fopen(argv[1], "r");
  32.         if (fp == NULL)
  33.                 err_sys("Line %d - Open file %s", __LINE__, argv[1]);
  34.         printf("Open %s OK/n", argv[1]);
  35.         fclose(fp);
  36.         return 0;
  37. }

9.c标准库的I/O缓冲区

c标准库的I/O缓冲区有三种类型:

全缓冲:如果缓冲区写满了就写回内核,常规文件通常是全缓冲的。

行缓冲:如果用户程序写的数据中有换行符,就把这一行写回内核,或者如果缓冲区写满了就写回内核。标准输入和标准输出对应终端设备时通常是行缓冲的。

无缓冲:用户程序每次调库函数做写操作都要通过系统调用写回内核。标准错误输出通常是无缓冲的,这样用户程序产生的错误信息可以尽快输出到设备。

二、Unbuffered I/O

1.open/close

  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <fcntl.h>
  4.  
  5. int open(const char *pathname, int flags);
  6. int open(const char *pathname, int flags, made_t mode);
  7. 返回值:成功返回新分配的文件描述符,错误返回-1 ,并设置errno
  8. flags:
  9. O_RDONLY只读打开;O_WRONLY只些打开;O_RDWR可读可写打开,以上三个常数中必须指定一个,且仅允许指定一个,以下选项可以同时指定0个或多个。
  10. O_APPEND追加;
  11. O_CREAT若此文件不存在则创建它,此时必须提供参数mode
  12. O_EXCL如果同时指定了O_CREAT,并且文件已存在,则出错返回
  13. O_TRUNC如果文件存在,并且以只写或可读可写方式打开,则将其长度截断(Truncate)为0字节
  14. O_NONBLOCK对于设备文件,以此方式打开可以做非阻塞I/O
  15.  
  16. mode:指定文件权限,可以用八进制数表示,如0644表示-rw-r--r--,也可以用S_IRUSR,S_IWUSR等宏定义按位或表示
  17.  
  18. #include <unistd.h>
  19. int close(int fd);//fd是要关闭的文件描述符
  20. 返回值:成功返回0,出错返回-1并设置errno

2.read/write函数

  1. #include <unistd.h>

  2. ssize_t read(int fd, void *buf, size_t count);//从打开的设备或文件中读取数据
  3. 返回值:成功返回读取的字节数,出错返回-1并设置errno,如果在调read之前已到达文件末尾,则这次read返回0
  4. ssize_t write(int fd, const void *buf, size_t count);//向打开的设备或文件中写数据
  5. 返回值:成功返回写入的字节数,出错返回-1并设置errno

3.lseek函数:

  1. #include <sys/types.h>
  2. #include <unistd.h>
  3.  
  4. off_t lseek(int fd, off_t offset, int whence);
  5. 参数offset和whence的含义和fseek函数完全相同
  6. 若lseek成功执行,则返回新的偏移量,因此可以用以下方法确定一个打开文件的当前偏移量:
  7. off_t currpos;
  8. currpos = lseek(fd, 0, SEEK_CUR);
  9. 注意:fseek成功时返回0失败时返回-1,要返回当前偏移量需要调用ftell,而lseek成功时返回当前偏移量失败时返回-1

4.fcntl函数:

可以用fcntl函数改变一个已打开的文件的属性,可以重新设置读、些、追加、非阻塞等标志(这些标志称为File Status Flag),而不必重新open文件。

  1. #include <unistd.h>
  2. #include <fcntl.h>
  3.  
  4. int fcntl(int fd, int cmd);
  5. int fcntl(int fd, int cmd, long arg);
  6. int fcntl(int fd, int cmd, struct flock *lock);

例子:

  1. #include <unistd.h>
  2. #include <fcntl.h>
  3. #include <errno.h>
  4. #include <string.h>
  5. #include <stdlib.h>
  6.  
  7. #define MSG_TRY "try again/n"
  8. int main(void)
  9. {
  10.     char buf[10];
  11.     int n;
  12.     int flags;
  13.     flags = fcntl(STDIN_FILENO, F_GETFL);
  14.     flags |= O_NONBLOCK;
  15.     if (fcntl(STDIN_FILENO, F_SETFL, flags) == -1)
  16.     {
  17.         perror("fcntl");
  18.         exit(1);
  19.     }
  20.     tryagain:
  21.     n = read(STDIN_FILENO, buf, 10);
  22.     if (n < 0)
  23.     {
  24.         if (errno == EAGAIN)
  25.         {
  26.             sleep(1);
  27.             write(STDOUT_FILENO, MSG_TRY, strlen(MSG_TRY));
  28.     goto tryagain;
  29.         }
  30.         perror("read stdin");
  31.         exit(1);
  32.     }
  33.     write(STDOUT_FILENO, buf, n);
  34.     return 0;
  35. }

5.ioctl函数:

ioctl用于向设备发控制和配置命令,有些命令也需要读写一些数据,但这些数据是不能用read/write读写的,称为Out-of-band数据。也就是说,read/write读写的数据是in-band数据,是I/O操作的主体,而ioctl命令传送的是控制信息,其中的数据是辅助的数据。

  1. #include <sys/ioctl.h>
  2. int ioctl(int d, int request, ...);
  3. d是某个设备的文件描述符,request是ioctl的命令,可变参数取决于request,通常是一个指向变量或结构体的指针。若出错则返回-1,若成功则返回其它值,返回值也是取决于request

例:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <sys/ioctl.h>
  5. int main(void)
  6. {
  7.     struct winsize size;
  8.     if (isatty(STDOUT_FILENO) == 0)
  9.         exit(1);
  10.     if(ioctl(STDOUT_FILENO, TIOCGWINSZ, &size)<0)
  11.     {
  12.         perror("ioctl TIOCGWINSZ error");
  13.         exit(1);
  14.     }
  15.     printf("%d rows, %d columns/n", size.ws_row, size.ws_col);
  16.     return 0;
  17. }

6.mmap函数:

mmap可以把磁盘文件的一部分直接映射到内存,这样文件中的位置直接就有对应的内存地址,对文件的读写可以直接用指针来做而不需要read/write函数。

  1. includ <sys/mman.h>
  2. void *mmap(void *addr, size_t len, int prot, int flag, int filedes, off_t off);
  3. 如果mmap成功则返回映射首地址,如果出错则返回参数MAP_FAILED。当进程终止时,该进程的映射内存会自动解除,也可以调用munmap解除映射,munmap成功返回0,出错返回-1.
  4. addr:内存地址
  5. len:是需要映射的那一部分文件的长度
  6. off:是从文件的什么位置开始映射,必须是页大小的整数倍(32位体系结构上通常是4k)
  7. filedes:代表文件的描述符
  8. port参数有四种取值:
  9. 1.PROT_EXEC:表示映射的这一段可执行,例如映射共享库
  10. 2.PROT_READ:表示映射的这一段可读
  11. 3.PROT_WRITE:表示映射的这一段可写
  12. 4.PROT_NONE:表示映射的这一段不可访问
  13. flag:常用有两种参数
  14. 1. MAP_SHARED:多个进程对同一个文件的映射是共享的,一个进程对映射的内存做了修改,另一个进程也会看到这种变化。
  15. 2. MAP_PRIVATE:多个进程对同一个文件的映射不是共享的,一个进程对映射的内存做了修改,另一个进程也不会看到这种变化,也不会真的写到文件中去。