仓颉编程语言青少年基础教程:输入输出
仓颉编程语言青少年基础教程:输入输出
仓颉编程语言v1.0.0的输入输出由标准库的core 包(std.core)和env 包(std.env)提供。标准库的convert包(std.convert),提供了类型转换和格式化功能。
什么是“输入 / 输出”?
• 输入(Input):程序从键盘、鼠标、文件或网络读取数据。
• 输出(Output):程序把结果展示到屏幕、文件或网络。
env 包中print()、println()和readln()介绍
print()、println()和readln(),这些函数属于标准库的env 包
基本输入输出功能由标准库核心包std.core提供,该包无需显式导入,默认即可使用。其输入输出能力围绕 “控制台输出” 和 “控制台输入”。
【core 包是标准库的核心包,提供了适用仓颉语言编程最基本的一些 API 能力。
提供了内置类型(有符号整型、无符号整型、浮点型等)、常用函数(print、println、eprint 等)、常用接口(ToString、Hashable、Equatable、Collection 等)、常用类和结构体(Array、String、Range 等)、常用异常类(Error、Exception 以及它们的一些细分子类)。
core 包不需要显式导入,默认导入。写不写import std.core.* 都可以。显式写 import std.core.* 不会报错,但多余。
仓颉编程语言标准库(std)是安装仓颉 SDK 时默认自带的库。标准库预先定义了一组函数、类、结构体等,旨在提供常用的功能和工具,以便开发者能够更快速、更高效地编写程序。
标准库包含了若干包(package),而包是编译的最小单元。包可以定义子包,从而构成树形结构。
没有父包的包称为 root(根)包,root 包及其子包(包括子包的子包)构成的整棵树称为模块(module)。】
控制台输出print()和println()函数
仓颉编程语言的print()和println()输出是程序最基础的功能,其中println()会自动追加换行符。
• print:仅将数据的 “字符串表达” 输出到控制台,输出后光标停留在当前行末尾,后续输出会紧跟当前内容。
• println:在将数据 “字符串表达”输出到控制台后,自动追加一个换行符(\n),光标跳转至下一行开头。
例如:
main() { println("这是println输出,自动换行")print("这是print输出,不自动换行。")print("这也是print输出。")
}
编译运行截图:
控制台输入:readln () 函数
控制台输入功能由readln()函数单独实现,用于接收用户从控制台输入的文本内容,直到遇到换行或EOF结束。
示例:(读一行)
源码如下:
import std.convert.* //使用Parse()函数用到main() {// 示例1:获取用户输入并直接输出print("请输入你的名字:"); // 提示用户输入let name = readln(); // 接收用户输入的名字(字符串类型)println("你好,${name}!"); // 输出问候语,如输入“Bob”,则输出“你好,Bob!”// 示例2:获取输入并转换为其他类型(需结合类型转换API)print("请输入你的年龄:");let ageStr = readln(); // 先获取输入的字符串let age = Int32.parse(ageStr); // Parse()函数,将字符串转为 Int32println("你的年龄是:${age}"); // 输出转换后的年龄
}
编译运行截图:
插值字符串
在仓颉编程语言中,输出语句中的 ${} 用于在字符串中内嵌表达式并进行格式化输出。语法:
${expression}
示例源码:
main() {// 1.基本变量插值let name = "仓颉"let version = "1.0"println("欢迎使用 ${name} ${version}")println("-------")// 2.表达式插值let a = 7let b = 3println("a + b = ${a + b}")println("-------")// 3.函数调用结果 func max(a: Int64, b: Int64): Int64 { if (a > b) {a} else {b}}let m = max(5, 9)println("较大值:${m}")println("-------")// 4.多行字符串中的插值let lang = "仓颉"let lines = """${lang}语言版本 1.0"""println(lines)println("-------")//5.转义与嵌套大括号let left = "{"let right = "}"println("转义示例:${left}内容${right}")
}
运行输出:
欢迎使用 仓颉 1.0
-------
a + b = 10
-------
较大值:9
-------
仓颉语言
版本 1.0
-------
转义示例:{内容}
env 包的标准输入、标准输出介绍
标准库的env 包(std. env)提供当前进程的相关信息与功能、包括环境变量、命令行参数、标准流、退出程序。也提供标准输入、标准输出、标准错误进行交互的方法。
getStdIn().readln() 从标准输入中读取字符串。
getStdIn().read() 从标准输入中读取一个字符。
getStdOut().write() 从标准输出中输出字符串,不自动换行。
getStdOut().writeln() 从标准输出中输出字符串,并自动换行。
【错误流通常也支持类似write()/writeln()的方法(用于输出错误信息)。
getStdErr() 获取当前进程标准错误流。
getStdIn() 获取当前进程标准输入流。
getStdOut() 获取当前进程标准输出流。
补充说明
• 标准输入(stdin):键盘 → 程序
• 标准输出(stdout):程序 → 屏幕
• 标准错误(stderr):程序 → 屏幕(专门报错)
标准输入,接收外部输入数据的默认来源,通常是键盘(用户通过键盘输入内容)。
标准输出,输出正常结果的默认目的地,通常是控制台(终端窗口),用户可以直接看到输出内容。
标准错误,输出错误信息的专用数据流,通常也是控制台(和 标准输出 看起来一样),但逻辑上是独立的流,输出错误、警告、诊断信息。
本包提供多平台统一操控能力,目前支持 Linux 平台,macOS 平台,Windows 平台与 HarmonyOS 平台。】
下面给出示例。
字符串级读(读一行)
方式1:使用readln()
readln()能从标准输入中读取字符串,示例见前面。
【import 语句导入其它包中的声明或定义】
方式2:使用getStdIn().readln() ,功能类似std.core的readln()
需要使用 import —— import std.env.*
源码如下:
字符串级读(读一行)
方式1:使用readln()
readln()能从标准输入中读取字符串,示例见前面。
【import 语句导入其它包中的声明或定义】
方式2:使用getStdIn().readln() ,功能类似std.core的readln()
需要使用 import —— import std.env.*
源码如下:
import std.env.*main() {getStdOut().write("请输入:") // 可换 print("请输入:")var line = getStdIn().readln() // 读取字符串var r = line.getOrThrow() // 拆包getStdOut().writeln("你输入的是:${r}") // 可换 println("你输入的是:${line}")
}
说明:其中两行
var line = getStdIn().readln() // 读取字符串
var r = line.getOrThrow() // 拆包
可写为一行
var line = getStdIn().readln().getOrThrow() //读取字符串, 并拆包
编译运行截图:
字符级读
可以用getStdIn().read()读取一个字符。示例源码如下:
import std.env.*main() { print("请输入字符:") let c = getStdIn().read().getOrThrow() // 读取一个字符,并拆包println("你输入的字符是: ${c}")getStdOut().writeln("你输入的字符是: ${c}")
}
编译运行
请输入字符:中国
你输入的字符是: 中
你输入的字符是: 中
字节级别(二进制场景)读
示例源码如下
import std.env.*main() {let buf: Array<UInt8> = [0, 0, 0, 0] // 创建一个长度为4的UInt8数组(可容纳4个字节)print("请输入:") let n = getStdIn().read(buf) // 从标准输入读取字节,存入bufprintln("读到 ${n} 字节:${buf[..n]}") //输出字节值// 遍历打印读取到的字节值for (i in 0..n) {print("${buf[i]} ")}
}
编译运行
请输入:中华人民共和国
读到 4 字节:[228, 184, 173, 229]
228 184 173 229
说明:
1.缓冲区大小
buf 只有 4 个 UInt8,所以 getStdIn().read(buf) 最多读 4 个字节。
2.汉字 UTF-8 长度
“中”的 UTF-8 编码:E4 B8 AD → 3 字节
“华”的 UTF-8 编码:E5 8D 8E → 3 字节
3.字节只能装下 E4 B8 AD E5,于是你看到数组里恰好是
[0xE4, 0xB8, 0xAD, 0xE5] → 十进制 [228, 184, 173, 229]
4.后续字节仍在输入缓冲区
剩下的 8D 8E ... 还留在系统输入队列中;下次再读就能拿到。
convert包的常用功能介绍
标准库的convert包(std.convert)提供了强大的类型转换和格式化功能
• 类型转换:支持将字符串解析为特定类型,也支持将其他类型转换为字符串。如其中Parsable<T>接口提供常规转换parse和tryParse函数。
• 格式化输出:格式化format函数功能,可将数据类型转换为指定格式的字符串(如控制宽度、对齐方式、进制、小数精度等)。
类型转换
parse函数和tryParse函数的区别
• parse 转换失败 直接抛异常,程序会当场崩溃(IllegalArgumentException)。
• tryParse 转换失败 不抛异常,而是返回一个 Option(选项) 包裹的结果:
– 成功 → Some(值)
– 失败 → None
示例 1:使用parse函数常规转换,可以使用try-catch处理已知可能异常的情况:
import std.convert.*main() {// 1. Bool转换let boolVal = Bool.parse("true");println("Bool转换结果:${boolVal}"); // 输出:true// 2. Int32转换(带负号)let intVal = Int32.parse("-456");println("Int32转换结果:${intVal}"); // 输出:-456// 3. Float64转换(科学计数法)let floatVal = Float64.parse("3.14e2");println("Float64转换结果:${floatVal}"); // 输出:314.0// 以下代码会抛IllegalArgumentException(演示异常场景)// let invalidInt = Int32.parse("123a"); // 含非数字字符// let overflowInt = Int8.parse("128"); // 超出Int8范围(127)
}
示例 2:使用tryParse函数,需通过 “模式匹配” 或 “判空” 处理结果,适合不确定输入是否合法的场景:
import std.convert.*;main(){/* 例 1:Int32 失败 */let maybeInt = Int32.tryParse("abc")match (maybeInt) {case Some(v) => println("Int32 成功:${v}")case None => println("Int32 失败:不是整数")}/* 例 2:UInt8 成功 */let maybeUint = UInt8.tryParse("255")match (maybeUint) {case Some(u) => println("UInt8 成功:${u}")case None => println("UInt8 失败")}/* 例 3:Rune 失败(长度≠1)*/let maybeRune = Rune.tryParse("AB")if (maybeRune.isNone()) {println("Rune 失败:长度不为 1")}
}
示例3,输入一个数,判断它的奇偶性:
import std.convert.*
main() {print("请输入一个整数:")var str: String = readln()var n = Int64.parse(str)// 判断奇偶if (n % 2 == 0) {println("${n} 是偶数")} else {println("${n} 是奇数")}
}
或写为:
import std.env.*
import std.convert.*
main() {getStdOut().write("请输入一个整数:") // 可换 print("请输入一个整数:")var str: String = getStdIn().readln().getOrThrow() //读取字符串, 并拆包var n = Int64.parse(str)// 判断奇偶if (n % 2 == 0) {println("${n} 是偶数")} else {println("${n} 是奇数")}
}
format函数格式化功能
尽管 format函数 支持 “实例.点语法” 这种类似方法的调用方式,但核心依据官方接口的 static func 声明,以及 “语法糖不改变本质” 的原则,其规范名称仍是 “函数”(更具体地说,是 “静态函数”)。
基本调用格式
value.format(format_spec)
其中:
• value:要格式化的值(任何实现了Formattable接口的类型)
• format_spec:格式说明符字符串,遵循特定语法:[flags][width][.precision][specifier]
format_spec(格式说明符)部分的说明:
1. flags(标志位)
用于控制对齐方式、符号显示、进制前缀等,支持 4 种标志:
标志 | 适用类型 | 功能说明 |
- | Int、UInt、Rune、Float | 左对齐,空位补在右侧(默认右对齐)。 |
+ | Int、UInt、Float | 正数强制显示+号,负数仍显示-号。 |
# | Int、UInt(进制转换时) | 为进制格式添加前缀:二进制(0b/0B)、八进制(0o/0O)、十六进制(0x/0X)。 |
0 | Int、UInt、Float | 空位用0填充(而非默认空格)。 |
示例
import std.convert.*main() {var num: Int32 = 20;// 左对齐(宽度10)print("\"${num.format("-10")}\"\n"); // 输出:"20 "// 强制显示正号(宽度10,右对齐)print("\"${num.format("+10")}\"\n"); // 输出:" +20"// 十六进制加前缀(宽度10)print("\"${num.format("#10x")}\"\n"); // 输出:" 0x14"// 0填充(宽度10)print("\"${num.format("010")}\"\n"); // 输出:"0000000020"
}
2. width(宽度)
指定输出字符串的最小宽度(正整数),若实际内容长度小于宽度,则用空格(或0,若指定0标志)填充;如果实际值宽度大于指定宽度,不会截断:
• 无符号:默认右对齐,空位补在左侧。
• 带-:左对齐,空位补在右侧。
• 若内容长度超过宽度,不截断,按实际长度输出。
• 符号(+/-)和进制前缀(如0x)会占用宽度计数。
示例:
import std.convert.*main() {var num: Int32 = 20;// 宽度1(小于实际长度2,不截断)println("\"${num.format("1")}\""); // 输出:"20"// 宽度4,带正号(+占用1个字符位)println("\"${num.format("+4")}\""); // 输出:" +20"
}
3. precision(精度)
以.开头的正整数,对整数和浮点数的影响不同,对于浮点数:指定小数位数;对于整数:指定最小数字位数(不足时用前导零填充):
类型 | 功能说明 |
浮点数 | 控制小数点后有效数字的位数: |
整数 | 控制最小位数: |
示例:
import std.convert.*main() {var f: Float64 = -1234.123456;// 浮点数:精度4(保留4位小数)println("\"${f.format(".4")}\""); // 输出:"-1234.1235"// 浮点数:精度20(保留20位小数)println("\"${f.format(".20")}\""); // 输出:"-1234.12345600000003287278"
}
4. specifier(格式符)
指定数据的输出格式(如进制、科学计数法等),不同类型支持的格式符不同:
格式符 | 适用类型 | 功能说明 |
b/B | Int、UInt | 二进制格式输出(b小写,B大写,仅影响进制前缀,如0b/0B)。 |
o/O | Int、UInt | 八进制格式输出(o对应0o前缀,O对应0O前缀)。 |
x/X | Int、UInt | 十六进制格式输出(x对应0x前缀,X对应0x前缀,字母部分x小写、X大写)。 |
e/E | Float | 科学计数法输出(e对应小写e,E对应大写E)。 |
g/G | Float | 自动选择精简格式(十进制或科学计数法,优先更短的表示)。 |
示例:
import std.convert.*main() {// 整数进制转换var num: Int32 = 20;println("二进制: ${num.format("b")}"); // 输出:"10100"println("八进制: ${num.format("o")}"); // 输出:"24"println("十六进制: ${num.format("x")}"); // 输出:"14"println("带前缀十六进制: ${num.format("#X")}"); // 输出:"0X14"// 浮点数格式var f: Float32 = 1234.1;var largeF: Float32 = 123412341234.1;println("科学计数法: ${f.format("20.2e")}"); // 输出:" 1.23e+03"println("精简格式: ${largeF.format("20G")}"); // 输出:" 1.23412E+11"
}
最后,给出一个format函数综合示例:
import std.convert.*main() {// 整数格式化示例let num = 12345// 基本格式化println(num.format("")) // 默认格式: "12345"println(num.format("10")) // 宽度10,右对齐: " 12345"println(num.format("-10")) // 宽度10,左对齐: "12345 "println(num.format("010")) // 宽度10,零填充: "0000012345"println(num.format("+10")) // 显示正号: " +12345"// 进制格式化println(num.format("b")) // 二进制: "11000000111001"println(num.format("o")) // 八进制: "30071"println(num.format("x")) // 十六进制(小写x): "3039"println(num.format("X")) // 十六进制(大写X): "3039"println(num.format("#x")) // 带前缀十六进制: "0x3039"// 浮点数格式化示例let pi = 3.1415926535// 小数精度控制println(pi.format("")) // 默认: "3.141593"println(pi.format(".2")) // 2位小数: "3.14"println(pi.format(".5")) // 5位小数: "3.14159"// 科学计数法println(pi.format(".3e")) // 科学计数法,3位小数: "3.142e+00"println(pi.format(".3E")) // 科学计数法(大写),3位小数: "3.142E+00"// 通用格式println(pi.format(".3g")) // 通用格式,3位有效数字: "3.14"// 组合使用println(pi.format("-10.2e")) // 左对齐,宽度10,科学计数法: "3.14e+00 "
}