5.1 引言

对此我并没有什么要补充的,一般来说我们使用流会比较多

5.2 流和FILE对象

第三章中,所有I/O函数都是针对文件描述符的。
文件描述符,类似于键值对

文件描述符 文件指针
3 0x00000024

而对于标准I/O库,他们的操作则是围绕着流进行的
为什么需要流呢,因为流无需考虑格式转化的问题

5.3 标准输入、标准输出和标准错误

对于一个进程预定义了三个流
标准输入、输出、错误分别对应0、1、2,这就是为什么文件描述符一般从3开始的原因

标准输入 0 STDIN_FILENO stdin
标准输出 1 STDOUT_FILENO stdout
标准错误 2 STDERR_FILENO stderr

简单展示

#标准输入
fyh@VM-16-14-ubuntu:~/note$ cat > ./true 
aaa
bb
cc
^C
fyh@VM-16-14-ubuntu:~/note$ cat true 
aaa
bb
cc

#标准输出
root@VM-16-14-ubuntu:~# tty 
/dev/pts/4
root@VM-16-14-ubuntu:~# echo "Hello World" > /dev/pts/4
Hello World

#标准错误
fyh@VM-16-14-ubuntu:~/note$ ll a
ls: cannot access 'a': No such file or directory
fyh@VM-16-14-ubuntu:~/note$ ll a 2>./error
fyh@VM-16-14-ubuntu:~/note$ cat error 
ls: cannot access 'a': No such file or directory

在 C 语言中,stdinstdoutstderr 是三个标准的文件指针,分别用于处理标准输入、标准输出和标准错误流。它们是由标准库 <stdio.h> 提供的。以下是它们的详细解释和使用示例:

  1. stdin(标准输入)
    • stdin 是标准输入流,通常从键盘读取数据。
    • 可以使用函数如 scanffgets 等从 stdin 读取数据。
  2. stdout(标准输出)
    • stdout 是标准输出流,通常用于向屏幕输出数据。
    • 可以使用函数如 printffprintf(stdout, ...) 等向 stdout 写入数据。
  3. stderr(标准错误)
    • stderr 是标准错误流,专门用于输出错误信息或诊断信息。
    • 可以使用函数如 fprintf(stderr, ...)stderr 写入数据。

5.4 缓冲

关于为什么要减少调用read和write的调用
关于标准I/O提供的3种类型的缓冲

  • 全缓冲
    • 当缓冲区存满的时候,才写入磁盘,减少缓冲次数,能明显带来的好处就是效率提升和磁盘的寿命
  • 行缓冲
    • 当缓冲区识别到换行后,将缓冲区的内容发送到输出设备
  • 不带缓冲
    • 每次输入输出都直接和硬件交互,不经过缓冲区

标准错误是不带缓存的
如果涉及终端设备的其他流,则它们是行缓存的;否则就是全缓存

setbuf

#include <stdio.h>

void setbuf(FILE *stream, char *buffer);

参数

  • stream:指向要设置缓冲区的文件流。

  • buffer:指向缓冲区的指针。如果为 NULL,则表示不使用缓冲区。

    返回值

  • 宽定向正值

  • 字节定向负值

  • 未定向0

#include <stdio.h>

int
main()
{
    FILE *fp;
    if ((fp = fopen("test.txt", "w"))  == NULL)
        printf("fopen test.txt error!");

    char buff[1024];
    //将文件流 fp 的缓冲区设置为 buff。这意味着对该文件的 I/O 操作将使用这个缓冲区,从而提高效率。
    setbuf(fp, buff);

    //把Hello,Wrold写入fp文件流
    fprintf(fp, "Hello, World!\n");

    fclose(fp);

    return 0;
}

setvbuf

#include <stdio.h>

int setvbuf(FILE *stream, char *buffer, int mode, size_t size);

参数

  • stream:指向要设置缓冲区的文件流。

  • buffer:指向缓冲区的指针。如果为 NULL,则由系统自动分配缓冲区。

  • mode:缓冲模式,可以是以下值之一:

    • _IOFBF:全缓冲,只有缓冲区满时才进行实际的 I/O 操作。
    • _IOLBF:行缓冲,当遇到换行符或缓冲区满时进行 I/O 操作。
    • _IONBF:无缓冲,所有 I/O 操作立即执行。
  • size:缓冲区的大小。

    返回值

  • 宽定向正值

  • 字节定向负值

  • 未定向0

#include <stdio.h>

int
main()
{
    FILE *fp;
    if ((fp = fopen("test.txt", "w"))  == NULL)
        printf("fopen test.txt error!");

    char buff[1024];
    //将文件流 fp 的缓冲区设置为 buff。这意味着对该文件的 I/O 操作将使用这个缓冲区,从而提高效率。
    setvbuf(fp, buff, _IOFBF, sizeof(buff) != 0);

    //把Hello,Wrold写入fp文件流
    fprintf(fp, "Hello, World!\n");

    fclose(fp);

    return 0;
}

fflush

强制刷新流

#include <stdio.h>

int fflush(FILE *stream);

参数

  • stream:指向 FILE 结构的指针,表示要刷新的文件流。如果 streamNULL,则刷新所有打开的输出流。

    返回值

  • 成功时,返回 0。

  • 失败时,返回 EOF,并设置相应的错误标志。

5.5 打开流

fopen

#include <stdio.h>

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

参数

  • filename:要打开的文件的名称。

  • mode:文件打开模式,如 "r"(只读)、"w"(写入,文件不存在则创建,存在则清空)、"a"(追加)等。

    返回值

  • 成功时,返回一个指向 FILE 结构的指针。

  • 失败时,返回 NULL

freopen

#include <stdio.h>

FILE *freopen(const char *filename, const char *mode, FILE *stream);

参数

  • filename:要重新打开的文件的名称。

  • mode:文件打开模式,如 "r"(只读)、"w"(写入,文件不存在则创建,存在则清空)、"a"(追加)等。

  • stream:要重新打开的文件流。

    • stdin :读
    • stdout :写
    • stderr :错误

      返回值

  • 成功时,返回一个指向 FILE 结构的指针。

  • 失败时,返回 NULL

#include <stdio.h>

int
main()
{
    FILE *fp, *fp2;
    if ((fp = fopen("test.txt", "w"))  == NULL)
        printf("fopen test.txt error!");

    char buff[1024];
    //将文件流 fp 的缓冲区设置为 buff。这意味着对该文件的 I/O 操作将使用这个缓冲区,从而提高效率。
    setvbuf(fp, buff, _IOFBF, sizeof(buff) != 0);

    //对于以打开的流先关闭,再打开
    if ((fp2 = freopen("test.txt", "w", stdout)) == NULL)
        printf("fopen test.txt error!");

    //把Hello,Wrold写入fp文件流
    //正常情况应该被fp流覆盖,但是文件的流已经被fp2打开
    fprintf(fp2, "fp2 Hello, World!\n");
    fprintf(fp, "fp Hello, World!\n");

    fclose(fp);
    fclose(fp2);

    return 0;
}

fdopen

#include <stdio.h>

FILE *fdopen(int fd, const char *mode);

参数

  • fd:文件描述符。

  • mode:文件打开模式。

    返回值

  • 成功时,返回一个指向 FILE 结构的指针。

  • 失败时,返回 NULL

mode的参数

mode 说明 标志
r 或 rb 读打开 O_RDONLY
w 或 wb 把文件截断至长度0长,写打开 O_WRONLY | O_CREAT | O_TRUNC
a 或 ab 追加:为在文件尾写而打开,或为写而创建 O_WRONLY | O_CREAT | O_APPEND
r+ 或 r+b 或 rb+ 读写打开 O_RDWR
w+ 或 w+b 或 wb+ 把文件截断至长度0长,读写打开 O_RDWR | O_CREAT | O_TRUNC
a+ 或 a+b 或 ab+ 追加读写 O_RDWR | O_CREATE | O_APPEND

“把文件截断至长度0长” 即为从文件起始处开始写, 覆盖
"追加:为在文件尾写而打开" 即为从文件末位处开始写, 追加

三者的区别

1、fopen 是打开路径上的一个文件,或者当前目录的一个文件,可以的形式为/tmp/test.txt或者test.txt。
2、freopen 这里简要说明就是如果流是被打开的,则会先关闭在打开,具体上面贴有代码。
3、fdopen 则可以打开一个以有的文件描述符指向的文件

fclose

关闭一个打开的流

#include <stdio.h>

int fclose(FILE *fp);

5.6 读和写流

getc

#include <stdio.h>

int getc(FILE *stream);

参数

  • stream:指向 FILE 结构的指针,表示要读取字符的文件流。

    返回值

  • 成功时,返回读取的字符(作为一个 unsigned char 转换为 int 类型)。

  • 失败或到达文件末尾时,返回 EOF

fgetc

#include <stdio.h>

int fgetc(FILE *stream);

参数

  • stream:指向 FILE 结构的指针,表示要读取字符的文件流。

    返回值

  • 成功时,返回读取的字符(作为一个 unsigned char 转换为 int 类型)。

  • 失败或到达文件末尾时,返回 EOF

#include <stdio.h>

int main() {
    FILE *fp;
    if ((fp = fopen("test.txt", "r")) == NULL)
        perror("Failed to open file");

    int ch;
    while ((ch = fgetc(fp)) != EOF) {
        putchar(ch);
    }

    fclose(fp);
    return 0;
}

getchar

#include <stdio.h>

int getchar(void);

参数

  • 无参数。

    返回值

  • 成功时,返回读取的字符(作为一个 unsigned char 转换为 int 类型)。

  • 失败或到达文件末尾时,返回 EOF

#include <stdio.h>

int main() {
    int ch;
    printf("Enter some text (Ctrl+D to end):\n");
    while ((ch = getchar()) != EOF) {
        putchar(ch);
    }
    return 0;
}

ferror

用于检查文件流是否发生了错误。

#include <stdio.h>

int ferror(FILE *stream);

参数

  • stream:指向 FILE 结构的指针,表示要检查的文件流。

    返回值

  • 如果文件流发生了错误,返回非零值。

  • 如果文件流没有发生错误,返回零。

feof

用于检查文件流是否到达了文件末尾。

#include <stdio.h>

int feof(FILE *stream);

参数

  • stream:指向 FILE 结构的指针,表示要检查的文件流。

    返回值

  • 如果文件流到达了文件末尾,返回非零值。

  • 如果文件流没有到达文件末尾,返回零。

clearerr

用于清除文件流的错误和文件结束状态。

#include <stdio.h>

void clearerr(FILE *stream);

参数

  • stream:指向 FILE 结构的指针,表示要清除状态的文件流。

ungetc

用于将一个字符推回到文件流中,使得下次读取时可以重新读取该字符。它在处理文件流时非常有用,特别是在需要回退一个字符的情况下。

#include <stdio.h>

int ungetc(int ch, FILE *stream);

参数

  • ch:要推回到文件流中的字符。它被解释为一个 unsigned char,然后被转换为 int 类型。

  • stream:指向 FILE 结构的指针,表示要将字符推回的文件流。

    返回值

  • 成功时,返回推回的字符。

  • 失败时,返回 EOF

    注意事项

  • ungetc 只能推回一个字符到文件流中。

  • 如果文件流已经到达文件末尾,ungetc 可以将文件流状态从文件末尾状态恢复。

  • 推回的字符将在下次读取时首先被读取。

  • ungetc 可以连续调用多次,但具体能推回多少字符取决于实现。

putc

用于将一个字符写入到指定的文件流中。

#include <stdio.h>

int putc(int ch, FILE *stream);

参数

  • ch:要写入的字符。它被解释为一个 unsigned char,然后被转换为 int 类型。

  • stream:指向 FILE 结构的指针,表示要写入字符的文件流。

    返回值

  • 成功时,返回写入的字符。

  • 失败时,返回 EOF

fputc

fputc 函数与 putc 类似,也是用于将一个字符写入到指定的文件流中。它们的功能几乎相同,但 fputc 通常是作为函数调用,而 putc 可能被实现为宏。

#include <stdio.h>

int fputc(int ch, FILE *stream);

参数

  • ch:要写入的字符。它被解释为一个 unsigned char,然后被转换为 int 类型。

  • stream:指向 FILE 结构的指针,表示要写入字符的文件流。

    返回值

  • 成功时,返回写入的字符。

  • 失败时,返回 EOF

putchar

用于将一个字符写入到标准输出(通常是屏幕)。它实际上是 putc(ch, stdout) 的简写。

#include <stdio.h>

int putchar(int ch);

参数

  • ch:要写入的字符。它被解释为一个 unsigned char,然后被转换为 int 类型。

    返回值

  • 成功时,返回写入的字符。

  • 失败时,返回 EOF

5.7 每次一行的I/O

fgets

用于从指定的文件流中读取一行字符串,直到读取到换行符、到达文件末尾或读取了指定数量的字符为止。

#include <stdio.h>

char *fgets(char *str, int n, FILE *stream);

参数

  • str:指向存储读取字符串的字符数组。

  • n:要读取的最大字符数,包括终止的空字符('\0')。

  • stream:指向 FILE 结构的指针,表示要读取的文件流。

    返回值

  • 成功时,返回指向 str 的指针。

  • 失败或到达文件末尾时,返回 NULL

gets

用于从标准输入读取一行字符串,直到读取到换行符或到达文件末尾为止。读取的字符串会自动添加终止的空字符('\0')。

#include <stdio.h>

char *gets(char *str);

参数

  • str:指向存储读取字符串的字符数组。

    返回值

  • 成功时,返回指向 str 的指针。

  • 失败或到达文件末尾时,返回 NULL

5.9 二进制I/O

fread

用于从文件流中读取数据块。

#include <stdio.h>

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

参数

  • ptr:指向存储读取数据的内存块的指针。

  • size:每个数据块的大小(以字节为单位)。

  • nmemb:要读取的数据块的数量。

  • stream:指向 FILE 结构的指针,表示要读取的文件流。

    返回值

  • 成功时,返回读取的完整数据块的数量。

  • 失败或到达文件末尾时,返回值可能小于 nmemb

fwrite

fwrite 函数用于向文件流中写入数据块。

#include <stdio.h>

size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

参数

  • ptr:指向要写入数据的内存块的指针。

  • size:每个数据块的大小(以字节为单位)。

  • nmemb:要写入的数据块的数量。

  • stream:指向 FILE 结构的指针,表示要写入的文件流。

    返回值

  • 成功时,返回写入的完整数据块的数量。

  • 失败时,返回值可能小于 nmemb

#include <stdio.h>
#include <string.h>

int main() {
    FILE *fp = fopen("test.txt", "rb");
    if (fp == NULL) {
        perror("Failed to open file");
        return 1;
    }

    FILE *fp2 = fopen("test1.txt", "wb");
    if (fp2 == NULL) {
        perror("Failed to open file");
        return 1;
    }

    int buffer[5];
    size_t bytesRead = fread(buffer, sizeof(int), 5, fp);
    size_t bytesWritten = fwrite(buffer, sizeof(int), sizeof(buffer)/sizeof(int), fp2);

    fclose(fp);
    fclose(fp2);
    return 0;
}

5.10 定位流

ftell

ftell函数用于获取文件指针的当前位置。它的原型如下:

#include <stdio.h>

long ftell(FILE *stream);

参数

  • stream:文件指针。

    返回值

  • 当前文件指针的位置(以字节为单位),如果出错则返回-1L。

    fseek

    fseek函数用于在文件中移动文件指针。它的原型如下:

#include <stdio.h>

int fseek(FILE *stream, long offset, int whence);

参数

  • stream:文件指针。

  • offset:相对于whence的偏移量。

  • whence:指定从哪里开始偏移。它可以是以下三个值之一:

    • SEEK_SET:从文件的开头开始偏移。
    • SEEK_CUR:从文件指针的当前位置开始偏移。
    • SEEK_END:从文件的末尾开始偏移。

      返回值

  • 如果成功,返回0;如果失败,返回非零值。

rewind

rewind函数用于将文件指针移动到文件的开头。它的原型如下:

#include <stdio.h>

void rewind(FILE *stream);

参数

  • stream:文件指针。
#include <stdio.h>

int main() {
    FILE *fp = fopen("test.txt", "r");
    if (fp == NULL) {
        perror("Failed to open file");
        return 1;
    }

    // 移动文件指针到文件的末尾
    if (fseek(fp, 0, SEEK_END) != 0) {
        perror("Failed to seek to end of file");
        fclose(fp);
        return 1;
    }

    // 获取文件的大小
    long fileSize = ftell(fp);
    if (fileSize == -1L) {
        perror("Failed to tell position in file");
        fclose(fp);
        return 1;
    }
    printf("File size: %ld bytes", fileSize);

    // 将文件指针移动到文件的开头
    rewind(fp);

    // 读取文件的前10个字节
    char buffer[11];
    size_t bytesRead = fread(buffer, 1, 10, fp);
    if (bytesRead < 10 && ferror(fp)) {
        perror("Failed to read from file");
        fclose(fp);
        return 1;
    }
    buffer[bytesRead] = '\0';  // 添加字符串终止符
    printf("First 10 bytes: %s\n", buffer);

    fclose(fp);
    return 0;
}

ftellofseeko 是用于文件流定位的函数,它们是 ftellfseek 的大文件版本,支持大于2GB的文件操作。这些函数使用 off_t 类型来表示文件偏移量,而不是 long 类型,从而支持更大的文件。

ftello

ftello 函数用于获取文件指针的当前位置。它的原型如下:

#include <stdio.h>

off_t ftello(FILE *stream);

参数

  • stream:文件指针。

    返回值

  • 当前文件指针的位置(以字节为单位),如果出错则返回 -1。

    fseeko

    fseeko 函数用于在文件中移动文件指针。它的原型如下:

#include <stdio.h>

int fseeko(FILE *stream, off_t offset, int whence);

参数

  • stream:文件指针。

  • offset:相对于 whence 的偏移量。

  • whence:指定从哪里开始偏移。它可以是以下三个值之一:

    • SEEK_SET:从文件的开头开始偏移。
    • SEEK_CUR:从文件指针的当前位置开始偏移。
    • SEEK_END:从文件的末尾开始偏移。

      返回值

  • 如果成功,返回 0;如果失败,返回非零值。

fgetposfsetpos 是用于文件流定位的标准库函数。它们提供了一种独立于平台的方式来保存和恢复文件指针的位置。与 ftellfseek 不同,fgetposfsetpos 使用 fpos_t 类型来表示文件位置,这在某些平台上可能比 long 类型更精确。

fgetpos

fgetpos 函数用于获取文件指针的当前位置,并将其存储在一个 fpos_t 类型的变量中。它的原型如下:

#include <stdio.h>

int fgetpos(FILE *stream, fpos_t *pos);

参数

  • stream:文件指针。

  • pos:指向 fpos_t 类型变量的指针,用于存储文件指针的位置。

    返回值

  • 如果成功,返回 0;如果失败,返回非零值。

    fsetpos

    fsetpos 函数用于将文件指针移动到由 fgetpos 保存的位置。它的原型如下:

    #include <stdio.h>
    int fsetpos(FILE *stream, const fpos_t *pos);

    参数

  • stream:文件指针。

  • pos:指向 fpos_t 类型变量的指针,表示要移动到的位置。

    返回值

  • 如果成功,返回 0;如果失败,返回非零值。

5.11 格式化I/O

printf

printf 函数用于将格式化的输出写入标准输出(通常是屏幕)。它的原型如下:

#include <stdio.h>

int printf(const char *format, ...);

参数

  • format:格式字符串,包含普通字符和格式说明符。
  • ...:可变参数列表,包含要格式化的值。

    返回值

    成功时返回输出的字符数,失败时返回负值。

fprintf

fprintf 函数用于将格式化的输出写入指定的文件流。它的原型如下:

#include <stdio.h>

int fprintf(FILE *stream, const char *format, ...);

参数

  • stream:文件指针,指定输出目标。
  • format:格式字符串,包含普通字符和格式说明符。
  • ...:可变参数列表,包含要格式化的值。

    返回值

    成功时返回输出的字符数,失败时返回负值。

#include <stdio.h>

int
main()
{
    FILE *fp;
    int rt;
    if ((fp = fopen("test.txt", "w")) == NULL)
        printf("fopen test.txt error!\n");

    if ((rt = fprintf(fp, "aaabbbb")) < 0)
        printf("fprintf error!\n");

    return 0;
}

dprintf

dprintf 函数用于将格式化的输出写入指定的文件描述符。它的原型如下:

#include <stdio.h>

int dprintf(int fd, const char *format, ...);

参数

  • fd:文件描述符,指定输出目标。
  • format:格式字符串,包含普通字符和格式说明符。
  • ...:可变参数列表,包含要格式化的值。

    返回值

    成功时返回输出的字符数,失败时返回负值。

dprintf.c: In function ‘main’:
dprintf.c:17:9: warning: implicit declaration of function ‘close’; did you mean ‘pclose’? [-Wimplicit-function-declaration]
   17 |         close(fd);
      |         ^~~~~
      |         pclose

//添加 unistd
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

int
main()
{
        int fd, num, rt;
        if ((fd = open("test.txt", 2)) == -1)
                printf("open test.txt error!\n");

        num = 10;
        if ((rt = dprintf(fd, "The number is %d", num)) < 0)
                printf("dprintf error!");

        close(fd);

        return 0;
}

sprintf

sprintf 函数用于将格式化的输出写入字符串。它的原型如下:

#include <stdio.h>

int sprintf(char *str, const char *format, ...);

参数

  • str:目标字符串,存储格式化的输出。
  • format:格式字符串,包含普通字符和格式说明符。
  • ...:可变参数列表,包含要格式化的值。

    返回值

    成功时返回输出的字符数,失败时返回负值。

#include <stdio.h>

int
main()
{
        char buff[100] = "Hello world!";
        char my[] = "My app";
        sprintf(buff, "This is %s\n", my);

        printf("%s", buff);

        return 0;
}

snprintf

snprintf 函数用于将格式化的输出写入字符串,并指定最大字符数。它的原型如下:

#include <stdio.h>

int snprintf(char *str, size_t size, const char *format, ...);

参数

  • str:目标字符串,存储格式化的输出。
  • size:目标字符串的最大长度。
  • format:格式字符串,包含普通字符和格式说明符。
  • ...:可变参数列表,包含要格式化的值。

    返回值

    成功时返回输出的字符数(不包括终止符),如果输出被截断,则返回欲写入的字符数。

  1. **printf**:将格式化的输出写入标准输出。
  2. **fprintf**:将格式化的输出写入指定的文件流。
  3. **dprintf**:将格式化的输出写入指定的文件描述符。
  4. **sprintf**:将格式化的输出写入字符串。
  5. **snprintf**:将格式化的输出写入字符串,并指定最大字符数,防止缓冲区溢出。

格式化参数

例: printf("%d", i);

#include <stdio.h>

int
main()
{
        FILE *fp;
        int rt;
        if ((fp = fopen("test.txt", "w")) == NULL)
                printf("fopen test.txt error!\n");

        if ((rt = fprintf(fp, "%d\n" , "a")) < 0)
                printf("fprintf error!\n");

        return 0;
}
#似乎是寻找了一个变量而非转换ascii码
fyh@VM-16-14-ubuntu:~/notec$ cat test.txt 
-803508187
  • [flags]
#include <stdio.h>

int
main()
{
        int num=1000000;
        printf("': %'d\n", num);
        printf("-: %-d\n", num);
        printf("+: %+d\n", num);
        printf("\" \": % d\n", num);
        printf("#: %#0x\n", num);
        printf("0: %0d\n", num);

        return 0;
}
fyh@VM-16-14-ubuntu:~/notec$ ./printf 
': 1000000
-: 1000000
+: +1000000
" ":  1000000
#: 0xf4240
0: 1000000
  • ' :将整数按千位分组字符
    • :在字段内左对齐输出
    • :总是显示带符号转换的正负号
  • (空格) :如果第一个字符不是正负号,则在其前面加上一个空格
  • #:指定另一种转换形式(例如 ,对于十六进制格式,加前缀0x)
  • 0 :添加前导0进行填充
    • [fldwidth]
#include <stdio.h>

int
main()
{
        int num=10;
        printf("d: %d\n", num);
        printf("i: %i\n", num);
        printf("o: %o\n", num);
        printf("u: %u\n", num);
        printf("x: %x\n", num);

        return 0;
}
fyh@VM-16-14-ubuntu:~/notec$ ./printf 
d: 10
i: 10
o: 12
u: 10
x: a
  • d、i:有符号十进制
  • o:无符号八进制
  • u:无符号十进制
  • x、X:无符号十六进制
  • f、F:双精度浮点数
  • e、E:指数格式双精度浮点数
  • g、G:根据转换后的值解释为f、F、e、E
  • a、A:十六进制指数格式双精度浮点数
  • c:字符(若带有长度修饰符1,为宽字符)
  • s:字符串(若带有长度修饰符1,为宽字符)
  • p:指向void的指针
    • [precision]
    • [lenmodifier]
#include <stdio.h>

int
main()
{
        short int sc = 10;
        printf("hhd: %hhd\n", sc);

        return 0;
}
fyh@VM-16-14-ubuntu:~/notec$ ./printf 
hhd: 10
  • hh:将相对应的参数按signed或unsigned char 类型输出
  • h:将相应的参数按signed或unsigned short类型输出
  • l:将相应的参数按signed或unsigned long或宽字符类型输出
  • ll:将相应的参数按signed或unsigned long long类型输出
  • j:intmax_t 或 uintmax_t
  • z:size_t
  • t:prtdiff_t
  • L:long double

变形体

vprintf, vfprintf, vdprintf, vsprintf, vsnprintf 是C语言中的一组函数,它们用于格式化输出,与 printf, fprintf, dprintf, sprintf, snprintf 类似,但它们接受一个 va_list 类型的参数,而不是可变参数。这使得它们可以在其他接受可变参数的函数中使用。

#include <stdarg.h>
#include <stdio.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);
#include <stdio.h>
#include <stdarg.h>

void
myprintf(int count, ...)
{
    va_list mys;
    va_start(mys, count);
    for (int i = 0; i < count; i++)
    {
        vprintf("%d\n", mys);
    }
    va_end(mys);
}

int
main()
{
    myprintf(4, 1, 2, 3, 4);

    return 0;
}
fyh@VM-16-14-ubuntu:~/notec$ ./vprintf 
1
2
3
4

va_list 是 C 语言标准库中的一个类型,用于处理可变参数函数。可变参数函数是指参数数量不固定的函数,例如 printf。为了处理这些可变参数,C 语言提供了一组宏和类型,包括 va_listva_startva_argva_end

#include <stdio.h>
#include <stdarg.h>

// 可变参数函数,计算所有参数的和
int sum(int count, ...) {
    va_list args;
    int total = 0;

    // 初始化 va_list 变量
    va_start(args, count);

    // 访问每个参数
    for (int i = 0; i < count; i++) {
        total += va_arg(args, int);
    }

    // 清理 va_list 变量
    va_end(args);

    return total;
}

int main() {
    //4 对应count
    int result = sum(4, 1, 2, 3, 4); // 计算 1 + 2 + 3 + 4
    printf("The sum is %d\n", result); // 输出结果

    return 0;
}
fyh@VM-16-14-ubuntu:~/notec$ ./va_list 
The sum is 10

scanf

scanf 从标准输入(通常是键盘)读取格式化数据。

#include 

int scanf(const char *format, ...);

返回值

赋值的输入项数,若输入出错或在任一转换前以达到文件尾端,返回EOF

#include <stdio.h>

int
main()
{
    int num;
    char cr[30];
    //%[^\n] 读到换行符就停下
    scanf("%d %[^\n]", &num, cr);

    printf("输入内容:%d %s\n", num, cr);

    return 0;
}

fscanf

fscanf 从文件流中读取格式化数据。

#include <stdio.h>

int fscanf(FILE *stream, const char *format, ...);

返回值

赋值的输入项数,若输入出错或在任一转换前以达到文件尾端,返回EOF

#include <stdio.h>

int
main()
{
    FILE *file;
    char cr[50];
    file = fopen("test.txt", "r");

    fscanf(file, "%[^\n]", cr);
    printf("%s", cr);

    return 0;
}

sscanf

sscanf 从字符串中读取格式化数据。

#include <stdio.h>

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

int
main()
{
    const char * input = "25 5.9 JohnDoe";
    int age;
    float height;
    char name[50];

    sscanf(input, "%d %f %[^/n]", &age, &height, name);

    printf("Age: %d\nHeight: %.2f\nName: %s\n", age, height, name);

    return 0;
}
fyh@VM-16-14-ubuntu:~/notec$ ./sscanf 
Age: 25
Height: 5.90
Name: Joh

5.12 实现细节

fileno

fileno 是一个 C 标准库函数,用于获取与文件流(FILE *)关联的文件描述符。文件描述符是一个整数,表示打开的文件、管道或网络套接字等资源。文件描述符在底层操作系统级别使用,而文件流在 C 标准库级别使用。

#include <stdio.h>

int fileno(FILE *stream);

参数

  • stream:指向一个打开的文件流的指针(FILE *)。

    返回值

  • 成功时,返回与文件流关联的文件描述符。

  • 失败时,返回 -1,并设置 errno 以指示错误。

#include <stdio.h>
#include <unistd.h>

int
main()
{
    FILE *file = fopen("test.txt", "w");

    int fd = fileno(file);

    char *text = "Hello World!\n";
    write(fd, text, 14);

    fclose(file);

    return 0;
}

5.13 临时文件

tmpnam

tmpnam 函数生成一个唯一的临时文件名。

#include <stdio.h>

char *tmpnam(char *s);

参数

  • s:指向一个足够大的缓冲区的指针,用于存储生成的文件名。如果 sNULL,则函数返回一个指向内部静态缓冲区的指针。

    返回值

  • 成功时,返回一个指向唯一文件名的指针。

  • 失败时,返回 NULL

#include <stdio.h>

int main()
{
    char buffer[L_tmpnam];
    char *ptr;

    tmpnam(buffer);
    printf("临时名称 1: %s\n", buffer);

    ptr = tmpnam(NULL);
    printf("临时名称 2: %s\n", ptr);

    return(0);
}
#include <stdio.h>

int main() {
    char buffer[100] = {0};
    FILE *stream = fmemopen(buffer, sizeof(buffer), "w+");

    if (stream == NULL) {
        perror("Failed to open memory stream");
        return 1;
    }

    // 写入数据
    const char *text = "Hello, World!";
    if (fwrite(text, 1, 13, stream) != 13) {
        perror("Failed to write to memory stream");
        fclose(stream);
        return 1;
    }

    // 重置文件位置指示器
    rewind(stream);

    // 读取数据
    char read_buffer[100] = {0};
    if (fread(read_buffer, 1, 13, stream) != 13) {
        perror("Failed to read from memory stream");
        fclose(stream);
        return 1;
    }

    printf("Read from memory stream: %s
", read_buffer);

    fclose(stream);
    return 0;
}

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注