• Linux C常见数I/O函数比较: printf, sprintf, fprintf, write...


    1. 输出

    1.1 输出格式化字符串

    1.1.1 printf

    输出格式化字符串到标准输出设备stdout

    #include <stdio.h>
    int printf(const char *format, ...);
    
    • 参数
      format 格式化字符串以null终结符结尾
      ... 对应format中转移字符, 支持char, short, int, long, float, double, 有符号/无符号等类型, char* (字符串), 不过, 需要配套转义字符.

    • 返回值
      成功返回写入的字符总数(不包括null终结符); 失败, 返回负数.

    如果要向屏幕/控制台打印一个文本字符串, 可以直接用printf.
    示例:

    int age = 20;
    printf("my age is %d
    ", age);
    

    注意: printf有自己的库缓存, " " 会将内容冲刷到内核IO缓存, 然后输出. 如果不使用 , 默认不会立即打印, 需要等待IO库缓存满.
    可以调用fflush(stdout); 将内容主动冲刷.


    1.1.2 fprintf

    将格式化串输出到流文件中, 除了第一个参数需要调用者指定流, 其他同printf.

    注意第一个参数是流文件, 如stdout, stderr, etc.

    #include <stdio.h>
    int fprintf(FILE *stream, const char *format, ...);
    

    向标准错误输出stderr打印一条错误消息

    fprintf(stderr, "post a error message: %s
    ", strerror(errno));
    

    1.1.3 sprintf

    将格式化串输出到缓存中, 除了第一个参数需要调用者指定流, 其他同printf.

    #include <stdio.h>
    int sprintf(char *str, const char *format, ...);
    

    将一个字符串写到指定缓存中:

    char buf[250];
    sprintf(buf, "post a error message: %s
    ", strerror(errno));
    

    这里存在一个潜在的问题: 如果要写到缓存中的数据, 超过了缓存空间, 会导致内存溢出, 从而导致未定义行为.
    使用sprintf就要求调用者得确保不会溢出. 另外的解决办法是, 使用更安全的snprintf.


    1.1.4 snprintf

    将指定长度的格式化串输出到缓存中, 其他同sprintf. 比sprintf更安全.

    #include <stdio.h>
    int snprintf(char *str, size_t size, const char *format, ...);
    

    将一个字符串写到指定缓存中:

    char buf[250];
    snprintf(buf, sizeof(buf), "post a error message: %s
    ", strerror(errno));
    

    通常, 将第二个参数size设置为缓存大小, 但不一定非得这样做, 可根据实际情况决定.


    1.1.5 dprintf

    将格式化串输出到已打开文件描述符, 其他同printf.

    #include <stdio.h>
    int sprintf(char *str, const char *format, ...);
    

    示例: 向标准输出(默认已打开, 文件描述符 = STDOUT_FILENO)输出

    int age = 20;
    dprintf(STDOUT_FILENO, "my age is %d
    ", age);
    

    1.1.6 v版本输出格式化字符串

    带v版本的函数vprintf, vfprintf, vdprintf(), vsprintf(), vsnprintf() 等同于函数printf(), fprintf(), dprintf(), sprintf(), snprintf(), 除了v版本输出函数使用va_list表示可变参数, 而不带v版本初始函数用"..."表示可变参数.

    #include <stdarg.h>
    
    int vprintf(const char *format, va_list ap);
    int vfprintf(FILE *stream, const char *format, va_list ap);
    int vdprintf(int fd, const char *format, va_list ap);
    int vsprintf(char *str, const char *format, va_list ap);
    int vsnprintf(char *str, size_t size, const char *format, va_list ap);
    

    printf可以用vprintf来实现:

    int printf(const char *format, ...)
    {
        va_list ap;
        int ret;    
    
        va_start(ap, format);
        ret = vprintf(format, ap);
        va_end(ap);
    
        return ret;
    }
    

    1.2 输出字符&字符串

    1.2.1 putchar

    向标准输出设备写一个字符c

    #include <stdio.h>
    int putchar(int c);
    
    • 返回值
      成功, 返回写入字符对应ASCII码; 失败, 返回EOF.

    示例: 打印一个字符

    char c = 'a';
    int ret = putchar(c);
    if (ret == EOF) {
        // 发生错误
    }
    

    1.2.2 putc, fputc

    向指定文件流stream写一个字符c. fputc <=> putc, 区别在于putc可能是用宏定义实现的.
    putchar(c) <=> putc(c, stdout)

    #include <stdio.h>
    int putc(int c, FILE *stream);
    int fputs(const char *s, FILE *stream);
    

    示例: 向stdout写一个字符

    char c = 'a';
    int ret = putc(c, stdout); // <=> ret = fputc(c, stdout);
    if (ret == EOF) {
        // 发生错误
    }
    

    1.2.3 fputc

    fputc <=> putc, 区别在于putc可能是用宏定义实现的.

    #include <stdio.h>
    int fputc(int c, FILE *stream);
    

    1.2.4 puts, fputs

    puts向stdout写一行字符串;
    fputs向指定文件流写一行字符串;

    注意: 字符串末尾会自动换行

    #include <stdio.h>
    
    int fputs(const char *s, FILE *stream);
    int puts(const char *s);
    

    示例: 写一行字符串

    puts("hello, this is a test string");
    fputs("hello, this is a test string", stdout);
    

    1.3 write

    向已打开文件描述符. 将缓存buf内容写count个字节到fd指向的文件. buf不必以null终结符结尾.

    #include <unistd.h>
    ssize_t write(int fd, const void *buf, size_t count);
    

    示例: 向stdout写入所有缓存字符

    char buf[2] = {'a', 'b'};
    int n = write(STDOUT_FILENO, buf, sizeof(buf));
    if (n < 0) {
        perror("write error");
        exit(1);
    }
    

    2. 输入

    2.1 输入格式化字符串

    2.1.1 scanf, fscanf, sscanf

    scanf 从标准输入设备stdin读取格式化字符串
    fscanf 从指定文件流读取格式化字符串, 其他同scanf
    sscanf 从指定缓存str中读取格式化字符串

    #include <stdio.h>
    
    int scanf(const char *format, ...);
    int fscanf(FILE *stream, const char *format, ...);
    int sscanf(const char *str, const char *format, ...);
    

    2.1.2 vscanf, vfscanf, vsscanf

    vscanf, vfscanf, vsscanf分别等同scanf, fscanf, sscanf, 除了可变参数不一样: 带v的函数, 可变参数是va_list类型; 不带v的函数, 可变参数是用"..."表示

    #include <stdarg.h>
    
    int vscanf(const char *format, va_list ap);
    int vsscanf(const char *str, const char *format, va_list ap);
    int vfscanf(FILE *stream, const char *format, va_list ap);
    

    同样的, scanf可以用fscanf来实现

    int scanf(const char *format, ...)
    {
        va_list(ap, format);
        int ret;
    
        va_start(ap);
        ret = vscanf(format, ap);
        va_end(ap);
        return ret;
    }
    

    2.2 输入一行字符串

    2.2.1 gets

    gets从stdin读取一行字符串, 直到新行EOF, 并用null终结符替代(换行符, 或EOF). 已弃用, gets没有检查缓冲区边界, 很可能会产生缓冲区溢出的问题. 替代方案是fgets.

    #include <stdio.h>
    
    char *gets(char *s);
    

    2.2.2 fgets

    fgets从指定文件流读取指定字节数的字符, 当遇到EOF或新行时, 停止读取; 或者缓冲区满时, 也会停止读取. 末尾元素会确保是null终结符.

    #include <stdio.h>
    
    char *fgets(char *s, int size, FILE *stream);
    

    2.2.3 getchar, getc, fgetc

    getchar 从stdin读取一个字符
    getc 从指定文件流读取一个字符
    fgetc <=> getc, 区别在于getc可能用宏定义实现, fgetc是函数实现

    getchar() <=> getc(stdin) <=> fgetc(stdin)

    #include <stdio.h>
    
    int fgetc(FILE *stream);
    int getc(FILE *stream);
    int getchar(void);
    

    2.2.4 ungetc

    ungetc 将一个字符c推入指定文件流中, 使其下一个读取到的字符就是推入的字符c

    #include <stdio.h>
    
    int ungetc(int c, FILE *stream);
    

    2.3 getline, getdelim

    getline 从指定文件流读取一行数据, 读取的存入lineptr 指向的地址, 长度存入 结果参数n. 当读取到换行符( )时, getline会认为这是一行结束.
    getline会用malloc分配缓冲区
    lineptr, 用于存储该行字符串, 当*lineptr指向的缓冲区大小不够时, getline会用realloc重新调整其大小

    getdelim 工作类似于getline, 除了由参数delim指定行分隔符, 而非默认的' '.
    getdelim(&line, &len, stream, ' ') <=> getline(&line, &len, stream);

    getline 和fgets区别
    都是从指定文件流读取一行字符串, 区别在于:

    1. getline会申请分配内存, 调用者只需读取分配的缓存首地址、大小, 调用完毕后, 需要主动释放(free), 而fgets需要调用者提供缓存.
    2. getline返回成功读取字符串长度, 而fgets返回字符串(缓存)地址;
    3. getline是POSIX.1内容, fgets C89和POSIX.1内容;
    #include <stdio.h>
    
    ssize_t getline(char **lineptr, size_t *n, FILE *stream);
    
    ssize_t getdelim(char **lineptr, size_t *n, int delim, FILE *stream);
    
    • 参数
      lineptr 由调用者提供, *lineptr指向由getline分配用于存放字符串的缓冲区. 不能为NULL
      n n为缓冲区lineptr大小, 由getline设置. 不能为NULL
      stream 文件流, 由调用者指定

    • 返回值
      成功时, 返回读取到的字符数(包含分隔符, 不包含null终结符); 失败时, 返回-1, errno被设置.

    示例: 从指定文件按行读取内容, 并打印到stdout

    #include <stdio.h>
    #include <stdlib.h>
    
    int main()
    {
        char *line = NULL;
        ssize_t nread;
        size_t len;
        FILE *stream;
    
        stream = fopen("./testfile", "r");
        if (stream == NULL) {
            perror("fopen");
            exit(EXIT_FAILURE);
        }
    
        while ((nread = getline(&line, &len, stream )) != -1) {
            printf("Retrieved line of length %zu:
    ", nread);
            fwrite(line, nread, 1, stdout);
        }
        
        free(line);
        fclose(stream);
    
        return 0;
    }
    

    运行结果如下, 可以看到fwrite缓冲区line内容时有换行, 可知getline并没有把换行符替换掉.

    $ cat testfile 
    hello, this is a test file
    test data:
    abc 123;
    yuiop @09876_54qwertyuio!;
    
    $ ./a.out 
    Retrieved line of length 27:
    hello, this is a test file
    Retrieved line of length 11:
    test data:
    Retrieved line of length 9:
    abc 123;
    Retrieved line of length 27:
    yuiop @09876_54qwertyuio!;
    Retrieved line of length 1:
    

    2.4 read

    read 从已打开文件描述符读取指定字节数的内容, 存放到缓存buf中
    缓存buf由调用者提供, 读取碰到EOF或者数量已达count为止. 读取的字节数不会超过count.

    #include <unistd.h>
    
    ssize_t read(int fd, void *buf, size_t count);
    

    示例: 从已打开文件, 读取并打印所有数据

    #define FILE_NAME     "./testfile"
    
    char buf[2];
    int n;
    int fd;
    
    fd = open(FILE_NAME, O_RDONLY);
    
    while ((n = read(fd, buf, sizeof(buf))) > 0) {
        write(STDOUT_FILENO, buf, n);  /* 输出到stdout */
    }
    
    close(fd);
    

    2.5 fread

    fread从指定文件流读取指定数量的内容到指定缓冲区ptr

    #include <stdio.h>
    
    size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
    
    • 参数
      ptr 缓冲区首地址
      size 要读取的元素个数
      nmemb 要读取的每个元素大小(byte)

    • 返回值
      成功, 返回读取的元素个数; 失败, 读取的总数≠nmemb(到达EOF或者出错), 此时需要用用feof, ferror来区分到达文件末尾, 还是出错.

    示例: 从已打开文件, 读取并打印所有数据

    #define FILE_NAME     "./testfile"
    
    char buf[2];
    int n;
    FILE *fp;
    
    fp = fopen(FILE_NAME, "r");
    while ((n = fread(buf, sizeof(buf), sizeof(buf[0]), fp)) != sizeof(buf)) {
        write(STDOUT_FILENO, buf, n);
    }
    
    if (feof(fp)) {
        write(STDOUT_FILENO, buf, n);
    }
    
    if (ferror(fp)) {
        fprintf(stderr, "fread error
    ");
    }
    
    fclose(fp);
    
  • 相关阅读:
    Experimental Educational Round: VolBIT Formulas Blitz D
    Experimental Educational Round: VolBIT Formulas Blitz C
    windows10 IOT +Azure会议概要总结
    财务报表开发实例分析:几个通用维度介绍与关键点
    eclipse中LogCat有时不显示信息的简单解决办法
    友情提醒:欲开发android5.0以上应用,请全部更新开发工具至最新
    高通、猎户机型Android典型bootloader分析
    Ubuntu 14.04 中安装 VMware10 Tools工具
    Linux内核中的GPIO系统之(3):pin controller driver代码分析
    linux内核中的GPIO系统之(2):pin control subsystem
  • 原文地址:https://www.cnblogs.com/fortunely/p/14872465.html
Copyright © 2020-2023  润新知