I/O流概述

仓颉编程语言将与应用程序外部载体交互的操作称为I/O操作,其中I对应输入(Input),O对应输出(Output)。仓颉所有的I/O机制都是基于数据流进行输入输出,这些数据流表示了字节数据的序列。

数据流是一串连续的数据集合,它就像承载数据的管道:在管道的一端输入数据,在管道的另一端就可以输出数据。仓颉将输入输出抽象为流(Stream)的概念:

  • 输入流(InputStream):将数据从外存中读取到内存中的流。输入端可以一段一段地向管道中写入数据,这些数据段会按先后顺序形成一个长的数据流。

  • 输出流(OutputStream):将数据从内存写入外存中的流。输出端也可以一段一段地从管道中读出数据,每次可以读取其中的任意长度的数据(不需要跟输入端匹配),但只能按顺序读取先输入的数据。

仓颉使用Stream接口统一描述标准输入输出、文件操作、网络数据流、字符串流、加密流、压缩流等各种形式的I/O操作。Stream主要面向处理原始二进制数据,最小的数据单元是Byte。

数据流分类

按照数据流职责上的差异,仓颉将Stream分为两类:

节点流

节点流是直接提供数据源的流,其构造方式通常依赖于某种直接的外部资源(如文件、网络等)。常见的节点流包括:

  1. 标准流(StdIn/StdOut/StdErr):程序与外部数据交互的标准接口
  2. 文件流(File):对文件系统进行读写操作
  3. 网络流(Socket):网络通信的数据通道

处理流

处理流不能直接提供数据源,而是代理其他数据流进行处理,其构造方式依赖于其他流。常见的处理流包括:

  1. 缓冲流(BufferedInputStream/BufferedOutputStream):提供缓冲功能提高I/O性能
  2. 字符串流(StringReader/StringWriter):提供字符串处理能力
  3. 链式流(ChainedInputStream):顺序从多个输入流读取数据

节点流详解

标准流

标准流包含标准输入流(stdin)、标准输出流(stdout)和标准错误输出流(stderr),是程序与外部数据交互的标准接口:

  • 程序运行时从输入流读取数据作为输入
  • 程序输出信息被传送到输出流
  • 错误信息被传送到错误流

在仓颉中可以使用Console类型访问标准流:

 
import std.io.* main() { // 从标准输入读取 let reader = Console.stdin let input = reader.readLine() // 向标准输出写入 let writer = Console.stdout writer.writeLine("Hello World") // 向标准错误写入 let errWriter = Console.stderr errWriter.writeLine("Error message") } 

文件流

仓颉通过fs包支持通用文件系统操作,屏蔽不同操作系统的差异,提供统一的功能接口:

 
import std.fs.* main() { // 创建文件/目录 File.create("test.txt") Directory.create("tempDir") // 检查存在性 if (exists("test.txt")) { println("文件存在") } // 读写文件 let file = File.open("test.txt") file.write("Hello".toArray()) let content = file.readAll() // 删除文件/目录 remove("test.txt") remove("tempDir", recursive: true) } 

文件流File类型同时提供常规文件操作和文件流两类功能:

  1. 常规文件操作:通过静态函数完成快捷操作
  2. 文件流功能:通过流接口进行细粒度控制

处理流详解

缓冲流

由于磁盘I/O相比内存I/O慢很多,对于高频次小数据量的读写,不带缓冲的数据流效率很低。缓冲流通过在内存中建立缓冲区,减少磁盘操作次数:

 
import std.io.* main() { let byteBuffer = ByteBuffer() byteBuffer.write("01234".toArray()) // 创建缓冲输出流 let bufferedOut = BufferedOutputStream(byteBuffer) bufferedOut.write("56789".toArray()) bufferedOut.flush() // 真正写入 println(String.fromUtf8(readToEnd(byteBuffer))) // 输出0123456789 } 

BufferedInputStream和BufferedOutputStream通过内部缓冲区显著提升I/O性能:

  • 写入时先放入内存缓冲区
  • 读取时先从缓冲区获取
  • 缓冲区满或主动flush时执行实际I/O

字符串流

仓颉的输入输出流基于字节数据抽象,在字符串处理场景不太友好。StringReader和StringWriter提供了更友好的字符串操作能力:

 
import std.io.* main() { // 字符串读取 let byteBuffer = ByteBuffer() byteBuffer.write("012\n346789".toArray()) let stringReader = StringReader(byteBuffer) let line = stringReader.readLine() // 读取一行 println(line ?? "error") // 输出012 // 字符串写入 let writer = StringWriter(byteBuffer) writer.write("Hello") writer.writeLine("Cangjie") } 

字符串流的优势:

  1. 自动处理编码转换
  2. 提供按行读取等便捷方法
  3. 性能优于手动字节转换

I/O编程实践

文件复制示例

结合节点流和处理流实现文件复制:

 
import std.fs.* import std.io.* func copyFile(src: String, dest: String): Unit { let input = File.open(src) let output = File.create(dest) // 使用缓冲流提高性能 let bufferedIn = BufferedInputStream(input) let bufferedOut = BufferedOutputStream(output) let buf = Array<Byte>(4096, repeat: 0) while (bufferedIn.read(buf) > 0) { bufferedOut.write(buf) } bufferedOut.flush() input.close() output.close() } 

标准流重定向

将标准输出重定向到文件:

 
import std.fs.* import std.io.* main() { let file = File.create("output.txt") let oldOut = Console.stdout // 重定向标准输出 Console.setOut(file.asOutputStream()) println("这将写入文件") // 恢复标准输出 Console.setOut(oldOut) println("这将打印到控制台") } 

总结

仓颉编程语言的I/O系统通过流抽象提供了统一而强大的输入输出能力,主要特点包括:

  1. 流式处理:基于管道模型,支持分段读写
  2. 分层设计:节点流对接数据源,处理流增强功能
  3. 缓冲优化:减少实际I/O操作次数
  4. 统一接口:文件、网络、字符串等统一抽象
  5. 类型安全:严格的类型检查和错误处理

掌握仓颉的I/O系统需要理解流的概念和分类,熟悉常用节点流和处理流的特性及组合方式,才能在实际开发中构建高效可靠的I/O密集型应用。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐