playbook/docs/tsl/syntax/20_strings_and_text.md

222 lines
6.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Strings And Text
文档类型:语法主线
是否可直接用于生成代码:是
是否含已验证可执行示例:是
是否含已验证反例:是
遇到不确定时跳转到:[04_values_and_literals.md](04_values_and_literals.md)、[19_types_and_conversions.md](19_types_and_conversions.md)、[07_expressions_and_operators.md](07_expressions_and_operators.md)
手册位置:第 20 篇,共 32 篇。上一篇:[19_types_and_conversions.md](19_types_and_conversions.md)。下一篇:[21_external_calls_and_threads.md](21_external_calls_and_threads.md)。
这一篇吸收字符串专题里较深的部分:字符串表达、编码边界、转义、非转义表达、字符串拼装和子串操作。
## 这一篇解决什么问题
回答“当任务进入字符串和编码细节,而不是只写普通字面量时,应该进入哪一篇”。
## 必须记住的规则
- 普通字符串、`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` 都能把 ASCII `0` 放进字符串,并且不会把字符串截断。
- 字符串下标当前已验证从 `1` 开始,不从 `0` 开始;访问 `s[0]` 会运行报 `String index out of bounds`
- 字符串切片 `s[start:end]`、替换 `s[start:end] := sub`、删除 `s[start:end] := ""`、插入 `s[index:0] := sub` 当前都可用。
- 基础字符串索引、切片、插入、删除和替换规则,阅读主线仍以 [04_values_and_literals.md](04_values_and_literals.md) 为准;这一篇只补已经单独实测过的深水边界。
## 已验证语法
原始字符串 `%%` 的空白分隔、标识符和多行:
代码块身份:已验证可执行示例
```tsl
program test;
begin
s1 := %% ABC%%;
s2 := %%tag A"B'C%%tag;
s3 := %%m
A
B%%m;
WriteLn(s1);
WriteLn(s2);
WriteLn(s3);
end.
```
已验证运行结果:
- `s1` 输出 `ABC`
- `s2` 输出 `A"B'C`
- `s3` 输出两行 `A`、`B`
- 说明 `%%` 开头后的第一个空白只是分隔符,不属于正文
- 说明 `%%tag ...%%tag` 可以用标识符配对
- 也说明 `%%` 原始字符串支持直接跨行
带前缀的原始字符串:
代码块身份:已验证可执行示例
```tsl
program test;
begin
WriteLn(L%% ABC%% = L"ABC");
WriteLn(U%% ABC%% = "ABC");
end.
```
已验证运行结果:
- 依次输出 `1`、`1`
- 说明 `L%% ...%%``U%% ...%%` 在当前解释器里都可用
`\uXXXX` 在普通串、宽串和 `U` 串里的边界:
代码块身份:已验证可执行示例
```tsl
program test;
begin
WriteLn(Length("\u0041"));
WriteLn(Length(L"\u0041"));
WriteLn(Length(U"\u0041"));
end.
```
已验证运行结果:
- 依次输出 `2`、`1`、`1`
- 因此不要把普通字符串里的 `"\u0041"` 直接理解成和 `L"\u0041"` 一样的“单字符”写法
字符串显式编码转换:
代码块身份:已验证可执行示例
```tsl
program test;
begin
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"));
end.
```
已验证运行结果:
- 依次输出 `0`、`1`、`1`、`1`
- 说明 `U""` 在当前解释器里不是宽串
- 说明 UTF8、宽串、普通串之间的显式转换链已经拿到正向最小结果
ASCII `0` 字符不会截断字符串:
代码块身份:已验证可执行示例
```tsl
program test;
begin
s1 := "A\0B";
s2 := "A"#0"B";
WriteLn(Length(s1));
WriteLn(Length(s2));
WriteLn(s1 = s2);
WriteLn(s1[2] = #0);
end.
```
已验证运行结果:
- 依次输出 `3`、`3`、`1`、`1`
- 说明 `\0``#0` 都可以把 ASCII `0` 放进字符串
- 也说明当前字符串不是遇到 `#0` 就自动截断的 C 风格零结尾串
字符码拼接:
代码块身份:已验证可执行示例
```tsl
program test;
begin
WriteLn("A"#48"B");
WriteLn("A"#13#10"B");
end.
```
已验证运行结果:
- 第一行输出 `A0B`
- 第二组输出会分成两行 `A`、`B`
- 说明 `#48` 这类写法会按字符码直接参与字符串拼接,`#13#10` 可以直接表示换行
字符串下标、切片和区间改写:
代码块身份:已验证可执行示例
```tsl
program test;
begin
s := "ABCD";
WriteLn(s[1]);
WriteLn(s[2]);
s := "AAABBB";
WriteLn(s[3:4]);
end.
```
已验证运行结果:
- 依次输出 `A`、`B`、`AB`
- 说明字符串下标当前从 `1` 开始
- 也说明 `s[3:4]` 会取到起止位置都包含在内的子串
替换、删除和插入:
代码块身份:已验证可执行示例
```tsl
program test;
begin
s1 := "AAABBB";
s1[3:4] := "111";
WriteLn(s1);
s2 := "AAABBB";
s2[3:4] := "";
WriteLn(s2);
s3 := "AAABBB";
s3[4:0] := "111";
WriteLn(s3);
end.
```
已验证运行结果:
- 三行依次输出 `AA111BB`、`AABB`、`AAA111BBB`
- 说明 `s[start:end] := sub` 可以直接替换区间
- 说明右值设为空串时,会删除该区间
- 说明 `s[index:0] := sub` 会在指定位置前插入子串
当前还额外验证到:
代码块身份:反例 / 不可照写
```text
s := "ABCD";
WriteLn(s[0]);
```
上面这种写法会运行报错,错误信息包含 `String index out of bounds`。不要把字符串下标按数组的 `0` 起始规则来写。
## 跳转指引
- 回看最常用字符串字面量:见 [04_values_and_literals.md](04_values_and_literals.md)
- 回看字符串上的表达式行为:见 [07_expressions_and_operators.md](07_expressions_and_operators.md)
- 看类型与转换:见 [19_types_and_conversions.md](19_types_and_conversions.md)
- 看值与字面量:见 [04_values_and_literals.md](04_values_and_literals.md)