编程语言语言运行时标准库垃圾回收并发编程【免费下载链接】cangjie_runtime仓颉编程语言运行时与标准库。项目地址https://gitcode.com/Cangjie/cangjie_runtime点击查看免费下载本文围绕仓颉标准库std.convert包中的三个核心接口展开Formattable按格式参数将类型实例格式化为字符串、ParsableT将字符串解析为具体类型与RadixConvertibleT按指定进制解析/转换整数。读完本文你可以完整掌握各接口的 API 签名、格式化参数语法、每个扩展类型的行为差异与异常语义并能结合 formattable.cj、parsable.cj 等标准库源码理解其底层实现原理。一、三大接口总览std.convert是仓颉标准库中负责“类型 ↔ 字符串”双向转换的基础包。在 convert 包概述中定义了三个接口接口功能已实现的扩展类型Formattable根据格式化参数将类型实例转换为对应格式的字符串Float16/32/64、Int8/16/32/64、UInt8/16/32/64、RuneParsableT提供统一的parse/tryParse方法把字符串解析为具体类型Bool、Rune、Float16/32/64、Int8/16/32/64、UInt8/16/32/64RadixConvertibleT按指定进制2–36解析字符串并生成指定进制的字符串表示Int8/16/32/64、UInt8/16/32/64三个接口的源码分别位于Formattableformattable.cjParsableT与RadixConvertibleTparsable.cj共享的进制/溢出常量与字符表base.cj浮点解析与浮点格式化的 C 侧原生实现floatparse.c、string.c接口定义Formattable定义了唯一的实例方法用于把当前实例按fmt参数格式化public interface Formattable { func format(fmt: String): String }参数fmt: String—— 格式化参数。返回String—— 当前实例按参数格式化后的字符串。其他类型也可以自行实现该接口来获得格式化能力。ParsableT提供两组成对的静态方法parse在解析失败时抛出异常tryParse则把结果包装进OptionT失败时返回Nonepublic interface ParsableT { static func parse(value: String): T static func tryParse(value: String): OptionT }RadixConvertibleT则额外引入了“进制”维度既包含带radix的静态解析方法也包含按进制输出字符串的实例方法public interface RadixConvertibleT { static func parse(value: String, radix!: Int64): T static func tryParse(value: String, radix!: Int64): OptionT func toString(radix!: Int64): String }二、Formattable按格式化参数输出字符串fmt 参数语法format的fmt参数遵循如下文法详见 convert 包概述中的“功能介绍”format_spec : [flags][width][.precision][specifier] flags : - | | # | 0 width : integer precision : integer specifier : b | B | o | O | x | X | e | E | g | Gflags-适用于 Int、UInt、Rune 和 Float表示左对齐适用于 Int、UInt 和 Float正数打印号负数忽略#针对进制打印二进制补0b/0B、八进制补0o/0O、十六进制补0x/0X0适用于 Int、UInt 和 Float空位补 0。width宽度正整数适用于 Int、UInt、Rune 和 Float。宽度前有负号表示左对齐、无负号右对齐宽度小于数值本身长度时不截断。前缀/-占用 1 个字符位0x/0o等占用 2 个字符位。precision精度正整数适用于 Int、UInt 和 Float。对浮点数表示小数点后的有效数字位数缺省打印 6 位小数对整数类型指定值大于数值本身长度时在前面补0。specifier进制/记法b|B|o|O|x|X适用于 Int 和 UInte|E|g|G适用于 Floate/E为科学计数法g/G自动选择十进制或科学计数法的精简表示。更完整的参数行为演示见 convert 使用示例。各类型的 format 实现Formattable为以下类型提供了扩展Float16、Float32、Float64、Int16、Int32、Int64、Int8、Rune、UInt16、UInt32、UInt64、UInt8。每个扩展都实现形如public func format(fmt: String): String参数fmt: String—— 格式化参数返回String—— 格式化后的字符串异常当fmt非法时抛出 IllegalArgumentException。下面按类型给出文档中的完整示例与真实运行结果。Float16import std.convert.* main() { var f: Float16 -0.34 var res1 f.format(-20) // 左对齐宽度 20 var res2 f.format(20) // 右对齐宽度 20 var res3 f.format(g) // 通用格式 var res4 f.format(e) // 科学计数法 println(\${res1}\) println(\${res2}\) println(\${res3}\) println(\${res4}\) }运行结果-0.340088 -0.340088 -0.340088 -3.400879e-01注意输出-0.340088Float16位宽有限-0.34转换后本身就存在精度损失这一点与后文Parsable解析3.14159得到3.140625的现象一致。Float32import std.convert.* main() { var f: Float32 3.14159 var res1 f.format(-20) // 左对齐宽度 20 var res2 f.format(20) // 右对齐带符号宽度 20 var res3 f.format(10) // 右对齐宽度 10 var res4 f.format(e) // 科学计数法 println(\${res1}\) println(\${res2}\) println(\${res3}\) println(\${res4}\) }运行结果3.141590 3.141590 3.141590 3.141590e00Float64import std.convert.* main() { var f: Float64 3.141592653589793 var res1 f.format(-20) // 左对齐宽度 20 var res2 f.format(20) // 右对齐带符号宽度 20 var res3 f.format(15) // 右对齐宽度 15 var res4 f.format(g) // 通用格式 println(\${res1}\) println(\${res2}\) println(\${res3}\) println(\${res4}\) }运行结果3.141593 3.141593 3.141593 3.14159Int16 / Int32 / Int8行为一致以 Int16 为例import std.convert.* main() { var u: Int16 20 var res1 u.format(-10) // 左对齐宽度 10 var res2 u.format(10) // 右对齐带符号宽度 10 var res3 u.format(10) // 右对齐宽度 10 var res4 u.format(b) // 二进制格式 println(\${res1}\) println(\${res2}\) println(\${res3}\) println(\${res4}\) }运行结果Int8 20、Int32 20 的结果完全相同20 20 20 10100Int64含十六进制前缀#ximport std.convert.* main() { var i: Int64 -123456789 var res1 i.format(-20) // 左对齐宽度 20 var res2 i.format(20) // 右对齐带符号宽度 20 var res3 i.format(20) // 右对齐宽度 20 var res4 i.format(#x) // 带前缀的十六进制格式 println(\${res1}\) println(\${res2}\) println(\${res3}\) println(\${res4}\) }运行结果-123456789 -123456789 -123456789 -0x75bcd15Runeimport std.convert.* main() { var r: Rune A var res1 r.format(-10) // 左对齐宽度 10 var res2 r.format(10) // 右对齐宽度 10 println(\${res1}\) println(\${res2}\) }输出A AUInt16 / UInt32 / UInt8以 UInt16 123 为例import std.convert.* main() { var i: UInt16 123 var res1 i.format(-10) // 左对齐宽度 10 var res2 i.format(10) // 右对齐带符号宽度 10 var res3 i.format(10) // 右对齐宽度 10 var res4 i.format(b) // 二进制格式 println(\${res1}\) println(\${res2}\) println(\${res3}\) println(\${res4}\) }输出UInt32 200、UInt8 200 同理二进制均为11001000123 123 123 1111011UInt64最大值示例import std.convert.* main() { var u: UInt64 18446744073709551615 var res1 u.format(-20) // 左对齐宽度 20 var res2 u.format(20) // 右对齐宽度 20 var res3 u.format(#x) // 带前缀的十六进制格式 println(\${res1}\) println(\${res2}\) println(\${res3}\) }输出18446744073709551615 18446744073709551615 0xffffffffffffffff从示例可以看到当宽度小于数值本身长度时不会发生截断结果原样输出。源码剖析fmt 是如何被解析和应用的从源码结构看formattable.cj所有整型的format实现都收敛到Int64/UInt64的实现上例如Int32.format直接调用Int64(this).format(fmt)浮点同理收敛到Float64避免为每个位宽重复实现。以Int64的实现为例核心流程是转义控制字符 → 校验 fmt → 解析 FormatSpecifier → 执行格式化。控制字符转义escapeControlCharacters会把\t、\b、\r、\n、\f、\v、\0以及 0x01–0x07、0x0E–0x1F 等控制字符转成\\x..形式的转义序列防止不可见字符污染格式串。格式串校验Verify类依据如下正则语义校验见 formattable.cj#L336-L391整型^([-||#|0])?([1-9][0-9]*)?(\\.[0-9])?([b|B|o|O|x|X])?$浮点^([-||#|0])?([1-9][0-9]*)?(\\.[0-9])?([e|E|g|G])?$字符^-?([1-9][0-9]*)?$校验不通过则抛出IllegalArgumentException(Wrong format string ... for Int type.)等错误。参数解析parser/parseFormat把fmt拆成Flags-//#/0、width、precision、specifier四元组。其中有两个防资源耗尽的上限formattable.cj#L27-L28const MAX_FORMAT_WIDTH: Int64 100000 /* 最大格式宽度防止内存耗尽。 */ const MAX_FLOAT_PRECISION: Int64 350 /* 最大浮点精度覆盖 Float64 全量程。 */宽度超过 100000 会抛出IllegalArgumentException(Format width ... exceeds maximum allowed value 100000.)浮点精度超过 350 时同理拒绝。这是使用format时容易忽视的隐藏约束。格式化执行FormatSpecifier类formattable.cj#L591内fmtInteger负责整型按b/B/o/O/x/X做二进制、八进制、十六进制转换内部自实现位运算转换处理#前缀0b/0o/0x、正号、0补零、精度前补 0最后由writePadding按-标志决定左右填充空格fmtFloat负责浮点默认精度为 6对应概述中“不指定则打印六位小数”g/e/E/G通过 CFFI 调用原生函数 CJ_FORMAT_Float64Formatter 完成格式化分配失败时抛出IllegalMemoryExceptionfmtCharacter只处理宽度与对齐。三、Parsable parse 与 tryParse 双通道解析ParsableT的核心设计是“同一语义、两种失败策略”static func parse(value: String): T—— 解析成功返回T失败抛出IllegalArgumentExceptionstatic func tryParse(value: String): OptionT—— 解析成功返回Some(值)失败返回OptionT.None。标准库已为Bool、Rune、Float16/32/64、Int8/16/32/64、UInt8/16/32/64全部实现了该接口。源码中各扩展位于 parsable.cj接口定义见 parsable.cj#L26-L47。各类型行为与异常语义汇总类型parse 抛出 IllegalArgumentException 的条件tryParse 失败时Bool字符串为空或转换失败返回NoneFloat16/32/64字符串不符合浮点语法返回NoneInt8/16/32/64字符串为空、以开头、转换失败、值超出该类型范围、含非法 UTF-8返回NoneRune字符串为空、转换失败、含非法 UTF-8返回NoneUInt8/16/32/64字符串为空、以或-开头、转换失败、值超出该类型范围、含非法 UTF-8返回None一个容易踩坑的不对称点有符号 Int 类型不允许前缀Int8.parse(5)会抛异常而无符号 UInt 类型连也不接受只有RadixConvertible系列才显式支持前缀见第四节。这一点在源码 parsable.cj#L986-L1012 的getDataAndFlagCore中体现得很清楚前缀仅在prefixPlusAllowed为true时才被接受否则直接抛IllegalArgumentException(Starting with \\ is illegal in Int8.)。浮点类型Float16/32/64目前不支持二进制/八进制浮点转换这是文档中明确的限制。Bool 示例import std.convert.* main() { var strTrue: String true var strFalse: String false var res1 Bool.parse(strTrue) var res2 Bool.parse(strFalse) println(After the conversion of parse, \true\ became ${res1}) println(After the conversion of parse, \false\ became ${res2}) }输出After the conversion of parse, true became true After the conversion of parse, false became falsetryParse版本输出则为Some(true)/Some(false)。从源码看parsable.cj#L1385-L1395Bool.tryParse通过逐字节比较精确匹配true/false两个词不区分大小写以外的任何变体True会返回None。Float16 示例精度损失直观可见import std.convert.* main() { var strFloat: String 3.14159 var res Float16.parse(strFloat) println(After the conversion of parse, \3.14159\ became ${res}) }运行结果After the conversion of parse, 3.14159 became 3.140625tryParse版本返回Some(3.140625)。Float32.parse(3.14159)得到3.141590Float64.parse(3.141592653589793)得到3.141593默认按 6 位小数展示。整型边界值示例整型解析对边界值含最小负值支持良好import std.convert.* main() { var strInt: String -32768 var res Int16.parse(strInt) println(After the conversion of parse, \-32768\ became ${res}) }运行结果After the conversion of parse, -32768 became -32768文档给出的一组边界值示例parse直接返回、tryParse包装为Some(...)类型输入结果Int8-128-128Int16-32768-32768Int32-2147483648-2147483648Int64-9223372036854775808-9223372036854775808UInt8255255UInt166553565535UInt3242949672954294967295UInt641844674407370955161518446744073709551615tryParse对上述输入分别返回Some(-128)、Some(-32768)、Some(-2147483648)、Some(-9223372036854775808)、Some(255)、Some(65535)、Some(4294967295)、Some(18446744073709551615)。Rune 示例import std.convert.* main() { var strRune: String A var res Rune.parse(strRune) println(After the conversion of parse, \A\ became ${res}) }运行结果After the conversion of parse, A became AtryParse版本输出Some(A)。注意Rune的字面量字符串必须带单引号源码 base.cj 中的strToChar还支持\n等转义字符与\u{...}Unicode 写法校验码点 ≤ 0x10FFFF 且不在 0xD800–0xDFFF 代理区。源码剖析整数解析与浮点解析的两条路径整数路径各Int*/UInt*的parse都先经getDataAndFlag剥离符号并得到(rawData, isNegative)再由parseCompInt逐字节累积parsable.cj#L57-L95。该函数有三个工程细节值得注意下划线分隔符允许1_000_000这类写法但禁止开头、结尾或连续下划线否则视为转换失败进制前缀以0开头的多字节串会经getBaseInfos识别0b/0o/0x前缀并切换到对应基数见 base.cj#L198-L217溢出前置检查每一步累加前比较num multminmultmin 最大值 / 基数把溢出判断摊薄到单步乘加中避免依赖带溢出的回绕。tryParse版本使用返回ConvertResultUInt64Success/Failure枚举的tryParseCompInt失败不抛异常而是返回Failure。浮点路径parseFloat/tryParseFloat通过FloatParsableT泛型接口统一三种浮点类型parsable.cj#L1078-L1176。纯十进制且小数位不超过 17 位时在仓颉侧直接按位累积遇到e/E指数、inf/nan等“非纯十进制”字符或位数过多时会转入 CFFI 调用原生CJ_STRTODfloatparse.c。随后Float16/Float32的fromFloat64会把超出位宽的值饱和为/- INF保证了解析不会静默产生错误量级。四、RadixConvertible 按 2–36 进制解析与输出设计约定进制通过radix参数显式指定而不是靠字符串前缀推断合法范围 2–36超过十进制的位用英文字母按字母序表示10 个数字 26 个字母 36 进制解析时大小写均可Int 系列字符串可以以或-开头无前缀按正数处理UInt 系列允许开头但以-开头会抛异常toString(radix)输出时超过十进制的位统一使用小写字母parse失败抛IllegalArgumentExceptiontryParse失败返回None。接口签名为static func parse(value: String, radix!: Int64): T static func tryParse(value: String, radix!: Int64): OptionT func toString(radix!: Int64): String各类型示例与运行结果RadixConvertible为Int8/16/32/64与UInt8/16/32/64提供了实现源码位于 parsable.cj各扩展遵循相同的“取数据与符号 → 查进制溢出表 → 逐位累积”结构。下面整理文档中的全部示例类型调用输入结果Int8parse(A, 16)10Int8tryParse(7a, 16)Some(122)Int8toString(16)1227aInt16parse(7fff, 16)32767Int16tryParse(7B, 16)Some(123)Int16toString(16)1237bInt32parse(7fffffff, 16)2147483647Int32tryParse(1z, 36)Some(71)Int32toString(36)711zInt64parse(7fffffffffffffff, 16)9223372036854775807Int64tryParse(1y2p0ij32e8e7, 36)Some(9223372036854775807)Int64toString(16)92233720368547758077fffffffffffffffUInt8parse(ff, 16)255UInt8tryParse(1z, 36)Some(71)UInt8toString(36)711zUInt16parse(ffff, 16)65535UInt16tryParse(1yz, 36)Some(2555)UInt16toString(36)25551yzUInt32parse(ffffffff, 16)4294967295UInt32tryParse(1z141z3, 36)Some(4294967295)UInt32toString(16)4294967295ffffffffUInt64parse(ffffffffffffffff, 16)18446744073709551615UInt64tryParse(3w5e11264sgsf, 36)Some(18446744073709551615)UInt64toString(16)18446744073709551615ffffffffffffffff完整代码示例以 Int8 十六进制解析和 36 进制 toString 为例import std.convert.* main(): Int64 { var strInt8: String A var res Int8.parse(strInt8, radix: 16) println(After the conversion of parse, \A\ became ${res}) return 0 }运行结果After the conversion of parse, A became 10import std.convert.* main(): Int64 { var value: UInt16 2555 var res value.toString(radix: 36) println(After the conversion of toString, 2555 in base-36 became \${res}\) return 0 }运行结果After the conversion of toString, 2555 in base-36 became 1yz源码剖析radix 校验与输出优化radix 校验checkParseRadix与isValidParseRadix限定2 radix 36parse越界直接抛IllegalArgumentException(The radix out of range.)tryParse越界则静默返回None见 parsable.cj#L976-L984。符号处理getDataAndFlagWithRadix走prefixPlusAllowed true的分支因此 Int 与 UInt 都接受但isSigned false的 UInt 遇到-仍会报错与文档描述一致。溢出检查getBaseInfosWithRadix对每个类型给出(radix, 最大值/radix, 最大值)配合parseCompInt(isMark: false)做单步溢出检测有符号负数用“绝对值上限MAXABSVAL”而非MAXVAL从而能正确解析出-2147483648这类最小值。输出优化toString(radix)中radix 为 2、4、8、16、32 等 2 的幂时走位运算快路径toRadixStringv (radix-1)取位、v MOVE_RADIX[radix]移位其余 radix 走通用除余法toRadixStringComparsable.cj#L1040-L1076字符表DIGITS 0123456789abcdefghijklmnopqrstuvwxyz保证输出为小写。radix 10时直接复用默认toString()。五、工程实践建议与错误处理小结综合文档与源码使用这三个接口时的关键约束可归纳为优先选择tryParse处理不可信输入tryParse把失败编码进OptionT避免了在热路径上反复创建/捕获异常parse适合“输入应当合法、否则程序状态已经不可信”的场景。注意 parse 的符号规则差异Parsable中 Int 不允许前缀、UInt 不允许任何符号前缀RadixConvertible中 Int/UInt 均允许。混用时不要把两种行为想当然。fmt 参数有硬上限format的宽度上限 100000、浮点精度上限 350超出即抛异常见 formattable.cj#L27-L28空精度串如以.结尾也会抛IllegalArgumentException。浮点转换存在精度语义Float16位宽最小解析/打印都会出现肉眼可见的精度损失3.14159→3.140625对Float16/32输入超出位宽的字面量会饱和为无穷大而不是报错。进制转换选对接口十进制字面量用Parsable二进制/八进制/十六进制等 2–36 进制字符串尤其是无前缀、任意进制的协议数据用RadixConvertible反过来把整数转成指定进制字符串用toString(radix: ...)而不是format的b/o/xspecifier后者输出风格、前缀行为与前者不同。六、相关文件索引内容路径接口 API 文档本文主体来源convert_package_interfaces.mdconvert 包概述format_spec 语法convert_package_overview.md参数与功能示例convert_samples.mdFormattable实现formattable.cjParsable/RadixConvertible实现parsable.cj进制/溢出常量与字符表base.cj浮点解析原生实现floatparse.c浮点格式化原生实现string.c通过本文你可以把std.convert的三个接口作为仓颉项目中所有“类型 ↔ 字符串”转换的统一入口用Formattable.format控制输出格式用Parsable.parse/tryParse做十进制解析用RadixConvertible处理任意 2–36 进制数据并能在出问题时依据上文的异常语义表快速定位是格式串、符号前缀、数值范围还是进制参数的问题。赞分享编程语言语言运行时标准库垃圾回收并发编程【免费下载链接】cangjie_runtime仓颉编程语言运行时与标准库。项目地址https://gitcode.com/Cangjie/cangjie_runtime点击查看免费下载相关推荐仓颉 std.convert 类型转换与格式化接口详解Formattable、Parsable 与 RadixConvertible 实战指南仓颉 std.convert 类型转换与格式化接口详解Formattable、Parsable 与 RadixConvertible 实战指南 导读 仓颉标准人工智能AI 技能文档开发工具仓颉 std.convert 包接口详解Formattable 格式化与 Parsable/RadixConvertible 字符串解析仓颉 std.convert 包接口详解Formattable 格式化与 Parsable/RadixConvertible 字符串解析 std.conver编程语言语言运行时标准库垃圾回收并发编程仓颉语言 std.convert 包接口深度解析Formattable、Parsable 与 RadixConvertible 三大转换接口实战指南仓颉语言 std.convert 包接口深度解析Formattable、Parsable 与 RadixConvertible 三大转换接口实战指南 本文聚焦文档知识库教程OpenHarmony上一篇【亲测免费】 Bits-UI 使用指南下一篇终极PPO算法实战指南如何在BipedalWalker环境中训练稳定行走的智能体创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考