第九章 I/O操作与异常处理 示例代码

浙江传媒学院 · 仓颉面向对象程序设计 · 基于 CangStream 框架构建

示例程序
22 个
程序示例
5 个
代码合计
27 个

一、I/O流概述(9.1)

9.1.1 数据流(Stream);9.1.2 数据流分类;9.1.3 数据流操作;9.1.4 数据流操作异常。本节为理论内容。

例9-1 I/O(输入输出)操作示例

9.1:使用 getStdIn()/getStdOut() 实现控制台输入输出交互。

package demo
import std.env.*
main() {
    getStdOut().write("请输入您的名字:")
    var namein = getStdIn().readln() // 输入:abc
    var nameget= namein.getOrThrow()
    getStdOut().write("您的名字为:")
    getStdOut().writeln(nameget)
    getStdOut().writeln("${nameget},您好,您的注册信息已经收到,欢迎您的光临!")
    return
}

二、Stream操作函数(9.2)

例9-2 使用流复制函数向目标区域复制数据

9.2.1:copy() 函数将输入流中的数据复制到输出流中,返回复制的字节数。

package demo
import std.io.ByteBuffer
import std.io.copy
main(): Unit {
    let sourceStream = ByteBuffer()
    let targetStream = ByteBuffer()
    let mystring="仓颉面向对象程序设计语言"
    /* 向源输入流写入数据 */
    let sourceData = mystring.toArray()
    sourceStream.write(sourceData)
    /* 使用 copy 函数将源输入流的数据拷贝到目标输出流 */
    let copiedBytes = copy(sourceStream, to: targetStream)
    println("向目标区域拷贝的数据大小是${copiedBytes} 字节.")
    /* 读取目标输出流中的数据 */
    let targetData: Array<Byte> = Array<Byte>(sourceData.size, repeat: 0)
    targetStream.read(targetData)
    println("目标区域收到的数据是: ${String.fromUtf8(targetData)}")
}

例9-3 使用 readString() 函数读取字符串及异常

9.2.2:readString() 读取输入流中所有剩余内容为字符串,非法 UTF-8 字节将抛出 ContentFormatException。

package demo
import std.io.ByteBuffer
import std.io.readString
import std.io.ContentFormatException
main(): Unit {
    let inputStream = ByteBuffer()
    let mystring="仓颉面向对象程序设计语言"
   /* 向输入流写入数据 */
    let sourceData = mystring.toArray()    
    inputStream.write(sourceData)
    /* 使用 readString 函数读取输入流中的所有剩余内容 */
    try {
        let result = readString(inputStream)
        println("读取字符串: ${result}")
    } catch (e: ContentFormatException) {
        println("Error: ${e.message}")
    }
    /* 向输入流写入一个不合法的 UTF-8 字符串 */
    let sourceDataError: Array<UInt8> = [0xC3, 0x28]
    inputStream.write(sourceDataError)
    /* 使用 readString 函数读取输入流中的所有剩余内容 */
    try {
        let result = readString(inputStream)
        println("读取字符串: ${result}")
    } catch (e: ContentFormatException) {
        println("错误: ${e.message}")
    }
}

例9-4 使用 readStringUnchecked() 函数读取字节

9.2.3:readStringUnchecked() 与 readString() 类似但不做合法性检查,unsafe 块中使用。

package demo
import std.io.ByteBuffer
import std.io.readStringUnchecked

main(): Unit {
    let inputStream = ByteBuffer()
    let mystring="Hello, World!"
    /* 向输入流写入数据 */
    let sourceData = mystring.toArray()    
    inputStream.write(sourceData)
    /* 使用 readStringUnchecked 函数读取输入流中的所有剩余内容 */
    unsafe {
        let result = readStringUnchecked(inputStream)
        println("读取字符: ${result}")    }

    /* 向输入流写入一个不合法的 UTF-8 字符串 */
    let sourceDataError: Array<UInt8> = [0xC3, 0x28, 0x48, 0x65, 0x6C, 0x6C, 0x6F]
    inputStream.write(sourceDataError)

    /* 使用 readStringUnchecked 函数读取输入流中的所有剩余内容 */
    unsafe {
        let result = readStringUnchecked(inputStream)
        println("读取字符: ${result}")
    }
}

例9-5 使用 readToEnd() 函数读取所有剩余字节内容

9.2.4:readToEnd() 读取输入流中所有剩余数据,返回一个字节数组。

package demo
import std.io.ByteBuffer
import std.io.readToEnd

main(): Unit {
    let inputStream = ByteBuffer()

    /* 向输入流写入数据 */
    let sourceData = "你好, 仓颉!".toArray()
    inputStream.write(sourceData)

    /* 使用 readToEnd 函数读取输入流中的所有剩余内容 */
    let data = readToEnd(inputStream)
    println("${String.fromUtf8(data)}")
}

三、I/O节点流(9.3)

例9-6 atExit 注册函数与 exit() 函数示例

9.3.1:atExit() 注册进程退出时执行的清理函数,exit() 退出当前进程。

package demo
import std.env.*
func saygoodbye() {
    println("程序执行完毕,再见")
}
main() {
    atExit(saygoodbye)  // 注册清理函数
    println("程序执行中.....")
    exit(1)  // 程序退出,cleanup() 将被执行
    return 0
}

例9-7 getCommand() 函数示例

9.3.1:getCommand() 获取当前进程的命令(可执行文件路径)。

package demo
import std.env.*
main() {
    try {
        let command = getCommand()
        println("Process command: ${command}")
    } catch (e: EnvException) {
        println("Error: ${e.message}")
    }
}

例9-8 getCommandLine() 函数示例

9.3.1:getCommandLine() 获取当前进程的命令行,包含命令及所有参数。

package demo
import std.env.*
main() {
    try {
        let commandLine = getCommandLine()
        println("Command line: ${commandLine}")
    } catch (e: EnvException) {
        println("Error: ${e.message}")
    }
}

例9-9 getStdIn()/getStdOut() 使用示例

9.3.1:使用标准输入/输出流实现多次交互式控制台输入输出。

package demo
import std.env.*
main() {
    getStdOut().write("请输入信息1:")
    var c = getStdIn().readln().getOrThrow() // 输入:你好,请问今天星期几?
    getStdOut().writeln("输入的信息1为:" +c )
    getStdOut().write("请输入信息2:")
    c = getStdIn().readln().getOrThrow()// 输入:你好,请问今天几号?  
    getStdOut().writeln("输入的信息2为:" + c)
    return
}

例9-10 exists 函数使用示例

9.3.2:exists() 函数检查指定路径的文件是否存在。

package demo
import std.fs.*
main() {
    let exist = exists("./src/character.txt")
    println("exist: ${exist}")
}


// 注意:文件系统如下:
// src
// `-- main.cj
// `-- character.txt
// `--readstrok.cj
// target
// `-- release
//     |-- .build-logs
//     |    |-- demo.errlog
//     |    `-- demo.outlog
//     |-- bin
//     |    |-- demo.errlog
//     |    |--main.exe
//     |-- demo

例9-11 移动、复制、删除文件示例

9.3.2:使用 move()、copy()、delete() 等函数进行文件操作。

package demo
import std.fs.*
main() {
    let srcPath = "./src/characters.txt"
    let destPath = "./src/character.txt"
    try {
        // 检查源文件是否存在
        if (exists(srcPath)) {
            println("文件已经存在,准备删除...")
            remove(srcPath)
        }
        // 复制文件
        if (exists(destPath)) {
            copy(destPath, to: srcPath, overwrite: true)
            println("文件复制完成")
        }
    } catch (e: Exception) {
        println("操作失败: ${e.message}")
    }
}

例9-12 读取与写入文件示例

9.3.2:File.readFrom() 一次性读取文件所有数据,File.writeTo() 写入数据。

package demo
import std.fs.*
main() {
    // 一次性读取文件所有数据
    let bytes = File.readFrom("./src/characters.txt")    
    // 将数据写入到另一个文件
    File.writeTo("./src/charactor.txt", bytes)
}

例9-13 文件创建与写入数据

9.3.2:File.create() 创建文件实例,file.write() 将数据写入文件。

package demo
internal import std.fs.*
internal import std.io.*

main() {
    // 创建一个文件实例
    let file = File.create("./src/啊.txt")
    let an="82,-64,0,-13,-12,-7,-12,-9,-3,-7,1,-6,4,-7,7,-10,7,-11,6,-64,0,-13,-12,-13,13,-13,13,-64,0,-4,-12,13,-12,13,-12,-64,0,10,-12,10,-12,-64,0,10,-12,10,11,9,12,3,12,3,12,-64,0,-2,-6,-2,7,-2,7,-64,0,-2,-5,-2,-5,5,-5,5,4,5,4,-64,0,-1,4,4,4,4,4,-64,-64,"
    file.write(an.toArray())
}

四、I/O处理流(9.4)

例9-14 缓冲流使用示例

9.4.1:BufferedInputStream 和 BufferedOutputStream 缓冲流的使用,flush() 刷新缓冲区。

package demo
import std.io.*
main(): Unit {
    // 模拟数据源
    let arr = "hello buffer".toArray()
    let byteBuffer = ByteBuffer()
    byteBuffer.write(arr)
    // 使用带缓冲的输入流
    let bis = BufferedInputStream(byteBuffer)
    let buf = Array<Byte>(20, repeat: 0)
    let n = bis.read(buf)
    println(String.fromUtf8(buf[..n])) // hello buffer
    // 使用带缓冲的输出流
    let outBuffer = ByteBuffer()
    let bos = BufferedOutputStream(outBuffer)
    bos.write("world".toArray())
    bos.flush()   // 必须 flush 才会写入
    println(String.fromUtf8(readToEnd(outBuffer))) // world
}

例9-15 字符串流使用示例

9.4.2:StringReader 按行读取字符串,StringWriter 写字符串,比字节操作更便捷。

package demo
import std.io.*
main(): Unit {
    // StringReader 按行读取
    let inputBuf = ByteBuffer()
    inputBuf.write("line1\nline2\nline3".toArray())
    let reader = StringReader(inputBuf)
    println(reader.readln() ?? "error") // line1
    println(reader.readln() ?? "error") // line2
    // StringWriter 写字符串
    let outputBuf = ByteBuffer()
    let writer = StringWriter(outputBuf)
    writer.write("hello")
    writer.writeln(" world")
    writer.write(123)
    writer.flush()
    println(String.fromUtf8(readToEnd(outputBuf))) 
    // hello world\n123
}

五、异常及异常处理(9.5)

例9-16 自定义异常类与 try-catch 表达式

9.5.1-9.5.3:自定义 FatherException/ChildException 异常类,throw 抛出异常,try-catch 捕获处理。

package demo
import std.io.*
import std.fs.*
open class FatherException <: Exception {
    public init() {
        super("This is FatherException.")
    }
    public init(message: String) {
        super(message)
    }
    public open override func getClassName(): String {
        "FatherException"
    }
}
class ChildException <: FatherException {
    public init() {
        super("This is ChildException.")
    }
    public override func getClassName(): String {
        "ChildException"
    }
}
// 以下是第一个例子
main() {
     try {
        throw NegativeArraySizeException("I am an Exception!")
    } catch (e: NegativeArraySizeException) {
        println(e)
        println("NegativeArraySizeException is caught!")
    }
    println("This will also be printed!")
}
// 以下是第二个例子
// main() {
// try {
// throw NegativeArraySizeException("NegativeArraySizeException")
// } catch (e: NegativeArraySizeException) {
// println("Exception info: ${e}.")
// } finally {
// println("The finally block is executed.")
// }
// }

例9-17 try-with-resources 使用示例

9.5.4:Resource 接口实现自动资源管理,try 块结束后自动调用 close() 方法释放资源。

// 第一个例子
package demo
import std.io.*
class R <: Resource {
public func isClosed(): Bool {
true
}
public func close(): Unit {
print("R is closed")
}
}
main() {
try (r = R()) {
println("Get the resource")
}
}




// 第二个例子

package demo
class R <: Resource {
public func isClosed(): Bool {
return true
}
public func close(): Unit {
print("R is closed")
}
}
main() {
try (r = R()) {
println("Get the resource")
} catch (e: Exception) {
println("Exception happened when executing the try-with-resources expression")
} finally {
println("End of the try-with-resources expression")
}
}

例9-18 使用 try-with-resources 表达式程序示例

9.5.4:Worker 类实现 Resource 接口,演示工具借还场景中的自动资源管理与异常处理。

package demo
import std.io.*
class Worker <: Resource {
    var hasTools: Bool = false
    let name: String
    public init(name: String) {
        this.name = name
    }
    public func getTools() {
        println("${name} picks up tools from the warehouse.")
        hasTools = true
    }
    public func work() {
        if (hasTools) {
            println("${name} does some work with tools.")
        } else {
            println("${name} doesn't have tools, does nothing.")
        }
    }
    public func isClosed(): Bool {
        if (hasTools) {
            println("${name} hasn't returned the tool.")
            false
        } else {
            println("${name} has no tools")
            true
        }
    }
    public func close(): Unit {
        println("${name} returns the tools to the warehouse.")
        hasTools = false
    }
}
main() {
    try (r = Worker("Tom")) {
        r.getTools()
        r.work()
    }
    try (r = Worker("Bob")) {
        r.work()
    }
    try (r = Worker("Jack")) {
        r.getTools()
        throw Exception("Jack left, because of an emergency.")
    }
}




六、使用Option处理异常(9.6)

例9-19 处理 Some 和 None 的值

9.6.1:模式匹配处理 Option 类型,match-case 分别处理 Some 和 None。

package demo
import std.io.*
func getString(p: ?Int64): String {
    match (p) {
        case Some(x) => "${x}"
        case None => "none"
    }
}
main() {
    let a = Some(1)
    let b: ?Int64 = None
    let r1 = getString(a) // r1 = "1"
    let r2 = getString(b) // r2 = "none"
    println(r1)
    println(r2)
}

例9-20 Coalescing 操作符(??)

9.6.2:?? 操作符在值为 None 时提供默认值,简化 Option 类型的处理。

package demo
main() {
    let a = Some(1)
    let b:?Int64 = None
    let r1:Int64 = a??0
    let r2:Int64 = b??0
    println(r1)
    println(r2)
}

例9-21 问号操作符(?)

9.6.3:? 操作符用于安全访问 Option 内部成员,值为 None 时返回 None。

package demo
struct R {
    public var a: Int64
    public init(a: Int64) {
        this.a = a
    }
}
main() {  
let r = R(100)
let x = Some(r)
let y = Option<R>.None
let r1 = x?.a  // r1 = Option<Int64>.Some(100)
let r2 = y?.a  // r2 = Option<Int64>.None
println(r1) // Some(100)
println(r2) // None
}

例9-22 getOrThrow() 函数使用示例

9.6.4:getOrThrow() 从 Option 中取值,如果是 None 则抛出 NoneValueException 异常。

package demo
func getOrThrow(a: ?Int64) {
    match (a) {
        case Some(v) => v
        case None => throw NoneValueException() // 抛出异常
    }
}
main() {
    let a = Some(1)
    let b: ?Int64 = None
    let r1 = a.getOrThrow() // r1 = 1
    println(r1)
    try {
        let r2 = b.getOrThrow() // 抛出异常
    } catch (e: NoneValueException) {
        println("b is None")
    }
}

七、程序示例(9.7)

示例1 学生成绩文件的读取

读取 score.txt 文件中的学生成绩数据,计算平均分并显示。

package demo
import std.fs.*
import std.convert.*

main() {
    let path = "./src/score.txt"
    try {
        // 写入文件
        let data = "张三 85\n李四 92\n王五 76".toArray()
        File.writeTo(path, data)

        // 读取文件
        let bytes = File.readFrom(path)
        let content = String.fromUtf8(bytes)
        println("文件内容:\n${content}")

        // 计算平均分
        let lines = content.split("\n")
        var total = 0
        var count = 0
        for (line in lines) {
            if (line != "") {
                let parts = line.split(" ")
                total += Int64.parse(parts[1])
                count++
            }
        }
        println("平均分: ${total / count}")
    } catch (e: Exception) {
        println("读取文件失败: ${e.message}")
    }
}

示例2 日志文件复制

模拟日志备份,将 log.txt 的内容复制到 log_backup.txt,输出复制的字节数。

package demo
import std.io.copy
import std.fs.*

main() {
    let srcPath = "./src/log.txt"
    let destPath = "./src/log_backup.txt"

    // 写入日志
    let log = "系统启动成功...\n用户已登录...\n数据处理完成.".toArray()
    File.writeTo(srcPath, log)

    // 打开文件流
    let srcFile = File(srcPath, Read)
    let destFile = File(destPath, Write)

    // 使用 copy 复制数据
    let copied = copy(srcFile, to: destFile)
    println("已复制 ${copied} 字节数据到 log_backup.txt")

    // 验证读取
    let bytes = File.readFrom(destPath)
    println("备份内容:\n${String.fromUtf8(bytes)}")
}

示例3 异常处理与 try-catch-finally

''计算用户输入两数的商,除零异常用 catch 捕获,finally 确保输出"计算结束"。''

package demo
import std.env.*
import std.convert.*

main() {
    try {
        getStdOut().write("请输入第一个整数:")
        let a = Int64.parse(getStdIn().readln().getOrThrow())

        getStdOut().write("请输入第二个整数:")
        let b = Int64.parse(getStdIn().readln().getOrThrow())

        let result = a / b
        println("${a} ÷ ${b} = ${result}")
    } catch (e: ArithmeticException) {
        println("错误: 除数不能为零")
    } finally {
        println("计算结束")
    }
}

示例4 文件写入与自动文件资源管理

使用 try-with-resources 自动管理文件资源,写入 Hello, World! 到文件并自动关闭。

package demo
import std.fs.*

main() {
    try (file = File.create("./src/hello.txt")) {
        file.write("Hello, World!".toArray())
        println("写入完成")
    } catch (e: Exception) {
        println("操作失败: ${e.message}")
    } finally {
        println("程序结束,文件资源已自动关闭")
    }
}

示例5 使用 Option 类型的快速查找

findUser() 函数从用户名列表中查找用户,配合 getOrThrow() 强制取值并捕获异常。

package demo

func findUser(name: String): ?String {
    let users = ["张三", "李四", "王五"]
    for (u in users) {
        if (u == name) {
            return Some(u)
        }
    }
    return None
}

main() {
    try {
        let u1 = findUser("李四").getOrThrow()
        println("找到用户: ${u1}")

        let u2 = findUser("赵六").getOrThrow()
        println("找到用户: ${u2}")
    } catch (e: NoneValueException) {
        println("查找失败: 用户不存在")
    }
}