17 KiB
TSL 值与字面量
这一篇整理基本类型、字面量、数组、字符串、字符串编码边界与基础值模型,避免把值规则分散在函数或金融示例里。
本篇职责
回答“基本类型怎么写、数组和字符串怎么索引、字符串编码边界怎么写、哪些值规则属于语言级事实”。本页拥有整数、实数、字符串、布尔、array(...) 字面量和下标 / 区间规则;矩阵与集合扩展不在本页展开。
核心规则
- 最先掌握的几类值是:整数、实数、普通字符串、布尔和
array(...)。 - 普通字符串既可以用双引号,也可以用单引号。
- 同类引号本身可以通过连续写两个同类引号放进字符串里。
\\、\"、\n这类基础转义可用。\t和\xNN这类转义也可用。\r、\r\n、\a、\b、\f、\v这些经典转义也可用。
array(...)既可以写顺序数组,也可以写字符串键表;顺序数组和binary(...)二进制缓冲区下标从0开始,字符串下标从1开始。- 数组元素可以是混合类型;给不存在的下标赋值会扩张数组。显式整数键可写成
array(0: value0, 1: value1)。 s[0]在运行时会越界,不要把字符串当成 0 基下标。- 字符串取子串用
s[start:end],并且end是包含在结果里的。 - 字符串替换子串用
s[start:end] := "..."。 - 字符串删除子串,本质上就是
s[start:end] := ""。 - 字符串插入子串用
s[index:0] := "..."。 - 普通字符串、
L""宽串、U""UTF8 前缀串,以及%%原始字符串都属于文档明确写法;合法形式不等于默认选择,生成优先级见本页“字符串字面量选择与边界规则”。 %%原始字符串开头后必须先跟一个空白分隔符;可以带标识符,也支持多行。L%% ...%%与U%% ...%%这两种带前缀的原始字符串也属于文档明确写法。- 普通字符串里的
\uXXXX不要直接按“单字符宽串”理解;length("\u0041") = 2,而length(L"\u0041") = 1、length(U"\u0041") = 1。 U""不是宽串;需要在 UTF8、宽串、普通串之间显式转换时,继续用utf8ToUnicode(...)、utf8ToAnsi(...)、ansiToUtf8(...)、unicodeToUtf8(...)、string(...)、wideString(...)。#number可以直接把字符码拼进字符串。\0和#0都能把 ASCII0放进字符串,并且不会把字符串截断。- 字符串
like的右侧按正则表达式解释,不是 SQL%/_通配,也不要把正则里的*当成独立的 glob 通配符;默认匹配不区分大小写。 - 数值
like表示近似相等;likeEps和likeEpsRate分别控制绝对误差与相对误差阈值,默认值都是1e-6。
可直接照写示例
使用这些示例时遵守:
- 只复制任务需要的值、字面量、下标或字符串片段,不要把多个示例拼成未写入文档的新语法。
- 普通示例默认按
.tsl脚本语句区书写;需要函数或类型时,放在后置声明区。 - 字符串字面量选择、编码、宽串和原始字符串边界不清楚时,优先看本页“字符串字面量选择与边界规则”。
最基础的值写法:
代码块身份:可直接照写示例
count := 1;
price := 12.5;
name := "ABC";
flag1 := true;
flag2 := false;
items := array(1, 2, 3);
writeLn(count);
writeLn(price);
writeLn(name);
writeLn(flag1);
writeLn(flag2);
writeLn(items[1]);
代码块身份:输出片段
1
12.5
ABC
1
0
2
代码块说明:true / false 可以直接写成布尔值;运行输出里分别表现为 1 / 0。
普通字符串字面量:
代码块身份:可直接照写示例
writeLn("ABC");
writeLn('XYZ');
结果说明:
- 依次输出
ABC、XYZ
字符串内引号与基础转义:
代码块身份:可直接照写示例
writeLn("A""B");
writeLn('A''B');
writeLn("A\\B");
writeLn("A\"B");
writeLn("A\nB");
结果说明:
- 依次输出
A"B、A'B、A\B "A\nB"会分成两行输出A和B
更多基础转义:
代码块身份:可直接照写示例
writeLn("A\tB" = "A"#9"B");
writeLn("A\x30B" = "A0B");
结果说明:
- 两行都输出
1 - 说明
\t可以表示制表符,\x30这类写法可以按 ANSI 字节值插入字符
经典控制字符转义:
代码块身份:可直接照写示例
writeLn("\r" = #13);
writeLn("\r\n" = #13#10);
writeLn(length("\r\n"));
writeLn("\a" = #7);
writeLn("\b" = #8);
writeLn("\f" = #12);
writeLn("\v" = #11);
结果说明:
- 依次输出
1、1、2、1、1、1、1 - 说明
\r/\r\n可以直接表示回车和回车换行 - 说明
\a、\b、\f、\v也属于可用的经典转义
代码块身份:可直接照写示例
items := array(10, 20, 30);
row := array("Code": "0001", "Price": 12.3);
s := "ABC";
writeLn(items[0]);
writeLn(items[1]);
writeLn(row["Code"]);
writeLn(s[1]);
writeLn(s[2]);
writeLn(s[3]);
代码块身份:输出片段
10
20
0001
A
B
C
代码块说明:顺序数组 items 从 0 开始,字符串键表 row 用字符串键访问,字符串 s 从 1 开始。
数组按写入下标扩张:
代码块身份:可直接照写示例
items := array(1);
items[3] := 4;
keyed := array(0: 2, 1: 3);
writeLn(length(items));
writeLn(items[1] = nil);
writeLn(items[3]);
writeLn(keyed[0]);
writeLn(keyed[1]);
代码块身份:输出片段
2
1
4
2
3
说明:length(items) 统计已有项数,不等于“最大整数键 + 1”;下标 1 没有写入,因此读取为 nil。
字符串字面量选择与边界规则
- 普通单行文本默认使用
"..."或'...';根据内容选择不冲突的引号,必要时再使用转义或连续同类引号。%% ...%%仅用于多行文本、引号非常密集等原始字符串场景;不得因为内容是中文、非 ASCII 或较长就自动改用%%。
U、L前缀只由目标字符串类型或已确认的 API 编码要求决定,不能因为内容是中文就自动添加;普通中文内容优先直接写成"中文内容"。
需要为真实 API 选择字符串类型或编码前缀时,先用 tsl-api-reference 取回该 API 的精确要求;语法页不根据文本内容猜测编码。
以下 %% 示例只说明原始字符串的合法外形和边界,不表示普通文本应默认采用原始字符串。
原始字符串 %% 的空白分隔、标识符和多行:
代码块身份:可直接照写示例
s1 := %% ABC%%;
s2 := %%tag A"B'C%%tag;
s3 := %%m
A
B%%m;
writeLn(s1);
writeLn(s2);
writeLn(s3);
结果说明:
s1输出ABCs2输出A"B'Cs3输出两行A、B- 说明
%%开头后的第一个空白只是分隔符,不属于正文 - 说明
%%tag ...%%tag可以用标识符配对 - 也说明
%%原始字符串支持直接跨行
代码块身份:输出片段
ABC
A"B'C
A
B
带前缀的原始字符串:
代码块身份:可直接照写示例
writeLn(L%% ABC%% = L"ABC");
writeLn(U%% ABC%% = "ABC");
结果说明:
- 依次输出
1、1 - 说明
L%% ...%%和U%% ...%%都属于文档明确写法
\uXXXX 在普通串、宽串和 U 串里的边界:
代码块身份:可直接照写示例
writeLn(length("\u0041"));
writeLn(length(L"\u0041"));
writeLn(length(U"\u0041"));
结果说明:
- 依次输出
2、1、1 - 因此不要把普通字符串里的
"\u0041"直接理解成和L"\u0041"一样的“单字符”写法
字符串显式编码转换:
代码块身份:可直接照写示例
utf8_s := U"\u5929\u8F6F";
utf8_s2 := unicodeToUtf8(L"\u5929\u8F6F");
wide_s := utf8ToUnicode(utf8_s);
ansi_s := utf8ToAnsi(utf8_s);
writeLn(ifWString(utf8_s));
writeLn(utf8_s = utf8_s2);
writeLn(wide_s = L"\u5929\u8F6F");
writeLn(ansi_s = string(L"\u5929\u8F6F"));
结果说明:
- 依次输出
0、1、1、1 - 说明
U""不是宽串 - 说明 UTF8、宽串、普通串之间可以按示例显式转换
ASCII 0 字符不会截断字符串:
代码块身份:可直接照写示例
s1 := "A\0B";
s2 := "A"#0"B";
writeLn(length(s1));
writeLn(length(s2));
writeLn(s1 = s2);
writeLn(s1[2] = #0);
结果说明:
- 依次输出
3、3、1、1 - 说明
\0和#0都可以把 ASCII0放进字符串 - 也说明 TSL 字符串不是遇到
#0就自动截断的 C 风格零结尾串
字符码拼接:
代码块身份:可直接照写示例
writeLn("A"#48"B");
writeLn("A"#13#10"B");
结果说明:
- 第一行输出
A0B - 第二组输出会分成两行
A、B - 说明
#48这类写法会按字符码直接参与字符串拼接,#13#10可以直接表示换行
二进制缓冲区:
代码块身份:可直接照写示例
b := binary("30");
writeLn(ifBinary(b));
writeLn(b[0] = "3");
writeLn(b[1] = "0");
b[1] := 0x31;
writeLn(b[1] = "1");
结果说明:
- 依次输出
1、1、1、1 - 说明
binary(...)会得到二进制缓冲区 - 说明二进制缓冲区的
[]下标从0开始 - 也说明二进制缓冲区项可以直接按单字节读写
字符串子串读写
字符串子串统一用 s[start:end] 区间访问,end 包含在结果里;同一形态既能读取,也能替换、删除和插入。
字符串子串读取:
代码块身份:可直接照写示例
s := "ABCDE";
writeLn(s[2:4]);
代码块身份:输出片段
BCD
代码块说明:字符串区间 2:4 会包含结束位 4。
字符串替换子串:
代码块身份:可直接照写示例
s := "ABCDE";
s[2:4] := "XYZ";
writeLn(s);
结果说明:
- 输出
AXYZE
字符串删除子串:
代码块身份:可直接照写示例
s := "ABCDE";
s[2:4] := "";
writeLn(s);
结果说明:
- 输出
AE
字符串插入子串:
代码块身份:可直接照写示例
s := "AAABBB";
s[4:0] := "111";
writeLn(s);
结果说明:
- 输出
AAA111BBB - 这说明
s[index:0] := ...是在指定位置插入,而不是删除或替换区间
字符串 $ 连接与拼装
$ 用于字符串连接和类型转字符串拼装:
代码块身份:可直接照写示例
s1 := "abc" $ "def";
a := 1;
b := 2.34;
c := "AAA";
d := "A=" $ a $ " B=" $ b $ " C=" $ c;
writeLn(s1);
writeLn(d);
代码块身份:输出片段
abcdef
A=1 B=2.34 C=AAA
说明:
"abc" $ "def"连接两个字符串得到"abcdef"- 整数、实数等类型可以直接用
$拼装成字符串 - 拼接实数时,保留最高精度,表现与
floattostr一致 - 纯小数可能走科学计数法:
"_" $ 0.0000005 $ ","返回"_5E-7," - 带整数部分的小数保留精度:
"_" $ 1.0000005 $ ","返回"_1.0000005,"
字符串与数值 like
字符串 like 的右侧是正则表达式;默认匹配不区分大小写:
代码块身份:可直接照写示例
result1 := "a" like "A";
result2 := "HELLO" like "hello";
result3 := "2009-1-1" like "\\d+-\\d+-\\d+";
writeLn("result1:", result1);
writeLn("result2:", result2);
writeLn("result3:", result3);
代码块身份:输出片段
result1: 1
result2: 1
result3: 1
说明:
- 默认情况下,
"a" like "A"和"HELLO" like "hello"都返回1(真) "\\d+-\\d+-\\d+"是正则表达式,匹配示例中的日期形态- 正则量词
*只修饰它前面的原子;不要把"he*"解释成“he后面任意字符”的 glob 模式,需要任意字符序列时写成.* - 匹配控制标志可以改变大小写规则;控制标志的精确 API、参数与目标 scope 使用
tsl-api-referenceskill 核对
数值 like 用于近似相等判断:
代码块身份:可直接照写示例
old_abs_eps := likeEps;
old_rate_eps := likeEpsRate;
likeEps := 0.1;
likeEpsRate := 0;
writeLn(1.05 like 1);
likeEps := old_abs_eps;
likeEpsRate := old_rate_eps;
代码块身份:输出片段
1
说明:
likeEps和likeEpsRate都可读写,默认值均为1e-6- 两数绝对差小于
likeEps,或“绝对差 / 两数绝对值的平均值”小于likeEpsRate时,数值like返回真 - 临时修改阈值后要恢复旧值,避免影响同一运行时中的后续比较
not like 是取反形式(TSL 2025/8 版本起支持):
代码块身份:可直接照写示例
result := "abc" not like "xyz.*";
writeLn(result);
代码块身份:输出片段
1
format 格式化占位符
format 用于按占位符格式化输出:
代码块身份:可直接照写示例
result := format("%d-%s", 7, "x");
writeLn(result);
代码块身份:输出片段
7-x
说明:
- 示例里的
%d/%s分别对应整数、字符串占位;只为读懂本例 format的完整占位符规范使用tsl-api-referenceskill 按名查询。
formatdatetime 日期格式化
formatdatetime 用于将日期按指定格式输出:
代码块身份:可直接照写示例
dt := strtodate("2024-01-02");
result := formatdatetime("yyyy-mm-dd", dt);
writeLn(result);
代码块身份:输出片段
2024-01-02
说明:
- 示例里的
yyyy-mm-dd对应四位年、两位月、两位日;只为读懂本例 formatdatetime的完整格式说明使用tsl-api-referenceskill 按名查询。
默认生成模板
如果你只是要抓住“基本类型 + array”的第一层,用这个最短例子:
代码块身份:可直接照写示例
count := 1;
price := 12.5;
name := "ABC";
flag := true;
items := array(1, 2, 3);
禁止项
- 用
s[0]访问字符串首字符。 - 误以为
array(...)只能写顺序数组,不能写字符串键表。 - 误以为字符串区间和数组一样从
0开始。 - 把字符串区间当成数组切片式的半开区间;
s[1:3]返回ABC而不是AB,结束位会被包含进结果,正向示例见本页“字符串子串读写”。 - 把
s[index:0] := ...误解成“替换到第 0 位”。 - 把二进制缓冲区也按字符串的
1基下标去理解。 - 因为内容是中文、非 ASCII 或较长,就把普通单行文本自动改写成
%%原始字符串。 - 因为内容是中文,就自动添加
U/L前缀;前缀只由目标字符串类型或已确认的 API 编码要求决定。 - 在普通字符串示例里默认切到原始字符串、宽串 / UTF8 前缀、字符码或编码转换,却没有按本页字符串字面量选择与边界规则确认。
- 把普通字符串里的
\uXXXX直接当成宽串单字符。 - 把
U""当成宽串;需要宽串时按本页转换链处理。 - 把
#0/\0当成 C 风格字符串终止符。 - 把字符串
like误写成默认区分大小写;默认行为是不区分大小写。 - 把
like只理解成字符串模式匹配,遗漏数值近似相等语义。 - 修改
likeEps/likeEpsRate后不恢复,导致后续代码继承意外的比较阈值。 - 以为给较大整数下标赋值后,中间所有位置都会自动生成实际元素;未写入的位置仍为
nil。
代码块身份:反例 / 不可照写
s := "ABC";
writeLn(s[0]);
上面这类写法在运行时会报字符串下标越界。
代码块身份:输出片段
String index out of bounds