C语言OutputStream

99ANYc3cd6
预计阅读时长 17 分钟
位置: 首页 C语言 正文

C 语言本身没有像 Java 或 C# 那样名为 OutputStream 的类或接口。

C语言OutputStream
(图片来源网络,侵删)

C 语言采用的是一种更接近底层、基于文件指针的 I/O 模型,我们可以将 C 语言中用于写入数据的一系列函数和机制,理解为“输出流”的等价概念,这些函数共同构成了 C 语言的输出功能。

下面我将从几个层面来为你解析 C 语言的“OutputStream”:

核心概念:文件指针

在 C 语言中,所有 I/O 操作都通过一个指向 FILE 结构体的指针来完成,这个指针通常被称为文件指针FILE 结构体在 <stdio.h> 中定义,它包含了控制文件 I/O 所需的所有信息,比如文件的缓冲区、当前读写位置、文件描述符等。

你可以把 FILE* 想象成一个“流”的句柄或控制器,当你打开一个文件(或标准输入/输出设备)时,你会得到一个 FILE*,后续所有的写入操作都通过这个指针来“流入”目标。

C语言OutputStream
(图片来源网络,侵删)

标准输出流

最基本、最常用的“输出流”就是标准输出流,它在程序启动时自动打开,对应的文件指针是 stdout

  • 声明: extern FILE *stdout; (在 <stdio.h> 中)
  • 默认目标: 你的终端/控制台。
  • 使用: 你不需要手动打开它,直接使用 printf, puts, putchar 等函数时,数据默认就会写入 stdout
#include <stdio.h>
int main() {
    // printf 的输出默认流向 stdout,即你的控制台
    printf("Hello, World!\n"); 
    // puts 也是如此
    puts("This is going to stdout as well.");
    return 0;
}

通用输出流函数

除了 printf 这种格式化输出函数,C 语言还提供了一系列通用的字节写入函数,它们是构建更复杂输出流的基础。

fwrite 函数 - 二进制写入

fwrite 是最核心的“输出流”函数之一,它将一个内存块中的数据原样(二进制格式)写入到输出流中。

#include <stdio.h>
int main() {
    // 打开一个文件用于写入(如果不存在则创建,如果存在则清空)
    FILE *fp = fopen("data.bin", "wb"); // "wb" 表示二进制写入
    if (fp == NULL) {
        perror("Failed to open file");
        return 1;
    }
    int numbers[] = {10, 20, 30, 40, 50};
    size_t num_elements = sizeof(numbers) / sizeof(numbers[0]);
    // 将 numbers 数组的内容写入文件流 fp
    size_t written = fwrite(numbers, sizeof(int), num_elements, fp);
    if (written != num_elements) {
        perror("Failed to write data");
    } else {
        printf("Successfully wrote %zu integers to data.bin\n", written);
    }
    // 关闭文件流,释放资源
    fclose(fp);
    return 0;
}

fwrite 的原型: size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

C语言OutputStream
(图片来源网络,侵删)
  • ptr: 指向要写入数据的内存地址。
  • size: 每个数据项的大小(字节)。
  • nmemb: 要写入的数据项的数量。
  • stream: 目标文件指针(即我们的“输出流”)。

fputcfputs 函数 - 字符和字符串写入

  • fputc: 向输出流写入单个字符
    FILE *fp = stdout;
    fputc('A', fp); // 在控制台输出字符 'A'
  • fputs: 向输出流写入一个字符串(不自动添加换行符)。
    FILE *fp = stdout;
    fputs("This is a string.\n", fp); // 在控制台输出字符串

高级特性:缓冲与刷新

C 语言的 I/O 流是缓冲的,这意味着数据并不是立即写入到磁盘或终端,而是先存放在一个内存缓冲区中,当缓冲区满了或者被手动刷新时,才会真正发生 I/O 操作。

  • 为什么需要缓冲? 减少 I/O 操作次数,大大提高程序性能,因为直接向磁盘或终端写一个字节非常慢,而向内存写则快得多。
  • 刷新缓冲区:
    • fflush(stream): 强制将指定流缓冲区中的数据写入其关联的文件或设备。
    • fclose(stream): 在关闭文件时,会自动刷新缓冲区。
    • 缓冲区满时: 会自动刷新。
    • 程序正常结束时: 所有打开的流都会被自动刷新和关闭。
#include <stdio.h>
#include <time.h>
int main() {
    printf("This message is in the buffer.\n");
    // 下面的代码会让程序暂停5秒,但你不会在控制台看到任何输出
    // 因为 printf 的输出还在缓冲区里
    sleep(5); 
    // 当程序结束时,缓冲区才会被刷新,所以你会看到消息一闪而过
    return 0;
}

如果你想在 sleep 之前就看到输出,就需要手动刷新:

#include <stdio.h>
#include <time.h>
int main() {
    printf("This message is in the buffer.\n");
    fflush(stdout); // 手动刷新标准输出流
    sleep(5); // 现在你会立即看到消息,然后程序暂停5秒
    return 0;
}

“流”的重定向

C 语言的“流”概念非常灵活,你可以轻松地将同一个输出函数(如 printf)的目标从 stdout(控制台)重定向到文件、管道或其他任何地方。

#include <stdio.h>
int main() {
    // 默认情况下,printf 输出到控制台
    printf("This goes to the console.\n");
    // 打开一个文件
    FILE *file_output = fopen("output.txt", "w");
    if (file_output == NULL) {
        return 1;
    }
    // 将 stdout 重定向到我们的文件
    // 这之后,所有对 stdout 的写入操作都会写入到 output.txt 文件中
    stdout = file_output;
    printf("This line goes to output.txt!\n");
    printf("This one too.\n");
    // 恢复 stdout 到原始的控制台
    // 这是一个好习惯,尤其是在更复杂的程序中
    fflush(stdout); // 先刷新缓冲区
    stdout = freopen(NULL, "w", stdout); // 重定向回原始标准输出
    printf("And this is back on the console.\n");
    // 关闭文件
    fclose(file_output);
    return 0;
}

注意: 直接对 stdout 进行赋值 (stdout = file_output;) 是一种简单但不够稳健的方法,更推荐使用 freopen 函数。

C 语言的“OutputStream”等价物

Java/C# 概念 C 语言等价物 描述
OutputStream FILE* 指针 代表一个输出流的句柄,通过 fopen() 获取。
write(int b) fputc(int c, FILE *stream) 写入单个字节/字符。
write(byte[] b) fwrite(const void *ptr, ...) 写入一块二进制数据。
flush() fflush(FILE *stream) 强制将缓冲区内容写入底层设备。
System.out stdout 预定义的标准输出流文件指针。
构造函数/new fopen("filename", "mode") 打开一个文件,返回一个 FILE* 流。
close() fclose(FILE *stream) 关闭流,释放资源,并自动刷新。

核心要点

  1. 没有类:C 的 I/O 是基于过程的,而不是面向对象的。
  2. *核心是 `FILEFILE*` 指针就是“流”本身。
  3. fwrite 是基石:它是处理二进制数据写入最通用的函数。
  4. 缓冲是关键:理解缓冲和刷新对于控制 I/O 行为和性能至关重要。
  5. 灵活性高:通过 freopen 等函数可以轻松地重定向流的输入/输出目标。
-- 展开阅读全文 --
头像
dede读取档案基本信息出错原因何在?
« 上一篇 01-03
dede后台权重排序如何修改?
下一篇 » 01-03

相关文章

取消
微信二维码
支付宝二维码

目录[+]