<span id="OSC_h2_1"></span>
1 概述
1.1 案例介绍
仓颉编程语言作为一款面向全场景应用开发的现代编程语言,通过现代语言特性的集成、全方位的编译优化和运行时实现、以及开箱即用的 IDE工具链支持,为开发者打造友好开发体验和卓越程序性能。
案例结合代码体验,让大家更直观的了解仓颉语言中的IO操作。
1.2 适用对象
- 个人开发者
- 高校学生
1.3 案例时间
本案例总时长预计40分钟。
1.4 案例流程
说明:
- 进入华为开发者空间,登录云主机;
- 使用CodeArts IDE for Cangjie编程和运行仓颉代码。
1.5 资源总览
资源名称 | 规格 | 单价(元) | 时长(分钟) | |
---|---|---|---|---|
开发者空间 – 云主机 | 鲲鹏通用计算增强型 kc2 | 4vCPUs | 8G | Ubuntu | 免费 | 免费 | 40 |
仓颉之I/O操作的交互奥秘 👈👈👈体验完整版案例,点击这里。
2 运行测试环境准备
2.1 开发者空间配置
面向广大开发者群体,华为开发者空间提供一个随时访问的“开发桌面云主机”、丰富的“预配置工具集合”和灵活使用的“场景化资源池”,开发者开箱即用,快速体验华为根技术和资源。
领取云主机后可以直接进入华为开发者空间工作台界面,点击打开云主机 > 进入桌面连接云主机。没有领取在开发者空间根据指引领取配置云主机即可,云主机配置参考1.5资源总览。
2.2 创建仓颉程序
点击桌面CodeArts IDE for Cangjie,打开编辑器,点击新建工程,保持默认配置,点击创建。
产物类型说明:
- executable,可执行文件;
- static,静态库,是一组预先编译好的目标文件的集合;
- dynamic,动态库,是一种在程序运行时才被加载到内存中的库文件,多个程序共享一个动态库副本,而不是像静态库那样每个程序都包含一份完整的副本。
2.3 运行仓颉工程
创建完成后,打开src/main.cj,参考下面代码简单修改后,点击编辑器右上角运行按钮直接运行,终端窗口可以看到打印内容。
<strong>package</strong> demo
<span style="color:#697070">// 第一个仓颉程序</span>
main(): Int64 {
<span style="color:#397300">println</span>(<span style="color:#880000">"hello world"</span>)
<span style="color:#397300">println</span>(<span style="color:#880000">"你好,仓颉!"</span>)
<strong>return</strong> <span style="color:#880000">0</span>
}
(* 注意:后续替换main.cj文件代码时,package demo保留)
(* 仓颉注释语法:// 符号之后写单行注释,也可以在一对 /* 和 */ 符号之间写多行注释)
到这里,我们第一个仓颉程序就运行成功啦!后面案例中的示例代码都可以放到main.cj文件中进行执行,接下来我们继续探索仓颉语言。
3 仓颉I/O操作
3.1 I/O流概述
仓颉编程语言将与应用程序外部载体交互的操作称为 I/O 操作。I 对应输入(Input),O 对应输出(Output)。
仓颉编程语言将输入输出抽象为流(Stream)。Stream 主要面向处理原始二进制数据,Stream 中最小的数据单元是 Byte。
3.1.1 输入流
程序从输入流读取数据源(数据源包括外界的键盘、文件、网络等),即输入流是将数据源读入到程序的通信通道。
仓颉编程语言用 InputStream 接口类型来表示输入流,它提供了 read 函数,这个函数会将可读的数据写入到 buffer 中,返回值表示了该次读取的字节总数。
InputStream接口定义:
interface <span style="color:#880000">InputStream</span> {
<strong>func</strong> <strong>read</strong>(<span>buffer</span>: <span style="color:#880000">Array</span><<span style="color:#880000">Byte</span>>): <span style="color:#880000">Int64</span>
}
输入流读取示例,具体代码操作如下:
Step1:复制以下代码,替换main.cj文件中的代码。(保留package)
<strong>import</strong> std.<span>io</span>.*
<strong>main</strong>() {
<span style="color:#697070">// 字符串转数组</span>
<strong>let</strong> array = <span style="color:#880000">"仓颉"</span>.<strong>toArray</strong>()
<strong>let</strong> byteArrayStream = <strong>ByteArrayStream</strong>()
<span style="color:#697070">// 写入数组</span>
byteArrayStream.<strong>write</strong>(array)
<strong>let</strong> <span>input</span>: <strong>InputStream</strong> = <strong>BufferedInputStream</strong>(byteArrayStream)
<strong>let</strong> buf = <strong>Array</strong><<strong>Byte</strong>>(<span style="color:#880000">256</span>, <span>item</span>: <span style="color:#880000">0</span>)
<span style="color:#697070">// 数据写入到buffer中</span>
<strong>while</strong> (input.<strong>read</strong>(buf) > <span style="color:#880000">0</span>) {
<strong>println</strong>(buf)
}
}
Step2:点击编辑器右上角运行按钮直接运行,终端窗口可以看到打印内容。
3.1.2 输出流
程序向输出流写入数据。输出流是将程序中的数据输出到外界(显示器、打印机、文件、网络等)的通信通道。
仓颉编程语言用 OutputStream 接口类型来表示输出流,它提供了 write 函数,这个函数会将 buffer 中的数据写入到绑定的流中。
特别的,有一些输出流的 write 不会立即写到外存中,而是有一定的缓冲策略,只有当符合条件或主动调用 flush 时才会真实写入,目的是提高性能。
OutputStream 接口定义:
interface <span style="color:#880000">OutputStream</span> {
<strong>func</strong> <strong>write</strong>(<span>buffer</span>: <span style="color:#880000">Array</span><<span style="color:#880000">Byte</span>>): <span style="color:#880000">Unit</span>
<strong>func</strong> <strong>flush</strong>(): <span style="color:#880000">Unit</span> {
<span style="color:#697070">// 空实现</span>
}
}
输出流读取示例,具体代码操作如下:
Step1:复制以下代码,替换main.cj文件中的代码。(保留package)
<strong>import</strong> std.<span>io</span>.*
<strong>main</strong>(): <strong>Unit</strong> {
<strong>let</strong> array = <span style="color:#880000">"仓颉"</span>.<strong>toArray</strong>()
<strong>let</strong> byteArrayStream = <strong>ByteArrayStream</strong>()
<span style="color:#697070">//向流中写入数据,此时数据在外部流的缓冲区中</span>
byteArrayStream.<strong>write</strong>(array)
<strong>let</strong> bufferedOutputStream = <strong>BufferedOutputStream</strong>(byteArrayStream)
<span style="color:#697070">// 调用 flush 函数,真正将数据写入内部流中</span>
bufferedOutputStream.<strong>flush</strong>()
<strong>println</strong>(<strong>String</strong>.<strong>fromUtf8</strong>(byteArrayStream.<strong>readToEnd</strong>()))
}
Step2:点击编辑器右上角运行按钮直接运行,终端窗口可以看到打印内容。
3.1.3 数据流分类
按照数据流职责上的差异,可以将 Stream 简单分成两类:
节点流:直接提供数据源,构造方式通常是依赖某种直接的外部资源(即文件、网络等)。
处理流:只能代理其它数据流进行处理,处理流的构造方式通常是依赖其它的流。
3.2 I/O节点流
什么是节点流?
节点流是指直接提供数据源的流,它的构造方式通常是依赖某种直接的外部资源(即文件、网络等)。节点流包括:标准流(StdIn、StdOut、StdErr)、文件流(File)、网络流(Socket)等。
3.2.1 标准流
标准流包含了标准输入流(stdin)、标准输出流(stdout)和标准错误输出流(stderr)。
在仓颉编程语言中可以使用 Console 类型来访问标准流。
使用 Console 类型需要导入 console 包:
<strong>import</strong> std.console.*
标准输入流读取示例,具体代码操作如下:
Step1:复制以下代码,替换main.cj文件中的代码。(保留package)
<strong>import</strong> std.<span>console</span>.*
<strong>main</strong>() {
<strong>let</strong> txt = <strong>Console</strong>.<span>stdIn</span>.<strong>readln</strong>()
<strong>println</strong>(txt ?? <span style="color:#880000">""</span>)
}
Step2:点击编辑器右上角运行按钮运行代码,在命令行上输入内容“1234”,然后换行结束,即可看到输入的内容。
标准输出流写入示例,具体代码操作如下:
Step1:复制以下代码,替换main.cj文件中的代码。(保留package)
<strong>import</strong> std.<span>console</span>.*
<strong>main</strong>() {
<strong>for</strong> (i <strong>in</strong> <span style="color:#880000">0.</span>
<span style="color:#880000">.5</span>) {
<span style="color:#697070">// 写入数据</span>
<strong>Console</strong>.<span>stdOut</span>.<strong>writeln</strong>(<span style="color:#880000">"hello, world!"</span>)
}
<span style="color:#697070">//stdOut调用flush才能保证内容被写到标准流中</span>
<strong>Console</strong>.<span>stdOut</span>.<strong>flush</strong>()
}
Step2:点击编辑器右上角运行按钮直接运行,终端窗口可以看到打印内容。
3.2.1 文件流
仓颉编程语言提供了 fs 包来支持通用文件系统任务。常规操作任务包括:创建文件/目录、读写文件、重命名或移动文件/目录、删除文件/目录、复制文件/目录、获取文件/目录元数据、检查文件/目录是否存在。
使用文件系统相关的功能需要导入 fs 包:
<strong>import</strong> std.fs.*
本节会着重介绍File相关的使用,File 类型在仓颉编程语言中同时提供了常规文件操作和文件流两类功能。
常规文件操作:
如果要检查某个路径对应的文件是否存在,可以使用 exists 函数。
具体代码操作如下:
Step1:复制以下代码,替换main.cj文件中的代码。(保留package)
<strong>import</strong> std.<span>fs</span>.*
<strong>main</strong>() {
<span style="color:#697070">// 文件是否在项目根目录下</span>
<strong>let</strong> exist = <strong>File</strong>.<strong>exists</strong>(<span style="color:#880000">"./tempFile.txt"</span>)
<strong>println</strong>(<span style="color:#880000">"exist: ${exist}"</span>)
}
Step2:点击编辑器右上角运行按钮直接运行,终端窗口可以看到打印内容。
拷贝文件,使用copy函数,具体代码操作如下:
Step1:在项目根目录下新建文本文件:tempFile.txt。
Step2:复制以下代码,替换main.cj文件中的代码。(保留package)
<strong>import</strong> std.<span>fs</span>.*
<strong>main</strong>() {
<strong>File</strong>.<strong>copy</strong>(<span style="color:#880000">"./tempFile.txt"</span>, <span style="color:#880000">"./tempFile2.txt"</span>, <span style="color:#669955">false</span>)
}
Step3:点击编辑器右上角运行按钮直接运行,资源管理器->项目根目录下复制了新的文件。
移动文件、删除文件,使用move函数和delete函数。
具体代码操作如下:
Step1:复制以下代码,替换main.cj文件中的代码。(保留package)
<strong>import</strong> std.<span>fs</span>.*
<strong>main</strong>() {
<span style="color:#697070">// 移动文件:tempFile2->tempFile3</span>
<strong>File</strong>.<strong>move</strong>(<span style="color:#880000">"./tempFile2.txt"</span>, <span style="color:#880000">"./tempFile3.txt"</span>, <span style="color:#669955">false</span>)
<span style="color:#697070">// 删除文件</span>
<strong>File</strong>.<strong>delete</strong>(<span style="color:#880000">"./tempFile.txt"</span>)
}
Step2:点击编辑器右上角运行按钮直接运行,可以看到项目根目录下只剩下tempFile3文件。
如果需要直接将文件的所有数据读出来,或者一次性将数据写入文件里,可以使用 File 提供的 readFrom、writeTo 函数直接读写文件。
readFrom、writeTo 函数使用,具体代码操作如下:
Step1:在项目根目录下新建文本文件:mineFile.txt,文本内容:study cangjie。
Step2:复制以下代码,替换main.cj文件中的代码。(保留package)
<strong>import</strong> std.<span>fs</span>.*
<strong>main</strong>() {
<span style="color:#697070">// 一次性读取了所有的数据</span>
<strong>let</strong> bytes = <strong>File</strong>.<strong>readFrom</strong>(<span style="color:#880000">"./mineFile.txt"</span>)
<span style="color:#697070">// 把数据一次性写入另一个文件中</span>
<strong>File</strong>.<strong>writeTo</strong>(<span style="color:#880000">"./otherFile.txt"</span>, bytes)
}
Step3:点击编辑器右上角运行按钮直接运行,资源管理器->项目根目录下,源文件数据写入了另外一个文件中。
文件流操作:
除了上述的常规文件操作之外,File 类型也被设计为一种数据流类型,因此 File 类型本身实现了 IOStream 接口。当创建了一个 File 的实例,可以把这个实例当成数据流来使用。
通过静态函数 openRead/create 直接打开文件或创建新文件的实例。
具体代码操作如下:
Step1:复制以下代码,替换main.cj文件中的代码。(保留package)
internal <strong>import</strong> std.<span>fs</span>.*
<strong>main</strong>() {
<strong>let</strong> file = <strong>File</strong>.<strong>create</strong>(<span style="color:#880000">"./tempFile.txt"</span>)
file.<strong>write</strong>(<span style="color:#880000">"hello, world!"</span>.<strong>toArray</strong>())
<span style="color:#697070">// 打开</span>
<strong>let</strong> file2 = <strong>File</strong>.<strong>openRead</strong>(<span style="color:#880000">"./tempFile.txt"</span>)
<span style="color:#697070">// 读取所有数据</span>
<strong>let</strong> bytes = file2.<strong>readToEnd</strong>()
<strong>println</strong>(bytes)
}
Step2:点击编辑器右上角运行按钮直接运行,终端窗口可以看到打印内容。
3.3 I/O处理流
处理流是指代理其它数据流进行处理的流。仓颉编程语言中常见的处理流包含 BufferedInputStream、BufferedOutputStream、StringReader、StringWriter、ChainedInputStream 等。
使用处理流时,需要导入io包:
<strong>import</strong> std.io.*
本节主要介绍缓冲流和字符串流。
3.3.1 缓冲流
由于磁盘的 I/O 操作相比内存的 I/O 操作要慢很多,所以要使用缓冲流,等凑够了缓冲区大小的时候再一次性操作磁盘,减少磁盘操作次数,从而提升性能表现。
仓颉编程语言标准库提供了 BufferedInputStream 和 BufferedOutputStream 这两个类型用来提供缓冲功能。
BufferedInputStream 使用示例,具体代码操作如下:
Step1:复制以下代码,替换main.cj文件中的代码。(保留package)
<strong>import</strong> std.<span>io</span>.*
<strong>main</strong>(): <strong>Unit</strong> {
<strong>let</strong> arr1 = <span style="color:#880000">"0123456789"</span>.<strong>toArray</strong>()
<strong>let</strong> byteArrayStream = <strong>ByteArrayStream</strong>()
<span style="color:#697070">// 写入数据</span>
byteArrayStream.<strong>write</strong>(arr1)
<span style="color:#697070">// BufferedInputStream </span>
<strong>let</strong> bufferedInputStream = <strong>BufferedInputStream</strong>(byteArrayStream)
<strong>let</strong> arr2 = <strong>Array</strong><<strong>Byte</strong>>(<span style="color:#880000">20</span>, <span>item</span>: <span style="color:#880000">0</span>)
<span style="color:#697070">// 读取流中数据,返回读取到的数据的长度</span>
<strong>let</strong> readLen = bufferedInputStream.<strong>read</strong>(arr2)
<strong>println</strong>(<strong>String</strong>.<strong>fromUtf8</strong>(arr2[..<span>readLen</span>]))
}
Step2:点击编辑器右上角运行按钮直接运行,终端窗口可以看到打印内容。
BufferedOutputStream 使用示例,具体代码操作如下:
Step1:复制以下代码,替换main.cj文件中的代码。(保留package)
<strong>import</strong> std.<span>io</span>.*
<strong>main</strong>(): <strong>Unit</strong> {
<strong>let</strong> arr1 = <span style="color:#880000">"1-2-3-4"</span>.<strong>toArray</strong>()
<strong>let</strong> byteArrayStream = <strong>ByteArrayStream</strong>()
byteArrayStream.<strong>write</strong>(arr1)
<span style="color:#697070">// BufferedOutputStream</span>
<strong>let</strong> bufferedOutputStream = <strong>BufferedOutputStream</strong>(byteArrayStream)
<strong>let</strong> arr2 = <span style="color:#880000">"-5-6-7-8-9"</span>.<strong>toArray</strong>()
<span style="color:#697070">// 向流中写入数据,此时数据在外部流的缓冲区中</span>
bufferedOutputStream.<strong>write</strong>(arr2)
<span style="color:#697070">// 调用 flush 函数,真正将数据写入内部流中 </span>
bufferedOutputStream.<strong>flush</strong>()
<strong>println</strong>(<strong>String</strong>.<strong>fromUtf8</strong>(byteArrayStream.<strong>readToEnd</strong>()))
}
Step2:点击编辑器右上角运行按钮直接运行,终端窗口可以看到打印内容。
3.3.2 字符串流
由于仓颉编程语言的输入流和输出流是基于字节数据来抽象的(拥有更好的性能),在部分以字符串为主的场景中使用起来不太友好,为了提供友好的字符串操作能力,仓颉编程语言提供了 StringReader 和 StringWriter 来添加字符串处理能力。
StringReader提供了字符串读取的能力,StringReader使用示例,具体代码操作如下:
Step1:复制以下代码,替换main.cj文件中的代码。(保留package)
<strong>import</strong> std.io.*
main(): Unit {
<span style="color:#880000">let</span> <span style="color:#ab5656">arr1</span> <span style="color:#ab5656">=</span> <span style="color:#880000">"我在学习仓颉"</span>.toArray()
<span style="color:#880000">let</span> <span style="color:#ab5656">byteArrayStream</span> <span style="color:#ab5656">=</span> ByteArrayStream()
byteArrayStream.write(arr1)
<span style="color:#697070">// StringReader</span>
<span style="color:#880000">let</span> <span style="color:#ab5656">stringReader</span> <span style="color:#ab5656">=</span> StringReader(byteArrayStream)
<span style="color:#697070">// 读取数据</span>
<span style="color:#880000">let</span> <span style="color:#ab5656">read</span> <span style="color:#ab5656">=</span> stringReader.readToEnd()
println(read)
}
Step2:点击编辑器右上角运行按钮直接运行,终端窗口可以看到打印内容。
StringWriter提供了写字符串的能力,StringWriter使用示例,具体代码操作如下:
Step1:复制以下代码,替换main.cj文件中的代码。(保留package)
<strong>import</strong> std.<span>io</span>.*
<strong>main</strong>(): <strong>Unit</strong> {
<strong>let</strong> byteArrayStream = <strong>ByteArrayStream</strong>()
<strong>let</strong> stringWriter = <strong>StringWriter</strong>(byteArrayStream)
<span style="color:#697070">// 写入字符串</span>
stringWriter.<strong>write</strong>(<span style="color:#880000">"我们"</span>)
<span style="color:#697070">// 写入字符串并自动转行</span>
stringWriter.<strong>writeln</strong>(<span style="color:#880000">"正在"</span>)
<span style="color:#697070">// 写入字符串</span>
stringWriter.<strong>write</strong>(<span style="color:#880000">"学习仓颉"</span>)
stringWriter.<strong>flush</strong>()
<strong>println</strong>(<strong>String</strong>.<strong>fromUtf8</strong>(byteArrayStream.<strong>readToEnd</strong>()))
}
Step2:点击编辑器右上角运行按钮直接运行,终端窗口可以看到打印内容。
至此,仓颉之IO操作的交互奥秘案例内容已全部完成。
如果想了解更多仓颉编程语言知识可以访问:https://cangjie-lang.cn/
</div>